6.1对象
6.1.1对象直接量
包含在一个花括号中,每个属性名可以是一个JS标识符或一个字符串或一个字符串,而每个属性值可以是一个常量或任意的JS表达式。
例:
Varempty={};
Varpoint={x:0,y:0};
Varhomer={“name”:”homer”,”age”:34};
(2)new创建对象
Vard=newDate();
Varreg=newRegExp();
6.1.2对象属性
对象的属性和变量工作方式相似,即可以把值存储其中,也可以从中读取值。一旦通过给属性赋值创建了该属性,就可以在任何时刻修改这个属性的值。
Varbook={};
Bool.widht=20//create
(1)属性的枚举
For/in语句枚举属性。
(2)检查属性的存在
In运算符可以用来测试一属性的存在
(3)删除属性delete运算符
删除属性并不是仅仅把属性设置为undefined,它实际上从对象中移除了属性,在删除之后,for/in将不会枚举该属性。
6.1.3作为关联数组的对象
就是用像读取数组的方式来读取属性值。
Obj.propery;
Obj[“property”]//关联数组
6.1.4通用ojbect属性和方法
1.constructor属性
每个对象都有一个constructor属性,它引用了初始化这个对象的构造函数。
例:
Vard=newDate();
d.constructor==Date//returntrue
2.hasOwnProperty()方法
如果此方法中指定的参数属性来自本地定义的一个非继承的属性,此方法返回true。
Varo={};
O.hasOwnProperty(“unde”);//returnfalse,thepropertyisnotdefined
O.hasOwnProperty(“toString”);//false,toStringisanihheritedproperty
Math.hasOwnProperty(“cos”);//true:theMathobjecthasacosproperty
3.propertyIsEnumeralbe()方法
如果此方法中指定的参数属性来自本地定义的一个非继承的属性,并且如果这个属性可以在一个for/in循环中枚举,此方法返回true。
注意,一个对象的所有用户定义的属性都是可以枚举的,不能枚举的属性通常是继承的属性,因此这个方法几乎总是会和hasOwnProperty()返回相同的值。
4.isPrototypeOf()方法
如果此方法所属的对象是参数的原型对象,那么就返回true。
Varo={};
Object.prototype.isPrototypeOf(o);//true
Object.isPrototypeOf(o);//false
6.2数组
6.2.1创建数组
字面值创建数组
Varempty=[];
Varprimes=[2,3,4];
Varmisc=[1.1,true,”ik”];
直接量的值还可以是表达式
Varbase=23;
Vartable[base,base+3,base+4];
直接量还可以是对象直接量或数组直接量
Varb=[[1,{x:1,y:2}],2];
未定义的元素通过忽略逗号之间的元素值来创建
Varcount=[1,,2];//数组有三个元素,中间的元素没有赋值
Varundef=[,,];//数组有2个元素,全部为赋值
用Array()构造函数创建数组
Vara=newArray():
Varb=newArray(3,2.3,4,”saf”,”sf”);
Varc=newArray(10);//这里的每个元素都没有赋值为undefined
6.2.2数组的读写
(1).添加数组新元素
JS中可以在任何时刻改变元素个数,而且由于JS数组是稀疏的,即数组的下标不必落在一个连续的数字范围内,只有那些真正存储在数组中的元素才能由JS实现分配内存。
例
a[0]=1;
a[100]=2;
这里JS解释器只给数组下标为0和100的元素分配内存,而试图访问其它的元素时将得到一个undefined
(2).删除数组元素
用delete运算符把一个元素设置为一个undefined值,但是元素本身还继续存在,要真正地删除一个元素,那么必须使用一个数组方法。(当用方法移出了一个元素后,再去访问该元素,虽然也会返回一个undefined值,但用length属性去测试会发现长度变短了,说明是真正的移出了一个元素;但是仅仅用delete运算符去删除一个元素,用length属性去测试时会发现长度没有变,说明没有真正移出元素!)
(3).数组长度(length属性)
(4).截断或增长数组
Length属性是可读可写的,通过修改length的值可以截断和增长数组的长度。
(5).多维数组
JS不支持真正的数组,它支持数组的数组,即变长数组!
Vara=newArray(10);
For(varb=0;b<a.length;b++)
{
A[b]=newArray(10);
}
(6).数组方法
参考API去
(七).函数
7.1函数定义和调用
JS也不会检测传递的数据是不是那个函数所要求的类型,也不会检测传递给它的参数个数是否正确。如果传递的参数比函数需要的个数多,那么多余的值会忽略掉;如果传递的参数比函数需要的个数少,那么所被忽略的参数就被赋予undefined。
7.1.1函数直接量
Varf=function(x){returnx*x;}
或
Varf=fucntionfact(x){returnx*x;}//也可以加函数名字
Vartest=(function(x){returnx*x;})(10);//定义后马上调用
7.1.2函数命名
函数名小写字母开始!
7.2函数参数
7.2.1.可选参数
它是利用了当传递给函数的参数个数小于函数的参数时,其它的参数有一个undefined值。要用可行参数时,必须能够为忽略掉的参数分配一个默认的合理值。
Functiontest(o,/*optional*/a){
a=a||[];
for(varpropertyino)a.push(property);
returna;
}
注意,在使用可选参数时,应该确保把可选参数放在参数列表的末尾。
7.2.2.可变长度的参数列表:arguments对象
Arguments并不是一个数组,它是一个对象,它虽然定义了带编码的元素和length属性。Arguments对象允许完全地存取那些实际参数值,即使某些或全部参数还没有被命令。例如有一个函数f,它定义了一个参数x,但是调用f时传递给了它两个参数,那第个一参数可以用x或arguments[0]可以存取第一个参数,而第二个实际参数只能通过arguments[1]来存取。
一般argumetns对象来编写这样的函数:期待固定数目的具有名字的且必须的函数,紧接着是任意数目的没有命名的可选参数。
Arguments[]数组和命名了的参数不过是引用同一变量的两种不同方法。相反,用参数名改变一个参数的值同时会改变通过arguments数组获得的值;通过arguments[]数组改变参数的值同样会改变用参数名获取的参数值。
记住,arguments只是一个普通的JS标识符,而不是一个保留字,如果函数有一个参数或者局部变量使用了这个名字,它就会隐藏对arguments对象的引用。
属性callee
Arguments对象用callee属性来引用当前正在执行的函数!!
7.3作为方法的函数
Varcalculator={
Operand1:1,
Operand2:2,
Compute:function(){
This.result=this.operand1+this.operand2;}
任何用作方法的函数都被效地传递了一个隐式的参数,即this,调用对象。
当一个函数作为函数而不是作为方法调用的时候,这个this关键字引用全局对象.
当一个嵌套的函数(作为函数)在一个包含的函数之中调用,而这个包含的函数是作为方法调用的,this关键字在包含的函数中有一个值,但是它却引用嵌套的函数体的内部的全局对象。
7.4函数的属性和方法
7.4.1属性length
这个属性是只读属性,返回的是函数需要的实际参数的数目,也就是在函数的开式参数列表中声明的形式参数的数目。注意,和arguments.length不同,这个length属性在函数体的内部和外部都有效。
7.4.2定义自己的函数属性
当函数需要使用一个在调用过程中都保持不变的值时,使用function对象的属性比定义全局变量更加方便。也即我们可以为函数定义属性。
例:
UniqueInteger.counter=0;
FunctionuniqueInteger(){
ReturnuniqueInteger.counter++;}
这里的counter相当于函数的静态局部变量一样!!!
7.4.3方法apply()和call()
Call和apply的第一个参数都是要调用函数的对象,在函数体内这一参数是关键字this值。Call()剩余的参数是传递给要调用的函数的值。
例:
F.call(o,1,2)
上例是把两个数字传递给函数f(),并将它作为对象的方法调用。
Apply()传递给函数的参数是由数组指定的。
F.apply(o,[1,2]):
7.5函数作用域和闭包
7.5.1词法作用域
JS函数是通过记法来划分作用域的,而不动态地划分作用域的。这意味着,它们在定义它们的作用域里运行,而不是在执行它们的作用域里运行。当定义一个嵌套的函数时,作用域链就包括外围的函数。这意味着嵌套的函数可以访问包含函数的所有参数和局部变量。
7.5.2作为闭包的嵌入函数
如果定义了一个嵌套的函数,这个函数引用了调用对象,因为调用对象在这个函数所定义的作用域链的顶端。如果一个嵌套的函数只是在外围函数的内部使用,那么,对嵌套函数的唯一引用在调用对象之中。当外围函数返回的时候,嵌套的函数引用了调用对象,并调用对象引用了嵌套的函数,(互相引用??)但是没有其它的东西引用它们二者,因此对这两个对象进行垃圾回收。
如果把一个嵌套的函数的引用保存到一个全局作用域中,情况不同了。使用嵌套函数作为外围函数的返回值,或者把嵌套的函数存储为某个其他对象的属性来做到这一点。在这种情况下,有一个对嵌套的函数的外部引用,并且嵌套的函数将它的引用保留给外围函数的调用对象。结果是外围函数的一次特定调用的调用对象依然存在,函数的参数和局部变量的名字和值在这个对象中得以维持。JS代码不会以任何方式访问这个调用对象,但是它所定义的属性是对嵌套函数任何调用的作用域链的一部分。(注:如果一个外围函数存储了两个嵌套函数的全局引用,这两个嵌套函数共享同一个调用对象,并且,一个函数的一次调用所做出的改变对于另一个函数的调用来说也是可见的)
JS函数是将要执行的代码以及执行这些代码的作用域构成一个综合体,叫闭包。
小结闭包:如果一个嵌套函数有全局引用吧,那么这个引用的产生肯定是调用了外围函数,因而产生了调用对象,因些这个调用对象会一直保持,由于这个调用对象的参数和变量等是嵌套函数的一部分,那么以后对嵌套函数的调用就会访问这个调用对象的这些参数和变量。
例:
Functionmakefun(x){returnfunction(){returnx;};}
Vara=[makefun(0),makefun(1),makefun(2)];
Alert(a[0]);//display0
Alert(a[1]);//display1
例:
VarUniqueid=(function(){
Varid=0;
Retrunfunction(){returnid++;};})();
Alert(uniqueid());//display0
Alert(uniqueid());//display1
这里的uniqueid得到的是函数Retrunfunction(){returnid++;}
当用uniqueid()调用函数时才得到值。从这种例子可以看出点什么呢?我觉得只可意会不可言传。反正就是闭包函数在定义它的作用域里执行,它用了包含函数里的数据。
(八)、类、构造函数和原型
8.1构造函数
通过构造函数首字母大写,以区分普通函数。实际上它们是没有区别的!
FunctionCar(){}
8.2原型和继承
JS对象从它的原型那里继承属性
一个对象的原型就是它的构造函数的prototype属性的值。所有的函数都有一个prototype属性,当这个函数被定义的时候,prototype属性自动创建和初始化。Prototype属性的初始化是一个对象,这个对象中带有一个属性。这个属性名为constructor,它指回到和原型相关联的那个构造函数。添加给这个原型对象的任何属性,都会成为被构造函数所初始化的对象的属性。
原型对象是放置方法和其他不变属性的理想地方。
注意,继承作为查找一个属性值的过程的一部分。属性并非从原型对象那里复制到新对象,它们只是像那些对象的属性一样地出现。首先,使用原型对象可以显著地减少每个对象的内存数量。其次,即便是在对象创建以后才添加到原型中的属性,对象也可以继承它。
8.2.1读取和写入继承的属性
当读取对象o的属性p的时候,JS首先检查o是否有一个名为p的对象。如果没有,它接下来检查o的原型对象是否有一个p的属性。
当写入一个对象的属性值的时候,JS不会使用原型对象。
8.3JS中模拟类
8.3.1实例属性
每个对象都拥有它的实例属性的一份单独拷贝。默认情况下,JS中的任何对象属性都是一个实例属性,这里我们特指由构造函数创建和初始化的属性。
8.3.2实例方法
在JS中,通过构造函数的原型对象中的一个属性设置为一个函数值,从而定义了一个实例方法。每个实例方法由一个类的所有实例方法共享。
8.3.3类属性
也就是为构造函数定义属性,跟普通的函数一样的。
Tes.Unique=2;
8.3.4类方法
也就是为让相应函数成为构造函数的一个属性而已。
Test.f=function(){}
8.3.5私有成员
JS中没有私有成员。一般把属性设置为以下划线开头的属性。
8.3.6定义类的常用两种方法
1.混合构造函数/原型方式
FunctionCar(scolor,sdoors){
This.color=scolor;
This.door=sdoors;
}
Car.prototype.showColor=function(){alert(this.color);};
2.动态原型方法
FunctionCar(scolor,sdoors){
This.color=scolor;
This.door=sdoors;
If(typeofCar._initialized==”undefined”)
{
Car.prototype.showColor=function(){alert(this.color);};
Car._initialized=true;
}
}
8.4通用对象模型
8.4.1toString方法
当定义一个类时,就为它定义一个toString()方法。
8.4.2valueOf方法
如果一个类需要把对象转换为某种基本类型,一般是转换为数字。那么可以为这个类定义此方法。
注意,在某些条件下,在把一个对象转换为一个字符串的时候,valueof方法可以比toString()方法更优先使用。因此如果要把类强制转换为一个字符串时,可能需要更加显式调用toString()方法。
8.4.3比较方法
要比较对象时最好自定义一个方法。因为等同运算符和相等运算符比较的是对象的引用,而关系运算符会把对象转换为数字或字符串。
8.5超类和子类
例:functionRectangle(w,h){this.width=w;this.height=h;}
Rectangle.prototype.area=function(){returnthis.width*this.height;}
FunctionDerivedRectangle(x,y,w,h)
{
Rectangle.call(this,x,y);
This.x=x;
This.y=y;
}
DerivedRectangle.prototype=newRectangle();
DeleteDerivedRectangle.prototype.width;
DeleteDerivedRectangle.prototype.height;
DerivedRectangle.prototype.constructor=DerivedRectangle;
DerivedRectangle.prototype.contains=function(){returnthis.x+this.y+this.width+this.height;
}
首先超类构造函数作为新创建的对象的一个方法调用的。必须显示地把这个原型对象创建为超类的一个实例,然后显示地设置原型对象的constructor属性。另外,可以删除超类构造函数在原型对象中创建的所有属性,因为我们要的原型对象从它的原型那里继承的那些属性。
8.5.1构造函数链
如果只有一层子类,可以为子类原型对象添加一个superclass属性
DerivedRectangle.prototype.superclass=Rectangle;
FunctionDerivedRectangle(x,y,w,h)
{
This.superclass(w,h);
This.x=x;this.y=y;
}
8.5.2调用被覆盖的方法
任何时候,当为一个类定义toString方法时,就会覆盖object的toString()方法。例:
DerivedRectangler.prototype.toString=function(){
Return“(“+this.x+”,”+this.y+”)”+
Rectangle.Prototype.toString.apply(this);
}
超类的toString的实现是用超类的原型对象的一个属性。无注调用该方法。使用apply或call方法。
(九)、正则表达式
9.1正则表达式支持
9.1.1创建正则表达式
例:
Varmatch=”cat”;
VarreCat=/cat/;
或reCat=newRegExp(“cat”);
Alert(reCat.test(match));//
RegExp的test方法测试字符串是否匹配这个模式。
Varmatch=”abat,aCat,afAtbat,afaTcat”;
VarreAt=/at/;
VararrMatches=reAt.exec(match);
RegExp对象的exce方法返回一个数组,数组中的第一条目是第一个匹配;其他的是反向引用。
String对象数组有的match()方法,它会返回一个包含所有字符串中的所有匹配的数组。
Vars=”abat,aCat,afAtbat,afaTcat”;
VarreAt=/at/gi;
或reCat=newRegExp(“cat”,”gi”);
Vararr=s.match(reAt);
String对象的Search()字符串方法返回在字符串中出现的一个匹配的位置。
Alert(s.search(reAt));
9.1.2扩展的字符串方法
String对象的replace()和split()方法都可以传递正则表达式。
这里的replace()方法的第二个参数可以传递一个函数。这处函数可以接受一个参数,即匹配了的文本,并返回应当进行替换的文本
vartt=”Theskyosred”;
varre=/red/;
varsText=tt.replace(re,function(sMatch){return“blue”;});
sMatch这个参数就是用模式re匹配后的文本!!
9.2简单模式
9.2.2元字符
正则表达式的元字符是正则表达式语法的一部分。
([{\^$|}?*+.
任何时候要在正则表达式中使用元字符都必须对它们进行转义用’\’。
Varretest=/\?/;
或varretest=newRegExp(“\\?”);//必须用两个反斜杠,如果用的一个反斜杠,那么JS解释器会按照\n的方式翻译\?.
9.2.2使用特殊字符
转义字符
9.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]单词字符
\W[^a-xA-Z_0-9]非单词字符
9.2.4量词
1.简单量词
代码描述
?出现零次或一次
*出现零次或多次(任意次)
+出现一次或多次
{n}刚好出现n次
{n,m}至少出现n次,至多m次
{n,}至少出现n次
2.贪婪的、惰性的和支配性量词
贪婪性量词先看整个字符串是否匹配,如果没有匹配,它先去掉该字符串最后一个字符,并再次尝试。这个过程一直重复下去。
惰性量词先看字符中的第一个字符是否匹配,如果不匹配,就读入下一个量词,组成两个字符的字符串,再比匹配。这个过程一直重复下去,直到匹配或没有匹配。
支匹配性量词只尝试匹配整个字符串,如果整个字符串不能产生匹配,不做进一步尝试。
贪婪惰性支配
????+
**?*+
++?++
{n}{n}?{n}+
{n,m}{n,m}?{n,m}+
{n,}{n,}?{n,}+
例:
VarsToMatch=”abbbaabbbaaabbb1234”;
Varre1=/.*bbb/g;
Varre2=/.*?bbb/g;
Varre3=/.*+bbb/g;
如果想获得的匹配是”abbb”,”aabbb”,”aaabbb”,这三个表达式中只有re2能匹配成功。
9.2.5标志
字符含义
i执行不区分大小定的匹配
g执行一个全局匹配,即找到所有匹配
m多行模式,^匹配一行的开头和字符串的开头,$匹配一行的结尾或字符串的结尾
9.3复杂模式
9.3.1分组
分组是通过用括号包围一系列字符、字符类及量词来使用的。每次必须全部匹配括号中的所有字符。
例:
varreg1=/(dog)?/;//匹配0个或一个dog字符串序列
varreg2=/(dog)*/;//匹配0个或多个dog字符串序列
varreg3=/(dog){2}/;//字符序列”dog”将在一行上连续出现两次!
9.3.2反向引用
在表达式计算完后对分组的利用,即每个分组都被存储在一个特殊的地方以备使用。这些存储在分组中的特殊值,我们称之为反向引用。
反向引用是按照从左到右遇到的左括号字符的顺序进行创建和编号的。例如表达式(A?(b?(c?)))将产生编号1-3的三个反向引用。
(1)(A?(B?(c?)))
(2)(B?(c?))
(3)(c?)
首先,使用正则表达式对象的test()exec()方法或string对象的match()search()方法后,反向引用的值可以从RegExp构造函数中得到
例:
vartest=”#12345”;
varre=/#(\d+)/;
re.test(test);
alert(RegExp.$1);
使用这test方法后,所有的反向引用都被保存在RegExp构造函数,从RegExp.$1开始,如果还有第二个引用,就是RegExp.$2.
然后,还可以直接在定义分组的表达式中包含反向引用,这可以通过使用特殊转义序列\1\2等实现。
vartt=”dogdog”;
varre=/(dog)\1/;
alert(re.test(tt));//outputs‘true”
正则表达式re首先创建单词dog的组,然后又被特殊转义序列\1引用,使得这个表达式等同于/dogdog/。
最后,反向引用可以用在String对象的replace()方法中,这通过使用特殊字符序列$1、$2等实现。
varstr=”12345678”;
varre=/(\d{4})(\d{4})/;
varnew=str.replace(re,”$2$1”);
alert(new);//outputs“56781234”;
9.3.3候选
例
varstrRed=”red”;
varstrBlue=”blue”;
varre1=/(red|blue)/;
把一个管道符”|”放在两个单独的模式之间,如上所示。当对这个模式进行测试时,只要有一个被匹配就返回true,因为它相当于一个or模式,即任何一个匹配就可以。
9.3.4非捕获性分组
前面的产生反向引用的分组,我们称之为捕获性分组。如果分组不想产生反向引用,那么只要在括号的后面加一个问号和一个紧跟的冒号.
vart=”#3142134”;
varre=/#(?:\d+)/;
re.test(t);
alert(RegExp.$1);//outputs“”;
9.3.5前瞻
它告诉正则表达式运算器向前看一些字符而不移动其位置。
正向前瞻检查的是接下来的是接下来出现的是不是某个特定字符集。而负向前瞻则是检查接下来的不应该出现的特定字符集。
创建正向前瞻要将模式放在(?=和)之间,注意,这不是分组,虽然它也用到了括号。事实上,分组是不会考虑前瞻的存在的。
varstr1=”bedroom”;
varstr2=”bedding”;
varre=/(bed(?=room))/;
alert(re.test(str1));//outputs“true”
alert(RegExp.$1));//outputs“bed”;
alert(re.test(str2))//ouputs“false”;
创建负向前瞻要将模式放在(?和)之间,
varstr1=”bedroom”;
varstr2=”bedding”;
varre=/(bed(?!room))/;
alert(re.test(str1));//outputs“false”
alert(RegExp.$1));//outputs“bed”;
alert(re.test(str2))//ouputs“true”;
9.3.6边界
指定了模式的位置。$,^可以改变匹配的方向,或者说是改变贪婪和惰性量词的默认行为。看下面例子
边界描述
^行开头
$行结尾
\b单词的边界
\B非单词边界
例:
varstr=”Imp
varre=/(w+)\.$/;
re.test(str);
这个例子查找的一行结束之前出的跟着一个句号的一个或多个单词字符。这个例子它改变了那个贪婪性量词的方向,它先匹配整个串,然后去掉头的一个字符,继续匹配下去。
varstr=”Imp
varre=/\(.+?)\b/;
re.test(str);
alert(RegExp.$1);//outputs“imp
这里用惰性量词来制定在单词边界之前可以出现任何字符,且可出现一个或多次。
注意行的开始和行的结束,通常由^和$表示的位置,对应也也认为是单词的边界。
9.3.7多行模式
要指定多行模式,只要在正则表达式后面添加一个m选项。这会让$边界匹配换行符(\n)以及字符串真正的结尾。
多行模式同样会改变^边界的行为,这时它会匹配换行符之后的位置。
varstr=”firstsecond\nthirdfourth\nfifthsixth”;
varre=/(\w+)$/gm;
varnew=str.match(re);//outputs“second”,”fourth”,”sixth”
9.4RegExp对象
9.4.1实例属性
有用的lastIndex属性,它可告诉你正则表达式在某个字符串中停止之前,查找了多远(即下次匹配将会从哪个字符位置开始)。这个属性只对exec和test方法才会填入(废话,RegExp对象只有这两个方法)。
varstr=”bbqisshort”;
varre=/b/g;
re.exec(str);
alert(re.lastIndex);//outputs“1”;下次从第二个b开始,位置1
re.exec(str);
alert(re.lastIndex);//outputs“2”