[转]JavaScript 学习 - 提高篇
JavaScript学习-提高篇
一.JavaScript中的对象.
JavaScript中的Object是一组数据的key-value的集合,有点类似于Java中的HashMap,所有这些数据都是Object里的property.通常情况下,JavaScript中建立一个对象用"new"加上constructorfunction来实现.如newDate(),newObject()等.
varbook=newObject();
book.name="JavaScriptisCool";
book.author="tom";
book.pages=514;
上面例子中的name和page就是名为book的对象中的property.我们可以用delete来删除Object中的property:"deletebook.name;".除了Object,Date等buildin的对象外,我们可以写自己的constructorfunction,然后使用new就可以建立自己的对象.如上面的book可以写成:
functionBook(name,author,page){
this.name=name;
this.author=author;
this.page=page;
}
varabook=newBook("JavaScriptisCool","tom",514);
二.function的用法
在JavaScript中,function是一种数据类型,所有的function都是从buildin的Functionobject衍生的对象.所以在JavaScript中function可以作为参数传递,可以作为Object的property,也可以当作函数返回值.function在JavaScript中有两种用法,一种是当作constructor,前面加上newkeyword用来建立对象.一种是当作method,为其他对象调用.
注意function和method在中文里的意思相当,在有些语言里也可以通用.但是在JavaScript中,它们还是有所区别的.function本身是是一个对象,而当作为一个方法他属于一个对象时,就成为了一个这个对象的method,相当于一个对象种的属性.也就是说method是相对于一个对象而言的,function在某些情况下成为了一个对象的method.
functionBook(name,author,page){
this.name=name;
this.author=author;
this.page=page;
this.getReader=Book_getReader;
}
functionBook_getReader(){
//....
}
上面的例子种,functionBook_getReader()就成为了Book的一个名为getReader的method.call()和apply()是Functionobject的两个方法,它们也可以使一个function作为另一个对象的method来调用用.call()和apply()都需要参数,而第一个参数就是调用对象,也就是当function内部出现this时,this所指的对象.call()和apply()的区别在于call()可以传递任意长度参数,只要第一个参数时调用对象.而apply只接受两个参数,需要将除调用对象外的所有参数放入一个数组中.即:
functiongetBooksWithSameAuthor(form,to){
varname=this.author;
varbooks=...
//getbookswrittenbynameandfromyear"from"toyear"to"
returnbooks;
}
varabook=newBook("JavaScriptisCool","tom",514);
varbooks=getBooksWithSameAuthor.call(abook,1990,2005);
或
varbooks=getBooksWithSameAuthor.apply(abook,[1990,2005]);
当一个function不作为一个对象的method时,JavaScript会认为它是属于一个GlobleObject对象的method,这个GlobleObject在Browser中就是window类.所以从这个角度来说,function和method又可以统一起来了.
Functionobject还有一个非常重要的property:prototype.它是一个predefined的prototypeobject.当一个Function用作对象的constructor时,protptypeproperty将发挥作用,中文翻译叫原型.JavaScript的新对象就是通过function的原型来建立的.同时我们还可以利用prototype来动态的向对象中添加属性,如:
functionBook(name,author,page){
this.name=name;
this.author=author;
this.page=page;
}
varabook=newBook("JavaScriptisCool","tom",514);
Book.prototype.getInfo=getInfo;
functiongetInfo(){
returnthis.name+"writtenby"+this.author+"with"+this.page+"pages";
}
alert(abook.getInfo());
这里有一个例子,用prototype方法来实现callback:
Function.prototype.andThen=function(g){
varf=this;
returnfunction(){
f();g();
}
};
functionManager(){
this.callback=function(){};//donothing
this.registerCallback=function(callbackFunction){
this.callback=(this.callback).andThen(callbackFunction);
}
}
varmanager=newManager();
manager.registerCallback(sayHi);
manager.registerCallback(sayBye);
manager.callback();
三.JavaScript中的OO
JavaScript中的对象是一个属性和方法的集合.JavaScript也有对应于Java的Class和Instance的概念.我们可以把JavaScript中定义的
function(Function类的子类)看作是Class,而利用这个function构造的对象看是Instance.对应于Java的InstanceProperty,Instance
Method,Class(static)property,ClassMethod,JavaScript都可以实现.
1.InstanceProperty
InstanceProperty就是cuntructorfunction中定义的property,对于每一个instance都会有一份copy.
2.Classproperty
ClassProperty其实是cunstructorfunction作为对象本身所具有的属性(和Java不一样,Java中method不是数据,更不是对象).
3.InstanceMethod
InstanceMethod是指属于某一对象的method,方法内的this代表这个method从属的对象,注意虽然是InstanceMethod,并不是每一个Instance都有一个此方法的copy,所有Instance共享一个method.
4.ClassMethod
ClassMethod同样可以理解为constructorfunction作为对象自己具有的方法,和由这个constructor建立的对象没有直接联系.所以ClassMethod方法里使用this会产生混淆.因为这个this指的不是期望操作的对象,而是一个FunctionObject(constructor).
下面的例子定义了一个复数的对象,包含有上述四种域,值得仔细看看:
/*
*Complex.js:
*ThisfiledefinesaComplexclasstorepresentcomplexnumbers.
*Recallthatacomplexnumberisthesumofarealnumberandan
*imaginarynumberandthattheimaginarynumberiisthe
*squarerootof-1.
*/
/*
*Thefirststepindefiningaclassisdefiningtheconstructor
*functionoftheclass.Thisconstructorshouldinitializeany
*instancepropertiesoftheobject.Thesearetheessential
*"statevariables"thatmakeeachinstanceoftheclassdifferent.
*/
functionComplex(real,imaginary){
this.x=real;//Therealpartofthenumber
this.y=imaginary;//Theimaginarypartofthenumber
}
/*
*Thesecondstepindefiningaclassisdefiningitsinstance
*methods(andpossiblyotherproperties)intheprototypeobject
*oftheconstructor.Anypropertiesdefinedinthisobjectwill
*beinheritedbyallinstancesoftheclass.Notethatinstance
*methodsoperateimplicitlyonthethiskeyword.Formanymethods,
*nootherargumentsareneeded.
*/
//Returnthemagnitudeofacomplexnumber.Thisisdefined
//asitsdistancefromtheorigin(0,0)ofthecomplexplane.
Complex.prototype.magnitude=function(){
returnMath.sqrt(this.x*this.x+this.y*this.y);
};
//Returnacomplexnumberthatisthenegativeofthisone.
Complex.prototype.negative=function(){
returnnewComplex(-this.x,-this.y);
};
//ConvertaComplexobjecttoastringinausefulway.
//ThisisinvokedwhenaComplexobjectisusedasastring.
Complex.prototype.toString=function(){
return"{"+this.x+","+this.y+"}";
};
//Returntherealportionofacomplexnumber.Thisfunction
//isinvokedwhenaComplexobjectistreatedasaprimitivevalue.
Complex.prototype.valueOf=function(){returnthis.x;}
/*
*Thethirdstepindefiningaclassistodefineclassmethods,
*constants,andanyneededclasspropertiesaspropertiesofthe
*constructorfunctionitself(insteadofaspropertiesofthe
*prototypeobjectoftheconstructor).Notethatclassmethods
*donotusethethiskeyword:theyoperateonlyontheirarguments.
*/
//Addtwocomplexnumbersandreturntheresult.
Complex.add=function(a,b){
returnnewComplex(a.x+b.x,a.y+b.y);
};
//Subtractonecomplexnumberfromanother.
Complex.subtract=function(a,b){
returnnewComplex(a.x-b.x,a.y-b.y);
};
//Multiplytwocomplexnumbersandreturntheproduct.
Complex.multiply=function(a,b){
returnnewComplex(a.x*b.x-a.y*b.y,
a.x*b.y+a.y*b.x);
};
//Herearesomeusefulpredefinedcomplexnumbers.
//Theyaredefinedasclassproperties,wheretheycanbeusedas
//"constants."(Note,though,thattheyarenotactuallyread-only.)
Complex.zero=newComplex(0,0);
Complex.one=newComplex(1,0);
Complex.i=newComplex(0,1);
四.对象的继承
JavaScript有多种方式模拟继承.
1.利用function:
functionsuperClass(){
this.bye=superBye;
this.hello=superHello;
}
functionsubClass(){
this.inheritFrom=superClass;
this.inheritFrom();
this.bye=subBye;
}
或者:
functionsubClass(){
superClass.call(this);
}
先定义subClass的inheritFrom方法,再调用这个方法(方法名称并不重要),或者直接使用FunctionObject的call方法将this做参数,都可以模拟实现从superClass的继承.注意调用superClass时的this指向.这个方法就是在执行subClass的cunstructorfunction时,先执行supperClass的cunstructorfunction.这个方法的缺点在于子类仅仅是在自己的构造函数中,将this作为参数调用了父类的构造函数,将构造函数赋予父类的所有域赋予子类.所以,任何父类在构造函数之外(通过prototype)定义的域,子类都无法继承.而且子类的构造函数一定要在定义自己的域之前调用父类的构造函数,免得子类的定义被父类覆盖.使用这种方法子类也尽量不要使用prototype来定义子类的域,因为prototype的定义在子类new的之后就执行,所以它一定会在调用父类构造函数前,同样会有被父类的定义覆盖的危险.
2.利用prototype:
functionsuperClass(){
this.bye=superBye;
this.hello=superHello;
}
functionsubClass(){
this.bye=subBye;
}
subClass.prototype=newsuperClass();
subClass.prototype.constructor=superClass;
这里将一个superClass的实例设置成subclass的原型:protytype,由于newsuperClass实例一定会调用父类prototype定义的所有域,所以这种方法避免了上一种方法的一个问题,父类可以通过prototype来描述域.可以实现从superClass的继承.而这个方法也有缺点,由于子类的peototype已经是父类的实例(Object实例),不能再被实例化,所以new子类实例的时候,父类的所有非基本数据类型(见JavaScript数据类型)都将是referencecopy而非数据copy.简单说就是所有的父类域在子类中虽然存在,但看起来就像Java中的static域一样在子类间share.被一个子类改变,所有子类都会改变.
注意这里的最后一句,改变了子类prototype中的constructor属性.它对子类使用没有影响,仅仅是为了在调用instanceOf方法时它使得子类实例返回subClass.
3.ParasiticInheritance(寄生继承)
functionsuperClass(){
this.bye=superBye;
this.hello=superHello;
}
functionsubClass(){
this.base=newsupperClass();
base.sayBye=subBye;
returnbase;
}
这种继承其实是一种扩展,因为在调用instanceOf时,子类会返回父类名称,它的好处在于在构造函数继承的基础上解放了父类,父类可以使用prototype定义自己的域,但是子类仍然不建议使用prototype,以免被父类覆盖.为了可以使子类的instanceof返回正确类型,我们可以再改进一下:
functionsubClass(){
this.base=newsupperClass();
for(varkeyinthis.base){
if(!this[key]){
this[key]=this.base[key];
}
}
this.sayBye=subBye;
}
将所有的父类域拷贝给子类一份,不再返回父类,instanceof子类实例时就可以返回正确类型.
五.this的用法
通常情况下,this代表的是前面提到的GlobleObject.也就是Browser环境时的windowObject.当function作为某一对象的method时,this代表这个function所属的object.下面这段代码有格错误,涉及到this的使用:
functionEmployee(a){
this.name=a;
}
functioninit(){
John=Employee("Johnson");
alert(John.name);
}
在init()中我们少了一个newkeyword.于是这个代码就会报错,因为Browser把Employee当作是windowobect的一个method,里面的this指的就是windowobject.init()应该改为:
functioninit(){
John=newEmployee("Johnson");
alert(John.name);
}
同时我们也可以将Employee的constructor函数修改,防止类似的错误:
functionEmployee(a){
if(!(thisinstanceofEmployee))returnnewEmployee(a);
this.name=a;
}
这样,我们即使使用原来的init()方法,也不会报错了.
六.ArrayinJavaScript
Array和Object本质上是一样的,只是Array需要由index来索引它其中的属性.index为>=0的整数.
Array有一系列buildin的方法:
1.jion()将array中的所有element以string的形式连在一起:
vara=[1,2,3];
s=a.join();//s=="1,2,3"
s=a.join(":");//s=="1:2:3"
2.reverse()将Array的element顺数颠倒
vara=[1,2,3];
a.reverse();
s=a.join();//s=="3,2,1"
3.sort()排序,默认按字母顺序排序casesensitive,可以自定义排序方式.
vara=[111,4,33,2];
a.sort();//a==[111,2,33,4]
a.sort(function(a,b){//a==[2,4,33,111]
returna-b;//Returns<0,0,or>0
});
4.concat()连接多个Array
vara=[1,2,3];
a.concat(4,5);//return[1,2,3,4,5]
a.concat([4,5]);//return[1,2,3,4,5]
a.concat([4,5],[6,7])//return[1,2,3,4,5,6,7]
a.concat(4,[5,[6,7]]);//return[1,2,3,4,5,6,7]
5.slice()返回Array的切片,原Array不变.
vara=[1,2,3,4,5];
a.slice(0,3);//Returns[1,2,3]
a.slice(3);//Returns[4,5]
a.slice(1,-1);//Returns[2,3,4],-1meansthelastindexofthearray
a.slice(-3,-2);//Returns[3],fromthethirdlastindextothesecondlastindex
6.splice向一个Array中添加或删除element.第一个参数表示位置,第二个参数表示删除长度,后面任意长的参数表示在1删除位置添加的elements.
vara=[1,2,3,4,5,6,7,8];
a.splice(4);//Returns[5,6,7,8];ais[1,2,3,4]
a.splice(1,2);//Returns[2,3];ais[1,4]
a.splice(1,1);//Returns[4];ais[1]
vara=[1,2,3,4,5];
a.splice(2,0,'a','b');//Returns[];ais[1,2,'a','b',3,4,5]
a.splice(2,2,[1,2],3);//Returns['a','b'];ais[1,2,[1,2],3,3,4,5]
7.push()andpop()向Array末尾添加或删除element
8.unshift()andshift()向Array的开头添加或删除element