在 JavaScript 编程中,this 关键字总是让初学者感到迷惑, Function.prototype.call 和 Function.prototype.apply 这两个方法也有着广泛的运用。
this
JavaScript 的 this 总是会指向一个对象,而具体指向哪个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时的环境。this 的指向大致可以分为以下 4 种:
- 作为对象的方法调用
- 作为普通函数调用
- 构造器调用
- Function.prototype.call 或 Function.prototype.apply 调用
1. 作为对象的方法调用
当函数作为对象的方法被调用时,this 指向该对象:
window.a = 0;
var obj = {
a: 1,
getA: function() {
alert( this === obj ); // true
alert( this.a ); // 1
}
};
obj.getA();
2. 作为普通函数调用
当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式。此时的 this 总是指向全局对象也就是 window 对象。
window.a = 0;
var obj = {
a: 1,
getA: function() {
return this.a;
}
};
var getA = obj.getA;
getA(); // 0
如何来解决此 this 指向的问题呢,我们可以使用后面所讲到的 call 来改变 this 的指向,修改代码:
getA.call(obj) // 1
这个时候 this 就指向了 obj,打印结果为 1。
3. 构造器调用
JavaScript 中没有类,但是可以从构造器中创建对象,同时也提供了 new 操作符,使得构造器看起来更像一个类。当用 new 运算符调用函数时,该函数总会返回一个对象,通常情况下,构造器里的 this 就指向返回的这个对象,如下代码:
var MyClass = function() {
this.name = "xiaoming";
};
var obj = new MyClass();
console.log(obj.name); // xiaoming
4. FuncFunction.prototype.call 或 Function.prototype.apply 调用
跟普通函数调用相比,用 Function.prototype.call 或 Function.prototype.apply 可以动态地改变传入函数的 this:
var obj1 = {
name: "one",
getName: function() {
return this.name;
}
};
var obj2 = {
name: "two"
}
console.log( obj1.getName() ); // one
console.log( obj1.getName.call(obj2) ); // two
call 和 apply
call 和 apply 的作用是一模一样的,都是可以动态的改变 this 的指向,区别在于传入参数形式的不同。
apply 接受两个参数,第一个参数指定了函数体内 this 对象的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以是类数组:
var func = function(a, b, c) {
alert([a, b, c]); // 1, 2, 3
}
func.apply(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 的时候,如果我们传入的第一个参数为 null,函数体内的 this 会指向默认的宿主对象,在浏览器中则是 window:
var func = function(a, b, c) {
alert(this === window); // true
}
func.call(null, 1, 2, 3);
但如果是在严格模式下,函数体内的 this 还是为 null:
var func = function(a, b, c) {
"use strict"
alert(this === null); // true
}
func.call(null, 1, 2, 3);