1

调用一个函数会暂停当前函数的执行,传递控制权和参数给新函数。除了声明时定义的形式参数,每个函数还接受两个附加参数: this 和 arguments。参数 this 的值取决于调用的模式。在JavaScript中一共有4中调用模式: 方法调用模式、函数调用模式、构造器调用模式和 apply 调用模式。

方法调用模式(隐式绑定)

当一个函数被保存为对象的一个属性时,我们称它为一个方法。当一个方法被调用时,this 被绑定到该对象。如果调用表达式包含一个提取属性的动作(即包含一个 .点表达式或[subscript]下标表达式),那么它就是被当做一个方法来调用。

// 创建 myObject 对象,有一个 value 属性和一个 increment 方法
// increment 方法接受一个可选的参数。如果参数不是数字,那么默认使用数字1

var myObject = {
    value: 0,
    increment: function (inc){
        this.value += typeof inc === 'number' ? inc : 1;
    }
};

myObject.increment();
document.writeln(myObject.value);   // 1

myObject.increment(2);
document.writeln(myObject.value);   // 3

方法可以使用 this 访问自己所属的对象,所以它能从对象中取值货对对象进行修改。 this 到对象的绑定发生在调用的时候。

函数调用模式(默认绑定)

当一个函数并非一个对象的属性时,它就是被当做一个函数来调用的。

var sum = add(3,4);   // sum 的值为7

以此模式调用函数时,this 被绑定到全局对象。这是语言设计上的一个错误。若正确,那么当内部函数被调用时,this 应该仍然绑定到外部函数的 this 变量。这样设计的后果就是方法不能利用内部函数来帮助它工作,因为内部函数的 this 被绑定了错误的值,不能共享该方法对对象的访问权。

解决方案: 如果该方法定义一个便令并给它赋值为 this,那么内部函数就可以通过那个变量访问到 this。按照约定,把那个变量命名为 that:

// 给 myObject 增加一个 double 方法

myObject.double = function () {
    var that = this;   // 解决方法
    
    var helper = function () {
        that.value = add(that.value, that.value);
    };
    
    helper();   // 以函数的形式调用helper
};

// 以方法的形式调用 double

myObject.double();
document.writeln(myObject.value);   // 6

如果使用严格模式(strict mode),那么全局对象无法使用默认绑定,this 会绑定到undefined

function foo() {
    "use strict";
    
    console.log(this.a);
}

var a = 2;

foo();   // TypeError: this is undefined

这里有一个微妙但是非常中亚偶的细节,虽然 this 的绑定规则完全取决于调用位置,但是只有 foo() 运行在非 strict mode 下时,默认绑定才能绑定到全局对象;严格模式下与 foo() 的调用位置无关:

function foo() {
    console.log(this.a);
}

var a = 2;

(function(){
    "use strict";
    
    foo();   // 2
})();

构造器调用模式

如果在一个函数前面带上 new 来调用,那么背地里将会创建一个连接到该函数的 prototype 成员的新对象,同时 this 会被绑定到那个新对象上。

首先,我们来了解一下构造函数。在传统的面向类的语言中,“构造函数”是类中的一些特殊方法,使用 new 初始化类时会调用类中的构造函数。通常的形式为:

something = new MyClass(..);

JavaScript也有一个 new 操作符,使用方法看起来和那些面向类的语言一样,但 JavaScript 中的 new 机制实际上和面向类语言完全不同。
在 JavaScript 中,构造函数只是一些使用 new 操作符时被调用的函数。它们不属于某个类,也不会实例化一个类。实际上,它们甚至都不能说是一种特殊的函数类型,它们只是被 new 操作符调用的普通函数而已。包括内置对象函数在内的所有函数都可以用 new 来调用,这种函数调用被称为构造函数调用。实际上并不存在所谓的构造函数,只有对于函数的构造调用。
使用 new 来调用函数,会自动执行以下操作:

  1. 创建(或者说构造)一个全新的对象。

  2. 这个新对象会被执行[[原型]]连接

  3. 这个新对象会绑定到函数调用的 this

  4. 如果函数没有返回其他对象,那么 new 表达式中的函数会自动返回这个新对象

// 创建一个名为 Quo 构造器函数。它构造一个带有 status 属性的对象

var Quo = function (string) {
    this.status = string;
};

// 给 Quo 的所有实例提供一个名为 get_status 的公共方法

Quo.prototype.get_status = function () {
    return this.status;
};

// 构造一个 Quo 实例

var myQuo = new Quo("confused");
document.writeln(myQuo.get_status());   // confused

Apply 调用模式

apply 方法让我们构建一个参数数组传递给调用函数,也允许我们选择 this 的值。apply 方法接收两个参数,第一个是要绑定给 this 的值,第二个就是一个参数数组。

// 构造一个包含两个数字的数组,并将它们相加

var array = [3,4];
var sum = add.apply(null,array);   // 7

// 构造一个包含 status 成员的对象

var statusObject = {
    status: 'A-OK';
};

// statusObject 并没有继承自 Quo.prototype,但我们可以在 statusObject 上调用 get_statuse 方法,尽管statusObject 并没有一个名为 get_status 的方法

var status = Quo.prototype.get_status.apply(statusObject);   // A-OK

puhongru
581 声望58 粉丝

立志成为一名合格的前端开发工程师


« 上一篇
枚举 for in