在 JavaScript 中函数的调用有 4 种模式:
- 方法调用模式;
- 函数调用模式;
- 构造器调用模式;
- Apply 调用模式。
方法调用模式
当一个函数被保存为对象的一个属性时,称之为方法。当一个方法被调用时, this
被绑定到调用该方法的对象上。 this
到对象的绑定发生在方法调用的时候,这种延迟绑定使得函数可以对 this
高度复用。通过 this
可取得其所属对象的上下文的方法称为公共方法。
函数调用模式
当一个函数并非一个对象的属性时,它就是被当作一个函数来调用的。这种模式调用函数时 this
被绑定到全局对象上,而不是外部函数的 this
。解决方案是在外部函数中定义一个变量(一般命名为 that
)并赋值为 this
,内部函数就可以通过这个变量访问外部函数的 this
。
构造器调用模式
构造器调用模式就是在函数调用前加上 new
关键字。构造器调用模式会创建一个连接到函数的 prototype
成员的新对象,同时 this
被绑定到新对象上。一个函数如果没有 return
语句,则默认会返回 undefined
。 new
关键字会改变默认添加的 return
语句的行为,使函数返回 this
。可以通过添加 return
语句重写函数默认的返回值。
构造器函数就是使用构造器调用模式调用的函数。如果一个构造器函数没有使用 new
关键字进行调用,可能会导致污染全局对象,因此构造器函数通常使用大驼峰命名法命名,或者在构造器函数中对 this
进行判断,使构造器能够安全地被调用。
// 安全构造器
var MyClass = function(args) {
if (this instanceof MyClass) {
// 构建对象
} else {
return new MyClass(args);
}
}
Apply 调用模式
在前面 3 种调用模式中, this
的值都是自动绑定的,而 Apply 调用模式则通过 apply()
、 call()
和 bind()
方法改变函数调用时的执行上下文,即 this
的值。
function setInfo (name, age) {
this.name = name;
this.age = age;
}
var charlie = {}, lucy = {};
setInfo.apply(charlie, ["charlie", 9]);
console.log(charlie.name + ', ' + charlie.age); // charlie, 9
setInfo.call(lucy, 'lucy', 8);
console.log(lucy.name + ', ' + lucy.age) // lucy, 8
apply(this, args)
和 call(this, arg1, ..., argN)
方法类似,区别只在于传入参数的方式不同。
注: apply()
也可以传入 arguments
对象作为参数。
bind()
方法的参数与 call()
方法类似,但不同的是 bind()
方法不会立即调用对应的函数,而是生成并返回一个新的函数。
window.id = 1;
var obj = {
id: 2
};
function sayId () {
console.log(this.id);
}
var objSayId = sayId.bind(obj);
sayId(); // 1
objSayId(); // 2