JS核心笔记
一、说明
JS权威指南文字用红色标出;
JS高级程序设计用橙色标出;
自己加上的文字用粉红色标出;
其(一)-(九)为JS权指南,(十)为JS高级程序设计
二、记法结构
2.1字符集
Javascript程序用的是Unicode字符集编码。
2.2大小写敏感
Javascript是一种区分大小写的语言,但是注意HTML是不区分大小写的(尽管XHTML区分大小写的)。也即在HTML中这些标记和属性名可以任意的大小写方式输入,但是在Javascript中它们通常都是小写的。
2.4可选的分号
尽管理论上说来JS允许在任意两个标识符之间放置换行符,但是实际上JS会自动插入分号。例:
Return
True;
JS会假定你的意图是:
Return;
True;
2.7标识符
第一个字母必须是字母、下划线或美元符号($)!
三、数据类型和值
3.1数字
Javascript不区整型值和浮点型数值。在Javascript中,所有的数字都是由浮点型表示的。
3.1.1,3.1.2整型直接量、八进制和十六进制的直接量
十六进制直接量是以”0x”或”0X”开头,而八进制是以数字0开头。
在ECMAScript标准中不支持八进制的直接量,但是Javascript的某些实现是允许八进制格式的整型直接量的。所以一般最好不要使用以0开头的整型直接量。
3.1.3浮点直接量
例如
3.14
234.342
.342424
2.32e23
1.23424E-32
3.1.6特殊的数值
当一个算术运算(如0除0)产生了未定义的结果或错误时,就会返回一个特殊的非数字的值,为NaN。这个数字值和任何数值都不相等,包括它自己在内,所以需要一个专门的数字来检测IsNaN()来检测这个值。
特殊数值常量
NAN
Infinity 无穷大
Number.MAX_VALUE
Number.MIN_VALUE
Number.NaN
Number.POSITIVE_INFIITY
Number.NEGATIVE_INFITY
这是特殊的常量都是在运算时可能产生的,用IsNaN()或isFinite()等函数来检测。
3.2字符串
3.2.1字符串直接量
字符串是由单引号或双引号括起来的Unicode字符.在单引号可以含有双引号,双引号可以含有单引号。
3.2.2字符串直接量中的转义序列
\0 \b \t \n \r \v \f \’ \” \\ \uXXXX(四位十六进制的Unicode字符) \xXX由两位十六进制指定的latin-1字符
3.2.3字符串使用
可以用(+)来连接字符串。
3.2.4数字转换为字符串
Var number=100;
Var s=number.toString();
还可以指定转换的基数,
Var s=number.toString(16);
还有三个函数可以精确指定转换后的数字格式,toFixed(),toExponential(),toPrecision();
3.2.5字符串转换为数字
当把一个字符串用于数字环境中,它也会自动地转换为一个数字,
Var t=”21” * “2”;
利用这一优点,只要把一个字符串减去0就可以将其转换为一个数字。
Var t=string_value-0;
ParseInt()和ParseFloat(),这两个函数可以从字符串开始处转换和返回任何的数字,忽略或舍去非数字部分。
ParseInt()甚至可以接受另一个参数来指定要解析的数字的基数。合法的范围在2至36之间。
ParseInt(“012”,2);
ParseInt(“0x23”);//默认返回10进制
这两个函数不能把指定的字符串转换为数字,它们会返回NaN.
ParseInt(“ele”);//返回NaN
3.7Null
Null被看作对象类型的一个特殊值,即代表“无对象”的值。
3.8undefined
使用了已经声明但还没有赋值的变量时,又或者使用了一个并不存在的对象属性时,返回的就是这个值。注意这个特殊的undefined值不同于null。但是==运算符却将两者相等的。
3.11Error对象
当运行发生错误时,Js会抛出某个类的对象。
3.12类型转换小结
不同的值在不同的环境运行时所转换成的值
值 字符串 数字 布尔 对象
未定义的值 “undefined” NaN false Error
Null “null” 0 false Error
非空字符串 不变 字符串中的数字值或NaN true String
空字符串 不变 0 false String
0 “0” 不变 false Number
NaN “NaN” 不变 false Number
无穷 “InFinity” 不变 true Number
负无穷 “-InFinity” 不变 true Number
任意其他的数字 数字字符串值 不变 true Number
True “true” 1 不变 Boolean
False “false” 0 不变 Boolean
对象 toString() valueOf(),toString(),或NaN true 不变
注:对于对象的转换,当在一个数字环境时JS首先调用该对象的ValueOf()方法,如果这个方法返回一个基本类型的值,这个值会被使用,然而在大多数情况下,valueOf()方法返回的是对象自己,在这种情况下,JS先使用toString()方法把对象转换为一个字符串,然后再试图把该字符串转换为一个数字。
3.13,3.14基本数据类型的包装对象
跟C#或JAVA里的包装对象是一样的,比如用一个字符串变量时使用了一个字符串对象的方法或属性,JS会为这个字符串值内部创建一个String包装对象.String对象替代了原始的字符串值。注意,这个对象中只是瞬时存在的,在用完以后就没有用了,系统会把它丢弃。
Var s=”asdf”;
Var len=s.length;//用长度属性。
注意:JS中函数的方法call(),apply(),可能还有其它的方法能被字符串常量,数字,布尔类型甚至null,undfined调用,为什么?因为包装对象啊!!!在这两个函数里面需要对象所以就把常量包装成对象了来调用对象,用完就丢掉!!jQuery中的的each方法循环对象的属性时就是用的这个特性。
3.15传值和传址
基本类型为Null,Undefinded,布尔型,数字。
引用类型为数组、函数、对象。
JS字符串是通过传址来复制和传递的,而它们是通过传值来比较的。可以说是既能是基本类型又是引用类型!
四、变量
4.1变量类型
JS是非类型的,也即变量可以存放任意类型的值。
4.2变量的声明
重复的声明和遗漏的声明
可以使用VAR语句重复声明一个变量不仅是合法的,而且也不会造成任何错误。
如果尝试读一个未声明的变量的值,JS会生成一个错误。如果尝试给一个用VAR语句的变量赋值,JS会隐式声明这个变量,但注意,隐式声明的变量总是被创建一个全局变量,即使该变量只是一个函数体内使用。
4.3变量的作用域
如果一个局部变量或函数的参数声明的名字与某个全局变量的名字相同,那么就有效地隐藏了这个全局变量。
局部变量必须是用var语句,否则就是全局变量了。
4.3.1没有块级作用域
JS中没有块级作用域的概念,函数中声明的所有变量无论是在哪里声明的,在整个函数中它们都是有定义的。实际这个说法对全局变量也是起作用的,例
Var scope=”global”
Function f(){
Alert(scope);//displays “undefined”,not global
Var scope=”local”;
Alert(scope); //displays “local”
}
F();
局部变量在整个函数体内部都是有定义的,这就意味着在整个函数体中会隐藏了同名的全局变量。虽然局部变量在整个函数体中是有定义的,但是在执行var语句之前,它是不会被初始化的。这个例子说明了把有的变量集中起来放置在函数的开头是一个好的编程习惯。
4.3.2未定义变量和未赋值变量
未定义变量就是没有被告声明过的,无论是显示声明不是隐式声明,读取这样的变量会出错.
未赋值的变量是虽已经声明了,但是没有给它赋值!
4.6作为属性的变量
4.6.1全局对象
JS解释器开始运行时,它首先要做的事情之一就是在执行任何JS代码之前,创建一个全局对象,这个对象的属性就是JS程序的全局变量。当声明一个JS的全局变量或函数时,实际上所做的是定义了那全局对象的一个属性。或者说用一个同名的属性来存储全局变量和全局函数。
此外,JS解释器还会用预定义的值和函数来初始化全局对象的许多属性,如Infinity,parseInt,Math分别引用了数值infinity,预定义函数ParseInt()和预定义对象Math.
在程序的顶层代码(不是函数中的代码)可以用this关键字来引用这个全局对象。
在客户端JS中,Window对象代表浏览器窗口,它是包含在该窗口中的所有JS代码的全局对象,这个全局对象具有自我引用的window属性,它代替了this属性,可以用来引用全局对象。
4.6.2局部变量: 调用对象
内嵌的函数、函数的参数和局部变量是调用对象的属性。调用对象是在函数运行时创建的!一般用完就释放,除非产生了闭包!
4.7深入理解变量作用域
在JS的顶层代码中,作用域链只由一个对象构成,那就是全局对象。所有的变量都是在这一对象中查询的。如果一个变量存在,那么这个变量的值就是未定义的。在一个函数(非嵌套)中,作用域链由两个对象构成,第一个函数的调用对象,第二个就是全局对象。当函数引用一个变量时,首先检查的是调用对象,其次才检查全局对象。在嵌套函数的作用域链中可以后在三个或更多的对象,查找方式以此类推。
以上所说也就是一个函数在运行的时候的查询变量的情况的流程!这在闭包中也是这样的一个查找过程!
五、表达式和运算符
5.3算术运算符
1.除法运算符
在js中,由于所有的数字都是浮点型的,所以除法的结果也是浮点型的(不像C++或C#中的那种除出来是整形!),除数是0的结果为正无穷大或负无穷,而0/0的结果则是NaN.
2.模运算符
取模操作的运算数通常都是整数,但它也适用于浮点数,如-4.3%2.1=-0.1;
除了加号比较特殊外,其它减、除、模、一元减(-)、一元加(-)、递增运算符(++)、递减运算符(--)都会把非数字变成数字的!
5.4相等运算符
5.4.1相等运算符和等同运算符
判定==运算符比较的两个值是否相等。
如果两个值具有相同的类型,那么就检测它们的等同性。
如果两个值的类型不同,它们仍然可能相等,用下面的规则和类型转换来检测它们的相等性。(数字有最高的优先级)
1)如果一个值是Null,另一个值是Undefined,则它们相等。
2)如果一个值是字符串,另一个值是数字,那么字符串先转化为数字再进行比较
3)如果一个值是布尔值,另一个值为数字或字符串,布尔值和字符串都先转化为数字再进行比较,true为1,false为0.
4)如果一个值是对象,别一个是数字或字符串,则对象转换为相应的原始类型的值。
5)其它组合的值不等的。
5.5关系运算符
5.5.1比较运算符(<,>,<=,=>)
这些比较运算符的运算数可以是任意类型的。但是比较运算只能在数字和字符串上执行,所以不是数字或字符串的运算数将被转换为数字或字符串。
1) 字符串和数字比较,转换为数字再进行比较。
2)操作数不能转换为数字或字符串时,比较结果为false。
3)若一个操作数为NAN或转换为了NAN,比较结果为false。
5.5.2In运算符
In运算符要求左边的运算数是一个字符串,或可以被转换为字符串,右边的运算数是一个对象或数组或函数。如果该运算符左边的值是其右边对象的一个属性名,它返回true。
例
Var point={x:1,y:2};
Var has_x_record=”x” in point;//return true
Var has_z_record=”z” in point;//return false
函数之所以可以由此运算符来检测属性,因为函数也可以有属性,并且本身就是特殊的对象(或叫类更好些),所以由推开去,很多对象有的功能,函数和数组也有
5.5.3instanceof运算符(跟C#的is关键字类似)
Instanceof运算符要求左边的运算数是一个对象,右边的运算数是对象类型的名字。注意,所有对象都是类object类的实例。
例:
Var d=new Date();
d instanceof Date;//return true
d instanceof Object;//return true
d instanceof Number//return false
var a=[1,2,3];
a instanceof Array;//return true
a instanceof Object;//return true\
5.6字符串运算符
字符串算符“+“中,字符串的优先级最高。
如:1+”2”=”12”,”2”+1=”21”.
5.7逻辑运算符
5.7.1逻辑与运算符(&&)
先计算左边表达式的值,如果左边表达式值可以转换为false,那么返回的是左边表达式的值,否则,它将计算右边的表达式的值,并且返回这个表达式的值。
5.7.2逻辑或运算符(||)
原理同逻辑与运算符,先计算左边表达式的值,如果左边表达式的值可以转换为true,则返回这个表达式的值,否则,它将计算右边表达式的值,并且返回该表达式的值。
5.7.3逻辑非运算符(!)
在对操作数取反之前,!运算符先把它的运算数转换为一个布尔类型的值,这意味着,对任何值x应用两次运算符(即!!x)都可以将它转换成一个布尔值。第一次为了使操作数在布尔环境中取得操作数的相反的布尔值,而第二次就是使取得操作数的正确的布尔值!!
5.10其它运算符
5.10.2typeof运算符(与C#的typeof()运算符一样的)
它测试一个操作数,返回一个字符串来表式一个操作数的类型。
对数字、字符串或布尔值,返回”number”、”string“、”boolean”,而对数组、对象和Null,它返回的是”object”,而对函数运算数,返回的是”function”,如果运算数是未定义的,它将返回”Undefined”.
5.10.4delete运算符
它将删除运数所指定的对象的属性、数组元素或变量或函数的属性。如果删除成功返回true,若不能删除,返回false。并非所有的属性和变量都是可以删除的,某些内部的核心和客户端属性不能删除,用var语句声明的用户定义变量也不能被删除。
注意,删除属性、变量(数组不一样)不只是把它们的值设置为undefined,当删除它们后,它们全部变成未定义的,也即不存在的,再次尝试读取它们时会出错。
Delete所能影响的只是属性值,并不能影响被这些属性引用的对象,即这些对象只要用其它属性引用就还存在!
5.10.5void运算符
它可以出现在任何类型操作数之前,它总是舍弃运算数的值,然后返回undefined.这种运算符通常用在客户端的javascript:Url中,在这里可以计算表式的值,但是不会显示出这个值。
<a href=”javascript:void window.open();”>open a widnow</a>
六、语句
6.1switch语句
ECMAScript v3标准允许case 语句扣跟随任意的表达式。例如:
Case 60*60:
Case Math.pi;
Case n+1;
注意:由于匹配的case表达式是用===等同运算符判定的,而不是用==运算符判定的,所以表达式必须在没有类型转换的情况下进行匹配。
6.9for/in语句
For(variable in object)
{statement
}
Variable应该是一个变量名,声明一个变量的var语句,数组的一个元素或者是对象的一个属性。Object是一个对象名。
在循环体执行之前,对象的一个属性名会被作为字符串赋给变量variable。在循环体中可以使用这个变量和[]运算符查询该对象属性的值。
注意,for/in循环中的variable可以是任意的表达式,只要它们的值适用于赋值表达式的的左边即可。
JS的数组是一种特殊的对象,因此可以像循环对象属性那样枚举数组下标。
如果for/in循环的主体删除了一个还没有枚举出的属性,那么该属性就不再枚举,如果循环主体定义了新属性,那么循环是否枚举该属性则由JS的实现决定。
For/in循环并不会遍历所有对象的所有可能的属性,那些被标记为只读的、永久的或不可列举的(继承的也不可以),这些属性使用此语句是枚举不出来的,所有用户自定义的属性可以枚举的!
6.14function语句
函数定义可以嵌套在其它函数定义中,但是只能嵌套在那些函数顶层中,也就是说,函数定义不能出现在IF语句,while语句或其它任何语句中。
在技术上说,function语句并非是一个语句.JS中语句会引发动态的行为,但是函数定义描述的却是静态的程序结构。语句是在运行时执行的,而函数则是实际运行之前,当JS代码被解析或被编译时定义的。当JS解析程序遇到一个函数定义时,它就解析并存储构成函数主休的语句,然后定义一个和该函数同名的属性(如果函数定义在其它的函数中,那么就在调用对象中定义这个属性,否则在全局对象中定义这个属性)以保存它。这会产生一些奇怪的行为。
例:
Alert(f(4));//displays 16
Var f=0;
Function f(x);
{
Reutrn x*x;
}
Alert(f);displays 0.f() has been overwritten by the variable f
出现这种情况是由于函数定义和变量定义出现在不同的时刻。
6.15return语句
如果一个函数执行了一个不带expression的return语句,或者因为它执行到了函数的尾部而返回,那么这个函数的表达式的值就是undefined。
6.16throw语句
Throw expression
Expression的值可以是任何类型的值,但通常它是一个Error对象或Error子类的一个实例。
6.17try/catch/finally语句
这个语句的使用和c#的使用差不多。
Try{} catch(e) {}finally{}
七、对象和数组
7.1对象
包含在一个花括号中,每个属性名可以是一个JS标识符或一个字符串或一个字符串,而每个属性值可以是一个常量或任意的JS表达式。
例:
Var empty={};
Var point={x:0,y:0};
Var homer={“name”:”homer”,”age”:34};
(2)new创建对象
Var d=new Date();
Var reg=new RegExp();
7.2对象属性
对象的属性和变量工作方式相似,即可以把值存储其中,也可以从中读取值。一旦通过给属性赋值创建了该属性,就可以在任何时刻修改这个属性的值。可以通过把一个值赋给对象的一个新属性来创建它。
Var book={};
Bool.widht=20//create
7.2.1属性的枚举
For/in语句枚举属性。JS权威指南中说,for/in循环枚举列出的属性并没有特定顺序,其它细节见6.9.但是,我做了一个示例它是按属性的定义来枚举的,那会在什么情况下的顺序会不定呢?
var obj={
t1:1,
t2:2,
t3:3,
t4:4
};
for(o in obj)
{
alert(obj[o]);//output 1,2,3,4
}
7.2.2检查属性的存在
In运算符可以用来测试一属性的存在,详见5.5.2
7.2.3删除属性delete运算符
删除属性并不是仅仅把属性设置为undefined,它实际上从对象中移除了属性,在删除之后,for/in将不会枚举该属性。详见5.10.4
7.3作为关联数组的对象
就是用像读取数组的方式来读取属性值。其实对象的属性也可以是数字的,类数组的对象!!
Obj.propery;
Obj[“property”]//关联数组
7.4通用ojbect属性和方法
7.4.1.constructor属性
每个对象都有一个constructor属性,它引用了初始化这个对象的构造函数。
例:
Var d=new Date();
d.constructor==Date//return true
7.4.3.hasOwnProperty()方法
如果此方法中指定的参数属性来自本地定义的一个非继承的属性,此方法返回true。
Var o={};
O.hasOwnProperty(“unde”);//return false,the property is not defined
O.hasOwnProperty(“toString”);//false,toString is an ihherited property
Math.hasOwnProperty(“cos”);//true:the Math object has a cos property
7.4.6.propertyIsEnumeralbe()方法
如果此方法中指定的参数属性来自本地定义的一个非继承的属性,并且如果这个属性可以在一个for/in循环中枚举,此方法返回true。
注意,一个对象的所有用户定义的属性都是可以枚举的,不能枚举的属性通常是继承的属性,因此这个方法几乎总是会和hasOwnProperty()返回相同的值。
7.4.7.isPrototypeOf()方法
如果此方法所属的对象是参数的原型对象,那么就返回true。
Var o={};
Object.prototype.isPrototypeOf(o);//true
Object.isPrototypeOf(o);//false
7.5数组
数组不过是一个具有额外功能的对象。
字面值创建数组
Var empty=[];
Var primes=[2,3,4];
Var misc=[1.1,true,”ik”];
直接量的值还可以是表达式
Var base=23;
Var table[base,base+3,base+4];
直接量还可以是对象直接量或数组直接量
Var b=[[1,{x:1,y:2}],2];
未定义的元素通过忽略逗号之间的元素值来创建
Var count=[1,,2];//数组有三个元素,中间的元素没有赋值
Var undef=[,,];//数组有2个元素,全部为没有赋值
用Array()构造函数创建数组
Var a=new Array():
Var b=new Array(3,2.3,4,”saf”,”sf”);
Var c=new Array(10);//这里的每个元素都没有赋值为undefined
7.6数组元素的读写
7.6.1添加数组新元素
JS中可以在任何时刻改变元素个数,而且由于JS数组是稀疏的,即数组的下标不必落在一个连续的数字范围内,只有那些真正存储在数组中的元素才能由JS实现分配内存。
例
a[0]=1;
a[100]=2;
这里JS解释器只给数组下标为0和100的元素分配内存,而试图访问其它的元素时将得到一个undefined
7.6.2删除数组元素
用delete运算符把一个元素设置为一个undefined值,但是元素本身还继续存在,要真正地删除一个元素,那么必须使用一个数组方法。(当用方法移出了一个元素后,再去访问该元素,虽然也会返回一个undefined值,但用length属性去测试会发现长度变短了,说明是真正的移出了一个元素;但是仅仅用delete运算符去删除一个元素,用length属性去测试时会发现长度没有变,说明没有真正移出元素!)
7.6.3.数组长度(length属性)
7.6.5截断或增长数组
Length属性是可读可写的,通过修改length的值可以截断和增长数组的长度。
7.6.6多维数组
JS不支持真正的数组,它支持数组的数组,即变长数组!
Var a=new Array(10);
For(var b=0;b<a.length;b++)
{
A[b]=new Array(10);
}
7.7数组方法
参考API去
7.8类似数组的对象
把任何具有一个length属性以及相应的非负整数属性的对象作为一种数组,这是合理。虽然不能调用数组的方法或者length属性期待特殊的行为,仍然可以用遍历一个真实数组代码来遍历它们。事实证明,许多数组对于类似数组的对象和真正的数组对象都是一样有效的。只要不尝试对类似数组的对象添加元素或者改变length属性。()
例:
var a={};
var i=0;
while(i<10)
{
a[i]=1*I;
i++;
}
a.length=i;
var total=0;
for(var j=0;j<a.length;j++)
total+=a[j];
结果:45
a.length=9;//改变length属性想改变类似对象数组的元素个数。
for(var j=0;j<a.10;j++)
total+=a[j];
结果为:45
上述改变为什么不成功?因为这些操作对类似数组的对象没用,想通过改变length来改变类似对象数组的个数,那是不可能的,它仅仅是一个对象的属性。不能作任何用处实际上!
八、函数
8.1函数定义和调用
JS也不会检测传递的数据是不是那个函数所要求的类型,也不会检测传递给它的参数个数是否正确。如果传递的参数比函数需要的个数多,那么多余的值会忽略掉; 如果传递的参数比函数需要的个数少,那么所被忽略的参数就被赋予undefined。
8.1.2函数直接量
Var f=function(x) {return x*x;}
或
Var f=fucntion fact(x) {return x*x;}//也可以加函数名字
Var test=(function(x) {return x*x;})(10);//定义后马上调用
8.1.3函数命名
函数名小写字母开始!
8.2函数参数
8.2.1.可选参数
它是利用了当传递给函数的参数个数小于函数的参数时,其它的参数有一个undefined值。要用可选参数时,必须能够为忽略掉的参数分配一个默认的合理值。
Function test(o,/*optional*/ a){
a=a || [];
for(var property in o) a.push(property);
return a;
}
注意,在使用可选参数时,应该确保把可选参数放在参数列表的末尾。
8.2.2.可变长度的参数列表:arguments对象
arguments并不是一个数组,它是一个对象,它虽然定义了带编码的元素和length属性。arguments对象允许完全地存取那些实际参数值,即使某些或全部参数还没有被命令。例如有一个函数f,它定义了一个参数x,但是调用f时传递给了它两个参数,那第个一参数可以用x或arguments[0]可以存取第一个参数,而第二个实际参数只能通过arguments[1]来存取。
一般argumetns对象来编写这样的函数:期待固定数目的具有名字的且必须的函数,紧接着是任意数目的没有命名的可选参数。
Arguments[]数组和命名了的参数不过是引用同一变量的两种不同方法。相反,用参数名改变一个参数的值同时会改变通过arguments数组获得的值;通过arguments[]数组改变参数的值同样会改变用参数名获取的参数值。
记住,arguments只是一个普通的JS标识符,而不是一个保留字,如果函数有一个参数或者局部变量使用了这个名字,它就会隐藏对arguments对象的引用。
属性callee
arguments对象用callee属性来引用当前正在执行的函数!!
8.4作为方法的函数
Var calculator={
Operand1:1,
Operand2:2,
Compute:function(){
This.result=this.operand1+this.operand2;}
任何用作方法的函数都被效地传递了一个隐式的参数,即this,调用对象。
当一个函数作为函数而不是作为方法调用的时候,这个this关键字引用全局对象.
当一个嵌套的函数(作为函数)在一个包含的函数之中调用,而这个包含的函数是作为方法调用的,this关键字在包含的函数中有一个值,但是它却引用嵌套的函数体的内部的全局对象。
8.6 函数的属性和方法
8.6.1属性length
这个属性是只读属性,返回的是函数需要的实际参数的数目,也就是在函数的开式参数列表中声明的形式参数的数目。注意,和arguments.length不同,这个length属性在函数体的内部和外部都有效。
8.6.3定义自己的函数属性
当函数需要使用一个在调用过程中都保持不变的值时,使用function对象的属性比定义全局变量更加方便。也即我们可以为函数定义属性。
例:
UniqueInteger.counter=0;
Function uniqueInteger(){
Return uniqueInteger.counter++;}
这里的counter相当于函数的静态局部变量一样!!!
8.6.4方法apply()和call()
call和apply的第一个参数都是要调用函数的对象,在函数体内这一参数是关键字this值。call()剩余的参数是传递给要调用的函数的值。
例:
f.call(o,1,2)
上例是把两个数字传递给函数f(),并将它作为对象的方法调用。
apply()传递给函数的参数是由数组指定的。
f.apply(o,[1,2]):
8.8函数作用域和闭包
8.8.1词法作用域
JS函数是通过记法来划分作用域的,而不动态地划分作用域的。这意味着,它们在定义它们的作用域里运行,而不是在执行它们的作用域里运行。当定义一个函数时,当前的作用域链就保存起来,并且成为函数的内部状态的一部分。在最顶级,作用域链仅由全局对象组成,而并不和词法作用域相关。当定义一个嵌套的函数时,作用域链就包括外围的函数。这意味着嵌套的函数可以访问包含函数的所有参数和局部变量。
8.8.2 调用对象
当JS解释器调用一个函数时,它首先将作用域设置为定义函数的时候起作用的那个作用域链。接下来,它在作用域的前面添加一个新对象,即调用对象。然后把函数内的传递的参数和局部变量及Arguments对象都放在调用对象中
8.8.4作为闭包的嵌入函数
如果定义了一个嵌套的函数,这个函数引用了调用对象,因为调用对象在这个函数所定义的作用域链的顶端。如果一个嵌套的函数只是在外围函数的内部使用,那么,对嵌套函数的唯一引用在调用对象之中。当外围函数返回的时候,嵌套的函数引用了调用对象,并且调用对象引用了嵌套的函数,(互相引用??)但是没有其它的东西引用它们二者,因此对这两个对象进行垃圾回收。(看看全局对象和调用对象的说法,就能理解嵌套函数引用了调用对象和调用对象引用了嵌套的函数)
如果把一个嵌套的函数的引用保存到一个全局作用域中,情况不同了。使用嵌套函数作为外围函数的返回值,或者把嵌套的函数存储为某个其他对象的属性来做到这一点。在这种情况下,有一个对嵌套的函数的外部引用,并且嵌套的函数将它的引用保留给外围函数的调用对象。结果是外围函数的一次特定调用的调用对象依然存在,函数的参数和局部变量的名字和值在这个对象中得以维持。JS代码不会以任何方式访问这个调用对象,但是它所定义的属性是对嵌套函数任何调用的作用域链的一部分。(注:如果一个外围函数存储了两个嵌套函数的全局引用,这两个嵌套函数共享同一个调用对象,并且,一个函数的一次调用所做出的改变对于另一个函数的调用来说也是可见的)
JS函数是将要执行的代码以及执行这些代码的作用域构成一个综合体,叫闭包。
JS解释器调用一个函数时,会创建一个执行环境,而一个执行环境是和作用域链相关的。例子:
- function a() {
- var i = 0;
- function b() {
- alert(++i);
- }
- return b;
- }
- var c = a();
- c();
1. 当定义函数a的时候,js解释器会将函数a的作用域链(scope chain)设置为定义a时a所在的“环境”,如果a是一个全局函数,则scope chain中只有window对象。
2. 当执行函数a的时候,a会进入相应的执行环境(excution context)。
3. 在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中的scope chain。即a.scope=a的作用域链。
4. 然后执行环境会创建一个活动对象(call object)。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后,把活动对象添加到a 的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。
5. 下一步是在活动对象上添加一个arguments属性,它保存着调用函数a时所传递的参数。把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。
九、类、构造函数和原型
JS中函数是特殊对象,而且这个特殊对象是有属性和方法的,那么如果用函数名去调用它自身的方法,方法中this指向的就是这个函数!Jquery中S的extend方法就是这样!Jquery函数,extend就是它的方法!
9.1构造函数
通过构造函数首字母大写,以区分普通函数。实际上它们是没有区别的!
Function Car(){}
9.2原型和继承
JS对象从它的原型那里继承属性
一个对象的原型就是它的构造函数的prototype属性的值。所有的函数都有一个prototype属性,当这个函数被定义的时候,prototype属性自动创建和初始化。Prototype属性的初始化是一个对象,这个对象中带有一个属性。这个属性名为constructor,它指回到和原型相关联的那个构造函数。添加给这个原型对象的任何属性,都会成为被构造函数所初始化的对象的属性。
原型对象是放置方法和其他不变属性的理想地方。
注意,继承作为查找一个属性值的过程的一部分。属性并非从原型对象那里复制到新对象,它们只是像那些对象的属性一样地出现。首先,使用原型对象可以显著地减少每个对象的内存数量。其次,即便是在对象创建以后才添加到原型中的属性,对象也可以继承它。
9.2.1读取和写入继承的属性
当读取对象o的属性p的时候,JS首先检查o是否有一个名为p的对象。如果没有,它接下来检查o的原型对象是否有一个p的属性。
当写入一个对象的属性值的时候,JS不会使用原型对象。
9.3JS中模拟类
9.3.1实例属性
每个对象都拥有它的实例属性的一份单独拷贝。默认情况下,JS中的任何对象属性都是一个实例属性,这里我们特指由构造函数创建和初始化的属性。
9.3.2实例方法
在JS中,通过构造函数的原型对象中的一个属性设置为一个函数值,从而定义了一个实例方法。每个实例方法由一个类的所有实例方法共享。
9.3.3类属性
也就是为构造函数定义属性,跟普通的函数一样的。
Test.Unique=2;
9.3.4类方法
也就是为让相应函数成为构造函数的一个属性而已。
Test.f=function() {}
9.3.7私有成员
JS中没有私有成员。一般把属性设置为以下划线开头的属性。
来自JS高级程序设计:定义类的常用两种方法
1.混合构造函数/原型方式
Function Car(scolor,sdoors){
This.color=scolor;
This.door=sdoors;
}
Car.prototype.showColor=function (){ alert(this.color);};
2.动态原型方法
Function Car(scolor,sdoors){
This.color=scolor;
This.door=sdoors;
If( typeof Car._initialized==”undefined”)
{
Car.prototype.showColor=function(){alert(this.color);};
Car._initialized=true;
}
}
9.4通用对象模型
9.4.1toString方法
当定义一个类时,就为它定义一个toString()方法。
9.4.2valueOf方法
如果一个类需要把对象转换为某种基本类型,一般是转换为数字。那么可以为这个类定义此方法。
注意,在某些条件下,在把一个对象转换为一个字符串的时候,valueof方法可以比toString()方法更优先使用。因此如果要把类强制转换为一个字符串时,可能需要更加显式调用toString()方法。
9.4.3比较方法
要比较对象时最好自定义一个方法。因为等同运算符和相等运算符比较的是对象的引用,而关系运算符会把对象转换为数字或字符串。
9.5超类和子类
例:function Rectangle(w,h){this.width=w;this.height=h;}
Rectangle.prototype.area=function(){return this.width*this.height;}
Function DerivedRectangle(x,y,w,h)
{
Rectangle.call(this,w,h);//this就是子类的一个实例对象,那么超类Rectangle成了这个实例对象的一个属性方法来调用,那么这个方法里的this就变成了当前的实例对象,显然里面的属性也成了这个实例对象的属性!!
This.x=x;
This.y=y;
}
DerivedRectangle.prototype=new Rectangle();
Delete DerivedRectangle.prototype.width;
Delete DerivedRectangle.prototype.height;
DerivedRectangle.prototype.constructor=DerivedRectangle;
DerivedRectangle.prototype.contains=function(){return this.x+this.y+this.width+this.height;
}
首先超类构造函数作为新创建的对象的一个方法调用的。必须显示地把这个原型对象创建为超类的一个实例,然后显示地设置原型对象的constructor属性。另外,可以删除超类构造函数在原型对象中创建的所有属性,因为我们要的原型对象从它的原型那里继承的那些属性。
9.5.1构造函数链
如果只有一层子类,可以为子类原型对象添加一个superclass属性
DerivedRectangle.prototype.superclass=Rectangle;
Function DerivedRectangle(x,y,w,h)
{
This.superclass(w,h);
This.x=x;this.y=y;
}
9.5.2调用被覆盖的方法
任何时候,当为一个类定义toString方法时,就会覆盖object的toString()方法。 例:
DerivedRectangler.prototype.toString=function(){
Return “(“+this.x+”,”+this.y+”)”+
Rectangle.Prototype.toString.apply(this);
}
超类的toString的实现是用超类的原型对象的一个属性。无注调用该方法。使用apply或call方法。
十、正则表达式
10.1正则表达式支持
10.1.1使用RegExp对象
例:
Var match=”cat”;
Var reCat=/cat/;
或 reCat=new RegExp(“cat”);
Alert(reCat.test(match));//
RegExp的test方法测试字符串是否匹配这个模式。
Var match=”a bat,a Cat,a fAt bat,a faT cat”;
Var reAt=/at/;
Var arrMatches=reAt.exec(match);
RegExp对象的exce方法返回一个数组,数组中的第一条目是第一个匹配;其他的是反向引用。
String对象数组有的match()方法,它会返回一个包含所有字符串中的所有匹配的数组。
Var s=”a bat,a Cat,a fAt bat,a faT cat”;
Var reAt=/at/gi;
或 reCat=new RegExp(“cat”,”gi”);
Var arr=s.match(reAt);
String对象的Search()字符串方法返回在字符串中出现的一个匹配的位置。
Alert(s.search(reAt));
10.1.2扩展的字符串方法
String对象的replace()和split()方法都可以传递正则表达式。
这里的replace()方法的第二个参数可以传递一个函数。这处函数可以接受一个参数,即匹配了的文本,并返回应当进行替换的文本
var tt=”The sky os red”;
var re=/red/;
var sText=tt.replace(re,function(sMatch){ return “blue”;});
sMatch这个参数就是用模式re匹配后的文本!!
10.2简单模式
10.2.2元字符
正则表达式的元字符是正则表达式语法的一部分。
( [ { \ ^ $ | } ? * + .
任何时候要在正则表达式中使用元字符都必须对它们进行转义用’\’。
Var retest=/\?/;
或var retest=new RegExp(“\\?”);//必须用两个反斜杠,如果用的一个反斜杠,那么JS解释器会按照\n的方式翻译\?.
10.2.2使用特殊字符
转义字符
10.2.3字符类
就是把一些字符放入方括号中,然后正则表达式去匹配里面的各个字符。也即只能匹配方括号中的任意一个字符,不是同时匹配全部啊!!!
1.简单类
例:[adf]等
2.负向类
例:[^adf]等,告诉正则表达式不能匹配脱字符^后面跟着的字符。
3.范围类
例:[a-z] [A-Z] [0-9] [^b-z]等
4.组合类
例 [a-m1-4\n]注意各个内部类之间没有空格。
5.预定义类
代码 等同于 匹配
. [^\n\r] 除了换行和回车之外的任意字符
\d [0-9] 数字
\D [^0-9] 非数字
\s [\t\n\f\r\x0b] 空白符
\S [^\t\n\f\r\x0b] 非空白符
\w [a-xA-Z_0-9] 单词字符 (有可能匹配汉字)看语言支持Unicode字段否
\W [^a-xA-Z_0-9] 非单词字符
10.2.4量词
1.简单量词
代码 描述
? 出现零次或一次
* 出现零次或多次(任意次)
+ 出现一次或多次
{n} 刚好出现n次
{n,m} 至少出现n次,至多m次
{n,} 至少出现n次
2.贪婪的、惰性的和支配性量词
贪婪性量词先看整个字符串是否匹配,如果没有匹配,它先去掉该字符串最后一个字符,并再次尝试。这个过程一直重复下去。
惰性量词先看字符中的第一个字符是否匹配,如果不匹配,就读入下一个量词,组成两个字符的字符串,再比匹配。这个过程一直重复下去,直到匹配或没有匹配。
支匹配性量词只尝试匹配整个字符串,如果整个字符串不能产生匹配,不做进一步尝试。
贪婪 惰性 支配
? ?? ?+
* *? *+
+ +? ++
{n} {n}? {n}+
{n,m} {n,m}? {n,m}+
{n,} {n,}? {n,}+
例:
Var sToMatch=”abbbaabbbaaabbb1234”;
Var re1=/.*bbb/g;
Var re2=/.*?bbb/g;
Var re3=/.*+bbb/g;
如果想获得的匹配是”abbb”,”aabbb”,”aaabbb”,这三个表达式中只有re2能匹配成功。
10.2.5标志
字符 含义
i 执行不区分大小定的匹配
g 执行一个全局匹配,即找到所有匹配,而不是在找到第 一个之后就停止
m 多行模式,^匹配一行的开头和字符串的开头,$匹配一行的结尾或字符串的结尾
10.3复杂模式
10.3.1分组
分组是通过用括号包围一系列字符、字符类及量词来使用的。每次必须全部匹配括号中的所有字符。
例:
var reg1=/(dog)?/;//匹配0个或一个dog字符串序列
var reg2=/(dog)*/;//匹配0 个或多个dog字符串序列
var reg3=/(dog){2}/;//字符序列”dog”将在一行上连续出现两次!
10.3.2反向引用
在表达式计算完后对分组的利用,即每个分组都被存储在一个特殊的地方以备使用。这些存储在分组中的特殊值,我们称之为反向引用。
反向引用是按照从左到右遇到的左括号字符的顺序进行创建和编号的。例如表达式(A?(b?(c?)))将产生编号1-3的三个反向引用。
(1)(A?(B?(c?)))
(2)(B?(c?))
(3)(c?)
首先,使用正则表达式对象的test() 、exec()方法或string对象的match() 、search()方法后,反向引用的值可以从RegExp构造函数中得到
例:
var test=”#12345”;
var re=/#(\d+)/;
re.test(test);
alert(RegExp.$1);
使用这test方法后,所有的反向引用都被保存在RegExp构造函数,从RegExp.$1开始,如果还有第二个引用,就是RegExp.$2.
然后,还可以直接在定义分组的表达式中包含反向引用,这可以通过使用特殊转义序列\1 \2等实现。
var tt=”dogdog”;
var re=/(dog)\1/;
alert(re.test(tt));//outputs ‘true”
正则表达式re首先创建单词dog的组,然后又被特殊转义序列\1引用,使得这个表达式等同于/dogdog/。
最后,反向引用可以用在String对象的replace()方法中,这通过使用特殊字符序列$1、$2等实现。
var str=”1234 5678”;
var re=/(\d{4})(\d{4})/;
var new=str.replace(re,”$2 $1”);
alert(new);//outputs “5678 1234”;
10.3.3候选
例
var strRed=”red”;
var strBlue=”blue”;
var re1=/(red | blue)/;
把一个管道符”|” 放在两个单独的模式之间,如上所示。当对这个模式进行测试时,只要有一个被匹配就返回true,因为它相当于一个or模式,即任何一个匹配就可以。
10.3.4非捕获性分组
前面的产生反向引用的分组,我们称之为捕获性分组。如果分组不想产生反向引用,那么只要在括号的后面加一个问号和一个紧跟的冒号.
var t=”#3142134”;
var re=/#(?:\d+)/;
re.test(t);
alert(RegExp.$1);//outputs “”;
10.3.5前瞻
它告诉正则表达式运算器向前看一些字符而不移动其位置。
正向前瞻检查的是接下来的是接下来出现的是不是某个特定字符集。而负向前瞻则是检查接下来的不应该出现的特定字符集。
创建正向前瞻要将模式放在(?=和)之间,注意,这不是分组,虽然它也用到了括号。事实上,分组是不会考虑前瞻的存在的。
var str1=”bedroom”;
var str2=”bedding”;
var re=/(bed(?=room))/;
alert(re.test(str1));//outputs “true”
alert(RegExp.$1));//outputs “bed”;
alert(re.test(str2))//ouputs “false”;
创建负向前瞻要将模式放在(?和)之间,
var str1=”bedroom”;
var str2=”bedding”;
var re=/(bed(?!room))/;
alert(re.test(str1));//outputs “false”
alert(RegExp.$1));//outputs “bed”;
alert(re.test(str2))//ouputs “true”;
10.3.6边界
指定了模式的位置。$,^可以改变匹配的方向,或者说是改变贪婪和惰性量词的默认行为。看下面例子
边界 描述
^ 行开头
$ 行结尾
\b 单词的边界,就是位于\w和\W之间的位置,或位于字符\w 和字符串的开头或结尾之间的位置。
\B 非单词边界
例:
var str=”Important word is ths last one”;
var re=/(w+)\.$/;
re.test(str);
这个例子查找的一行结束之前出的跟着一个句号的一个或多个单词字符。这个例子它改变了那个贪婪性量词的方向,它先匹配整个串,然后去掉头的一个字符,继续匹配下去。
var str=”Important word is ths last one”;
var re=/\(.+?)\b/;
re.test(str);
alert(RegExp.$1);//outputs “important”
这里用惰性量词来制定在单词边界之前可以出现任何字符,且可出现一个或多次。
注意行的开始和行的结束,通常由^和$表示的位置,对应也也认为是单词的边界。
10.3.7多行模式
要指定多行模式,只要在正则表达式后面添加一个m选项。这会让$边界匹配换行符(\n)以及字符串真正的结尾。
多行模式同样会改变^边界的行为,这时它会匹配换行符之后的位置。
var str=”first second\nthird fourth\nfifth sixth”;
var re=/(\w+)$/gm;
var new=str.match(re);//outputs “second”,”fourth”,”sixth”
10.4理解RegExp对象
10.4.1实例属性
有用的lastIndex属性,它可告诉你正则表达式在某个字符串中停止之前,查找了多远(即下次匹配将会从哪个字符位置开始)。这个属性只对exec和test方法才会填入(废话,RegExp对象只有这两个方法)。必须要有全局标志(g)才行。
var str=”bbq is short”;
var re=/b/g;
re.exec(str);
alert(re.lastIndex);//outputs “1”;下次从第二个b开始,位置1
re.exec(str);
alert(re.lastIndex);//outputs “2”
10.5RegExp对象的两个方法
10.5.1 test()方法
检测一个字符串是否含有一个模式。
10.5.2 exec()方法
(1) 在调用非全局(没加g标志)的RegExp对象的exec()方法来检索字符串string,从中得到与表达式regexp相匹配的文本。如果exec()找到了匹配的文本,它就会返回一个结果数组。否则,返回一个null。这个返回的第0个元素就是与表达式相匹配的文本。第1个元素是与regexp的第一个子表达式(分组表达式)相匹配的文本。第2个元素以此类推。还有一个length属性,除了length属性和数组元素外,exec()方法还返回两个属性,index属性声明的是匹配文本的第一个字符的位置,input属性指定的就是string。在调用非全局(没加g标志)的RegExp对象的exec()方法时,返回的数组与调用方法String.match()方法相同。
在调用全局的RegExp对象的exec()方法时,exec()的行为不同。它每次匹配是从regexp的属性lastIndex指定的字符处开始检索字符串string(相当于它是有记忆的,执行一次后,下次该从哪执行是记录下来的,而没有加全局标志的每次都string头开始匹配查找)。当它找到了与表达式相匹配的文本时,在匹配后,它将把regexp的lastIndex属性设置为匹配完文本后的下一个字符的位置。这样可以反复调用exec()方法来匹配所有匹配文本。当再也找不到匹配文本时,返回null,自动把属性设置为0。当然它的第1个元素及以后的元素仍然存放的是子表达示的文本。这在match()方法中是没有的。
简单的来说,exec()方法的全局检索和非全局检索区别在于:全局检索会多出一个lastIndex索引来指示下一次开始匹配字符的位置。而且可以多次调用exec()方法来匹配所有匹配文本。
(2) 如果 regexp 没有标志 g,那么 match() 方法就只能在 stringObject 中执行一次匹配。如果没有找到任何匹配的文本, match() 将返回null。否则,它将返回一个数组,其中存放了与它找到的匹配文本有关的信息。该数组的第 0 个元素存放的是匹配文本,而其余的元素存放的是与正则表达式的子表达式匹配的文本。除了这些常规的数组元素之外,返回的数组还含有两个对象属性。 index 属性声明的是匹配文本的起始字符在 stringObject 中的位置,input 属性声明的是对 stringObject 的引用。
如果 regexp 具有标志 g,则 match() 方法将执行全局检索,找到 stringObject 中的所有匹配子字符串。若没有找到任何匹配的子串,则返回 null。如果找到了一个或多个匹配子串,则返回一个数组。不过全局匹配返回的数组的内容与前者大不相同,它的数组元素中存放的是 stringObject 中所有的匹配子串(也就是说返回的数组中不是存放的正则表达式的子达式的匹配文本而是与个表达式匹配的文本,可能有多个),而且也没有 index 属性或 input 属性。
注意,无论regexp是否全局模式,exec()会将完事的细节添加到返回的数组中。这是exec()方法与match()方法不同的地方。
例如:
var pattern=/\bJava\w*\b/g;
var text="JavaScript is more fun than Java or JavaBeans";
while((result=pattern.exec(text))!=null)
{
alert("Matched '"+result[0]+"' at position "+result.index+" next seatch begins at position "+ pattern.lastIndex);
}//必须循环才能找出所有匹配文本。
//
“1 plus 2 equals 3”.match(/\d+/g) // returns [“1”,”2”,”3”]返回的子串
十、错误处理
十一、补充
11.1一个变量第一次被声明时赋了值,而接着又再一次声明了它,但没有赋值,那么这时使用它时,它还是显示第一次声明时的值。这种在检测名称空间是否存在时很合适。
如:var tt=2;
var tt;
alert(tt);//显示2
11.2对正则的exec方法和字符串的match方法,当没有全局标志g时,它们两的行为是一样的,有index,input,lastindex等属性!除了返回与表达式相匹配的文本外,所有匹配的子串也会返回!!
Javascript高级程序设计第二版
第二章 在HMTL中使用Javascript
2.1<script>元素
一般,外部JS文件带有.js扩展名,但这个扩展名不是必需的。因为浏览器不会检查包含JS的文件的扩展名。因此,使用JSP,PHP之类的服务器端语言动态生成的JS代码也就成为了可能
2.2文档模式
文档模式:混杂模式(quirks mode)和标准模式(standards mode)。IE还有一种叫准标准模式。这种文档模式是在文档的开始处进行声明的。即<!DOCTYPE html PUBLIC节。如果没有这个文档类型节的声明,所有浏览器使用混杂模式。
标准模式对应的文档类型为:
Xhtml 1.0 严格型
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1- strict.dtd">
html 4.01 严格型
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 strict//EN" "http://www.w3.org/TR/html4/ strict.dtd">
准标准模式就是文档的过渡型或框架型来开启。即有transitional或frameset两个关键字。
总的来说,混杂模式让IE像IE5,标准模式使用IE7的呈现引擎。IE8又采用一种超级标准模式作为它的默认文档模式。而上面所说的标准模式,准标准模式,超级标准模式差别不大。
文档模式主要是会影响CSS和JS等的呈现!
第七章 匿名函数
7.2闭包
由于闭包会携带包含它的函数的作用域,因些会比其它函数占用更多的内存。过度使用闭包可能会导致内存占用过多。一般建议少用闭包。
7.2.2关于this对象
一般this对象在运行时基于函数的执行环境绑定的:在全局环境中,this等于window,而当函数作为某个对象的方法调用时,this等于那个对象。不过。匿名函数的执行环境具有全局性,因此其this对象通常指向window。
例: var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"
出现上面的情况的原因是:每个活动对象在被调用时,其活动对象会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此不可能直接访问外部函数中的这两个变量。
解决方法,把外部的this对象放在一个闭包能访问到的变量里:
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //"MyObject"
7.4私有变量
多查找作用域链中的一个层次,就会在一定程序上影响查找速度。而这正是使用闭包和私有变量明显的不中之处。