第 1 部分 JavaScript 技巧与高级特性.docx
- 文档编号:24917011
- 上传时间:2023-06-02
- 格式:DOCX
- 页数:20
- 大小:28.79KB
第 1 部分 JavaScript 技巧与高级特性.docx
《第 1 部分 JavaScript 技巧与高级特性.docx》由会员分享,可在线阅读,更多相关《第 1 部分 JavaScript 技巧与高级特性.docx(20页珍藏版)》请在冰豆网上搜索。
第1部分JavaScript技巧与高级特性
第1部分:
JavaScript技巧与高级特性
简介:
随着Ajax应用的流行,JavaScript语言得到了越来越多的关注。
开发人员对JavaScript的使用也日益深入。
JavaScript已经不再只是用来为页面添加一些花哨的效果,它已经成为构建Ajax应用的重要基石。
JavaScript作为一种专门设计用来在浏览器中执行的动态语言,它有许多重要的特性,并且不同于传统的Java或C++语言。
熟悉这些特性可以帮助开发者更好的开发Ajax应用。
本文章介绍了JavaScript语言中十三个比较重要的特性,包括prototype、执行上下文、作用域链和闭包等。
null与undefined
JavaScript中一共有5种基本类型,分别是String、Number、Boolean、Null和Undefined。
前3种都比较好理解,后面两种就稍微复杂一点。
Null类型只有一个值,就是null;Undefined类型也只有一个值,即undefined。
null和undefined都可以作为字面量(literal)在JavaScript代码中直接使用。
null与对象引用有关系,表示为空或不存在的对象引用。
当声明一个变量却没有给它赋值的时候,它的值就是undefined。
undefined的值会出现在如下情况:
∙从一个对象中获取某个属性,如果该对象及其prototype链中的对象都没有该属性的时候,该属性的值为undefined。
∙一个function如果没有显式的通过return来返回值给其调用者的话,其返回值就是undefined。
有一个特例就是在使用new的时候。
∙JavaScript中的function可以声明任意个形式参数,当该function实际被调用的时候,传入的参数的个数如果小于声明的形式参数,那么多余的形式参数的值为undefined。
关于null和undefined有一些有趣的特性:
∙如果对值为null的变量使用typeof操作符的话,得到的结果是object;而对undefined的值使用typeof,得到的结果是undefined。
如typeofnull==="object";typeofundefined==="undefined"
∙null==undefined,但是null!
==undefined
if(""||0)
对于if表达式,大家都不陌生。
JavaScript中if后面紧跟的表达式的真假值判断与其它语言有所不同。
具体请看表1。
表1.JavaScript中的真假值
类型
真假值
Null
总是为假(false)
Undefined
总是为假(false)
Boolean
保持真假值不变
Number
+0,-0或是NaN的时候为假,其它值为真
String
空字符串的时候为假,其它值为真
Object
总是为真(true)
从表1中可以看到,在JavaScript中使得if判断为假的值可能有null、undefined、false、+0、-0、NaN和空字符串("")。
==与===
JavaScript中有两个判断值是否相等的操作符,==与===。
两者相比,==会做一定的类型转换;而===不做类型转换,所接受的相等条件更加严格。
===操作符的判断算法
在使用===来判断两个值是否相等的时候,如判断x===y,会首先比较两个值的类型是否相等,如果不相等的话,直接返回false。
接着根据x的类型有不同的判断逻辑。
∙如果x的类型是Undefined或Null,则返回true。
∙如果x的类型是Number,只要x或y中有一个值为NaN,就返回false;如果x和y的数字值相等,就返回true;如果x或y中有一个是+0,另外一个是-0,则返回true。
∙如果x的类型是String,当x和y的字符序列完全相同时返回true,否则返回false。
∙如果x的类型是Boolean,当x和y同为true或false时返回true,否则返回false。
∙当x和y引用相同的对象时返回true,否则返回false。
==操作符的判断算法
在使用==来判断两个值是否相等的时候,如判断x==y,当x和y的类型一样的时候,判断逻辑与===一样;如果x和y的类型不一样,==不是简单的返回false,而是会做一定的类型转换。
∙如果x和y中有一个是null,另外一个是undefined的话,返回true。
如null==undefined。
∙如果x和y中一个的类型是String,另外一个的类型是Number的话,会将String类型的值转换成Number来比较。
如3=="3"。
∙如果x和y中一个的类型是Boolean的话,会将Boolean类型的值转换成Number来比较。
如true==1、true=="1"
∙如果x和y中一个的类型是String或Number,另外一个的类型是Object的话,会将Object类型的值转换成基本类型来比较。
如[3,4]=="3,4"
需要注意的是==操作符不一定是传递的,即从A==B,B==C并不能一定得出A==C。
考虑下面的例子,varstr1=newString("Hello");varstr2=newString("Hello");str1=="Hello";str2=="Hello",但是str1!
=str2。
Array
JavaScript中的数组(Array)和通常的编程语言,如Java或是C/C++中的有很大不同。
在JavaScript中的对象就是一个无序的关联数组,而Array正是利用JavaScript中对象的这种特性来实现的。
在JavaScript中,Array其实就是一个对象,只不过它的属性名是整数,另外有许多额外的属性(如length)和方法(如splice)等方便地操作数组。
创建数组
创建一个Array对象有两种方式,一种是以数组字面量的方式,另外一种是使用Array构造器。
数组字面量的方式通常为大家所熟知。
如vararray1=[2,3,4];。
使用Array构造器有两种方式,一种是vararray2=newArray(1,2,3);;另外一种是vararray3=Array(1,2,3);。
这两种使用方式的是等价的。
使用Array构造器的时候,除了以初始元素作为参数之后,也可以使用数组大小作为参数。
如vararray4=newArray(3);用来创建一个初始大小为3的数组,其中每个元素都是undefined。
Array的方法
JavaScript中的Array提供了很多方法。
∙push和pop在数组的末尾进行操作,使得数组可以作为一个栈来使用。
∙shift和unshift在数组的首部进行操作。
∙slice(start,end)用来取得原始数组的子数组。
其中参数start和end都可以是负数。
如果是负数的话,实际使用的值是参数的原始值加上数组的长度。
如vararray=[2,3,4,5];array.slice(-2,-1);等价于array.slice(2,3)。
∙splice是最复杂的一个方法,它可以同时删除和添加元素。
该方法的第一个参数表示要删除的元素的起始位置,第二个参数表示要删除的元素个数,其余的参数表示要添加的元素。
如代码vararray=[2,3,4,5];array.splice(1,2,6,7,8);执行之后,array中的元素为[2,6,7,8,5]。
该方法的返回被删除的元素。
length
JavaScript中数组的length属性与其他语言中有很大的不同。
在Java或是C/C++语言中,数组的length属性都是用来表示数组中的元素个数。
而JavaScript中,length属性的值只是Array对象中最大的整数类型的属性的值加上1。
当通过[]操作符来给Array对象增加元素的时候,如果[]中表达式的值可以转换为正整数,并且其值大于或等于Array对象当前的length的值的话,length的值被设置成该值加上1。
length属性也可以显式的设置。
如果要设置的值比原来的length值小的话,该Array对象中所有大于或等于新值的整数键值的属性都会被删除。
如代码清单1中所示。
清单1.Array的length属性
vararray=[];
array[0]="a";
array[100]="b";
array.length;//值为101
array["3"]="c";
array.length=4;//值为"b"的第101个元素被删除
arguments
在JavaScript中,在一个function内部可以使用arguments对象。
该对象中包含了function被调用时的实际参数的值。
arguments对象虽然在功能上有些类似数组(Array),但是它不是数组。
arguments对象与数组的类似体现在它有一个length属性,同时实际参数的值可以通过[]操作符来获取。
但是arguments对象并没有数组可以使用的push、pop、splice等function。
其原因是arguments对象的prototype指向的是Object.prototype而不是Array.prototype。
使用arguments模拟重载
Java和C++语言都支持方法重载(overloading),即允许出现名称相同但是形式参数不同的方法;而JavaScript并不支持这种方式的重载。
因为JavaScript中的function对象也是以属性的形式出现的,在一个对象中增加与已有function同名的新function时,旧的function对象会被覆盖。
不过可以通过使用arguments来模拟重载,其实现机制是通过判断arguments中实际参数的个数和类型来执行不同的逻辑。
如代码清单2中所示。
清单2.使用arguments模拟重载示例
functionsayHello(){
switch(arguments.length){
case0:
return"Hello";
case1:
return"Hello,"+arguments[0];
case2:
return(arguments[1]=="cn"?
"你好,":
"Hello,")+arguments[0];
};
}
sayHello();//结果是"Hello"
sayHello("Alex");//结果是"Hello,Alex"
sayHello("Alex","cn");//结果是"你好,Alex"
arguments.callee
callee是arguments对象的一个属性,其值是当前正在执行的function对象。
它的作用是使得匿名function可以被递归调用。
下面以一段计算斐波那契序列(Fibonaccisequence)中第N个数的值的代码来演示arguments.callee的使用,见代码清单3。
清单3.arguments.callee示例
functionfibonacci(num){
return(function(num){
if(typeofnum!
=="number")return-1;
num=parseInt(num);
if(num<1)return-1;
if(num==1||num==2)return1;
returnarguments.callee(num-1)+arguments.callee(num-2);
})(num);
}
fibonacci(100);
prototype与继承
JavaScript中的每个对象都有一个prototype属性,指向另外一个对象。
使用对象字面量创建的对象的prototype指向的是Object.prototype,如varobj={"name":
"Alex"};中创建的对象obj的prototype指向的就是Object.prototype。
而使用new操作符创建的对象的prototype指向的是其构造器的prototype。
如varusers=newArray();中创建的对象users的prototype指向的是Array.prototype。
由于一个对象A的prototype指向的是另外一个对象B,而对象B自己的prototype又指向另外一个对象C,这样就形成了一个链条,称为prototype链。
这个链条会不断继续,一直到Object.prototype。
Object.prototype对象的prototype值为null,从而使得该链条终止。
图1中给出了prototype链的示意图。
图1.JavaScriptprototype链示意图
在图1中,studentA是通过new操作符创建的,因此它的prototype指向其构造器Student的prototype;Student.prototype的值是通过new操作符创建的,其prototype指向构造器Person的prototype。
studentA的prototype链在图1中用虚线表示。
prototype链在属性查找过程中会起作用。
当在一个对象中查找某个特定名称的属性时,会首先检查该对象本身。
如果找到的话,就返回该属性的值;如果找不到的话,会检查该对象的prototype指向的对象。
如此下去直到找到该属性,或是当前对象的prototype为null。
prototype链在设置属性的值时并不起作用。
当设置一个对象中某个属性的值的时候,如果当前对象中存在这个属性,则更新其值;否则就在当前对象中创建该属性。
JavaScript中并没有Java或C++中类(class)的概念,而是通过prototype链来实现基于prototype的继承。
在Java中,状态包含在对象实例中,方法包含在类中,继承只发生在结构和行为上。
而在JavaScript中,状态和方法都包含在对象中,结构、行为和状态都是被继承的。
这里需要注意的是JavaScript中的状态也是被继承的,也就是说,在构造器的prototype中的属性是被所有的实例共享的。
如代码清单4中所示。
清单4.JavaScript中状态被继承的示例
functionStudent(name){
this.name=name;
}
Student.prototype.selectedCourses=[];
Student.prototype.addCourse=function(course){
this.selectedCourses.push(course);
}
Student.prototype.outputCourses=function(){
alert(this.name+"选修的课程是:
"+this.selectedCourses.join(","));
}
varstudentA=newStudent("Alex");
varstudentB=newStudent("Bob");
studentA.addCourse("算法分析与设计");
studentB.addCourse("数据库原理");
studentA.outputCourses();//输出是“Alex选修的课程是算法分析与设计,数据库原理”
studentB.outputCourses();//输出同上
代码清单4中的问题在于将selectedCourses作为prototype的属性之后,studentA和studentB两个实例共享了该属性,它们操作的实际是同样的数据。
this
JavaScript中的this一直是容易让人误用的,尤其对于熟悉Java的程序员来说,因为JavaScript中的this与Java中的this有很大不同。
在一个function的执行过程中,如果变量的前面加上了this作为前缀的话,如this.myVal,对此变量的求值就从this所表示的对象开始。
this的值取决于function被调用的方式,一共有四种,具体如下:
∙如果一个function是一个对象的属性,该funtion被调用的时候,this的值是这个对象。
如果function调用的表达式包含句点(.)或是[],this的值是句点(.)或是[]之前的对象。
如myObj.func和myObj["func"]中,func被调用时的this是myObj。
∙如果一个function不是作为一个对象的属性,那么该function被调用的时候,this的值是全局对象。
当一个function中包含内部function的时候,如果不理解this的正确含义,很容易造成错误。
这是由于内部function的this值与它外部的function的this值是不一样的。
代码清单5中,在myObj的func中有个内部名为inner的function,在inner被调用的时候,this的值是全局对象,因此找不到名为myVal的变量。
这个时候通常的解决办法是将外部function的this值保存在一个变量中(此处为self),在内部function中使用它来查找变量。
∙如果在一个function之前使用new的话,会创建一个新的对象,该funtion也会被调用,而this的值是新创建的那个对象。
如functionUser(name){this.name=name};varuser1=newUser("Alex");中,通过调用newUser("Alex"),会创建一个新的对象,以user1来引用,User这个function也会被调用,会在user1这个对象中设置名为name的属性,其值是Alex。
∙可以通过function的apply和call方法来指定它被调用的时候的this的值。
apply和call的第一个参数都是要指定的this的值,两者不同的是调用的实际参数在apply中是以数组的形式作为第二个参数传入的,而call中除了第一个参数之外的其它参数都是调用的实际参数。
如func.apply(anotherObj,[arg1,arg2])中,func调用时候的this指的是anotherObj,两个参数分别是arg1和arg2。
同样的功能用call来写则是func.call(anotherObj,arg1,arg2)。
清单5.内部function的this值
varmyObj={
myVal:
"HelloWorld",
func:
function(){
alert(typeofthis.myVal);//结果为string
varself=this;
functioninner(){
alert(typeofthis.myVal);//结果为undefined
alert(typeofself.myVal);//结果为string
}
inner();
}
};
myObj.func();
new
JavaScript中并没有Java或是C++中的类(class)的概念,而是采用构造器(constructor)的方式来创建对象。
在new表达式中使用构造器就可以创建新的对象。
由构造器创建出来的对象有一个隐含的引用指向该构造器的prototype。
所有的构造器都是对象,但并不是所有的对象都能成为构造器。
能作为构造器的对象必须实现隐含的[[Construct]方法。
如果new操作符后面的对象并不是构造器的话,会抛出TypeError异常。
new操作符会影响function调用中return语句的行为。
当function调用的时候有new作为前缀,如果return的结果不是一个对象,那么新创建的对象将会被返回。
在代码清单6中,functionanotherUser中通过return语句返回了一个对象,因此u2引用的是返回的那个对象;而functionuser并没有使用return语句,因此u1引用的是新创建的user对象。
清单6.new操作符对return语句行为的影响
functionuser(name){
this.name=name;
}
functionanotherUser(name){
this.name=name;
return{"badName":
name};
}
varu1=newuser("Alex");
alert(typeofu1.name);//结果为string
varu2=newanotherUser("Alex");
alert(typeofu2.name);//结果为undefined
alert(typeofu2.badName);//结果为string
eval
JavaScript中的eval可以用来解释执行一段JavaScript程序。
当传给eval的参数的值是字符串的时候,该字符串会被当成一段JavaScript程序来执行。
隐式的eval
除了显式的调用eval之外,JavaScript中的有些
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 部分 JavaScript 技巧与高级特性 技巧 高级 特性