最近在看《javascript语言精粹》,感觉里面很多内容写得特别好,这本书不太适合初学者阅读,而是适合有一定js开发基础的的读者,推荐有基础的又想继续学习javascript的去看看。
这里记录并摘抄了一些书中内容,是关于javascript中函数中的this的使用的问题,归纳得很好,如果有对this的使用还比较模糊不是很清楚的可以在此一起讨论学习。
调用一个函数会暂停当前函数的执行,传递控制权和参数给新函数。除了声明时定义的形式参数,每个函数还自动接收两个附加的参数:this和arguments。参数this在面向对象编程中十分重要,它的值取决于调用的模式。在javascript中一共有4种调用模式:方法调用模式、函数调用模式、构造器调用模式和apply调用模式。这些模式在如何初始化关键参数this上存在差异。
调用运算符是跟在任何产生一个函数值的表达式之后的一对圆括号。圆括号内可包含零个或多个用逗号隔开的表达式,每个表达式产生一个参数值,每个参数值被赋予函数声明时定义时的参数名。当实际参数(实参arguments)的个数与形式参数(形参parameters)的个数不匹配时不会导致运行时错误。如果实际参数过多了,超出的参数将被忽略。如果实际参数值过少,缺失的值将会被替换为undefined。对参数值不会进行类型检查:任何类型的值都可以被传递给参数。
方法调用模式:当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this被绑定到这个对象。如果调用表达式包含一个提取属性的动作(即包含一个 . 点表达式或[subscript]下标表达式),那么它就是被当做一个方法来调用。
1 //创建myObject对象,他有一个value属性和一个increment方法 2 //increment方法接受一个可选参数。如果参数不是数字,那么默认使用数字1 3 var myObject = { 4 value: 0, 5 increment:function(inc){ 6 this.value += (typeof inc === "number" ? inc : 1); 7 } 8 }; 9 myObject.increment(); 10 document.writeln(myObject.value); //结果为1 11 12 myObject.increment(2); 13 document.writeln(myObject.value); //结果为3 14 15 //常见的简单事件绑定就属于这种调用模式 16 eleNode.onclick = function(){ 17 console.log(this); //绑定事件的eleNode 18 }
函数调用模式:当一个函数并非一个对象的属性时,那么它就是被当做一个函数来调用的:
var sum = add(3,4); //sum的值为7,函数调用
以此方式调用函数时,this被绑定到全局对象(window?)。这是语言设计上的一个错误,倘若语言设计正确,那么当内部函数被调用时,this应该仍然绑定到外部函数的this变量。这个设计错误的后果就是方法不能利用内部函数来帮助它工作,因为内部函数的this函数被绑定错误的值,所以不能共享该方法对对象的访问权。幸运的是,有一个很容易的解决方案:如果该方法定义一个变量并给它赋值为this,那么内部函数就可以通过那个变量访问到this。按照约定,我把那个变量命名为that:
1 //给myObject增加一个double方法 2 myObject.double = function(){ 3 var that = this; 4 var helper = function(){ 5 that.value = add(that.value, that.value); 6 }; 7 helper(); 8 }; 9 //以方法的形式调用double 10 myObject.double(); 11 document.writeln(myObject.value); //结果为6
构造器调用模式:如果在一个函数前面带上new来调用,那么背地里将会创建一个链接到该函数的prototype成员的新对象,同时,this会被绑定到那个新对象中。
New前缀也会改变return语句的行为。
1 //创建一个名为Con的构造器函数,它构造一个带有status属性的对象 2 var Con = function(string){ 3 this.status = string; 4 }; 5 //给Con的所有实例提供一个名为getStatus的公共方法 6 Con.prototype.getStatus = function(){ 7 return this.status; 8 }; 9 //构造一个Con实例。 10 var myCon = new Con("confused"); 11 document.writeln(myCon.getStatus()); //打印显示"confused"
Apply调用模式:apply方法让我们构建一个参数数组传递给调用函数,他也允许我们选择this的值。apply方法接收两个参数,第一个是要绑定给this的值,第二个就是一个参数数组。另一种为Call调用模式,本质上和Apply调用模式是没区别的,只是传递参数的方式有点区别,具体区别可看apply和call方法的讲解。
构造一个包含两个数字的数组,并将它们相加。
1 var array = [3,4]; 2 var sum = add.apply(null,array); //sum的值为7 3 //构造一个包含status成员的对象 4 var statusObject = { 5 status: "A-OK" 6 }; 7 //statusObject并没有继承自Con.prototype,但我们可以在statusObject上调用getStatus方法,尽管statusObject并没有一个名为getStatus的方法 8 var status = Con.prototype.getStatus.apply(statusObject); //status值为"A-OK"
前辈们教我们的一句话就是,写代码并不难,只要善于归纳、善于总结,了解其中的规律并记住他们然后拿来运用几次就掌握了。第一次写博客在此分享,难免会有不足的地方,虽然大部分都是书中的内容,有不好的希望各位能指出,我会虚心接受学习。