JavaScript—设计模式与开发实践–第二章 this、apply、call
this
JavaScript的this总是指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。
this指向
- this指向大致可以分为以下几种。
- 作为对象的方法调用。
- 作为普通函数调用。
- 构造器调用。
- Function.prototype.call 或 Function.prototype.apply 调用。
1.作为对象的方法调用
当函数作为对象的方法被调用时,this指向该对象:
var obj = {
a:1,
getA:function(){
alert( this === obj); //true
alert( this.a); // 1
}
}
obj.getA();
2.作为普通函数调用
当函数不作为对象的属性被调用时,也就是我们常说的普通函数模式,此时的this总是指向全局对象。在浏览器的JavaScript里,这个全局对象是window对象。
window.name = 'globalName';
var getName = function(){
return this.name;
}
console.log(getName());//globalName
3.构造器调用
JavaScript中没有类,但是可以从构造器创建对象,大部分JavaScript函数都可以当构造器使用,区别在于被调用的方式。当使用 new 运算符来调用函数时,该函数总会返回一个对象,构造器里的this 就指向返回的这个对象。
function Father (){
this.x = 10;
}
var a = new Father():
console.log(a.x); //10
但是使用 new 调用构造器时,还要注意一个问题,通过构造器显式返回了object类型的对象那么此次会返回这个对象,而不是之前的this,而如果显示返回不是object类型的,不会有这样的问题。
var MyClass = function(){
this.name = 'sven';
return {
name:'anne'
}
}
var obj = new MyClass();
alert(obj.name);
4.Function.prototype.call或Function.prototype.apply调用
call 和 apply 用来改变this指向。
var obj1 = {
name:'sven',
getName:function(){
return this.name;
}
};
var obj2 = {
name:'anne'
};
console.log(obj1.getName()); //sven
console.log(obj1.getName.call(obj2)); //anne
call 和 apply 的区别
他们的作用一模一样,区别在于传入的参数不同。
apply 接收两个参数 ,第一个参数指定了函数体内的this对象的指向,第二个参数为一个带下表的集合,这个集合可以为数组,也可以为类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数。
var func = function(a,b,c){
alert([a,b,c]); //[1,2,3]
};
func.call(null,[1,2,3])
call 传入的参数数量不固定,跟 apply 相同的是,第一个参数也是代表函数体内的 this 指向, 从第二个参数开始往后,每个参数被依次传入函数.
var func = function(a,b,c){
alert([a,b,c]); //[1,2,3]
};
func.call(null,1,2,3);
call 是包装在apply上面的一语法糖如果我们明确知道函数接受多少个参数,而且像一目了然表达形参和实参的对应古纳希,那么就可以用call来传达函数。
call和apply的用途
1.改变this指向
call 和 apply 常见的用途是改变函数内部的 this 指向
var obj1 = {
name:'sven'
};
var obj2 = {
name:'anne'
};
window.name = 'window';
var getName = function(){
alert(this.name);
};
getName();//window
getName.call(obj1);//sven
getName.call(obj2);//anne
当执行getName.call(obj1)这句代码时,getName函数中this就指向obj1对象,所以此处的
var getName = function(){
alert(this.name);
}
相当于
var getName = function(){
alert(obj1.name);
}
在实际开发中,经常会遇到 this 指向被不经意改变的场景,比如有一个 div 节点,div 节点 的 onclick 事件中的 this 本来是指向这个 div 的:
document.getElementById('div').onclick = function(){
alert(this.id);// div
}
加入该事件函数中有一个内部函数func,在事件内部调用func函数时,func函数体内的this就指向了window,二不是我们预期的div,
document.getElementById('div').onclick = function(){
alert(this.id);// div
var func = function (){
alert(this.id);// undefined
};
}
这个时候我们改变this指向,使其依然指向div
document.getElementById('div').onclick = function(){
alert(this.id);// div
var func = function (){
alert(this.id);// div
};
func.call(this);
}
2.借用其他对象的方法
我们知道,杜鹃既不会筑巢,也不会孵雏,而是把自己的蛋寄托给云雀等其他鸟类,让它们 代为孵化和养育。同样,在 JavaScript中也存在类似的借用现象。
借用方法的第一种场景是“借用构造函数”,可以实现类似继承的效果:
var A = function(name){
this.name = name;
};
var B = function(){
A.apply(this,arguments);
};
B.prototype.getName = function(){
return this.name ;
};
var b = new B('sven');
console.log(b.getName());// sven
A 就是借用过来的方法;
第二种场景:
例如:函数的参数列表 arguments 是一个类数组对象,虽然它也有“下标”,但它并非真正的数组, 所以也不能像数组一样,进行排序操作或者往集合里添加一个新的元素。这种情况下,我们常常 会借用 Array.prototype 对象上的方法。比如想往 arguments 中添加一个新的元素,通常会借用 Array.prototype.push:
(function(){
Array.prototype.push.call(arguments,3);
console.log(arguments); // [1,2,3]
})(1,2);
这段代码的意思是:原本arguments并非数组,没有数组的方法,所以要借用数组上的方法.
关于这种借用机制 在这本书中给出了原理,知识有限,给出了V8引擎的源码,看不懂,所以放弃知晓这种可以借用的其他对象的方法原理,但是给出的结论就是这个过程是一个属性复制的过程,把参数按照 下标依次添加到被 push的对象上面,顺便修改了这个对象的 length 属性。至于被修改的对象是 谁,到底是数组还是类数组对象,这一点并不重要。
对于这点的详细介绍,等知识完整之后再来回顾。