了解Function.prototype.apply

use*_*256 3 javascript prototype

以下代码摘自MDN页面Function.prototype.apply:

Function.prototype.construct = function (aArgs) {
    var fConstructor = this,
        fNewConstr = function () { fConstructor.apply(this, aArgs); };
    fNewConstr.prototype = fConstructor.prototype;
    return new fNewConstr();
};

function MyConstructor() {
    for (var nProp = 0; nProp < arguments.length; nProp++) {
        this["property" + nProp] = arguments[nProp];
    }
}

var myArray = [4, "Hello world!", false];
var myInstance = MyConstructor.construct(myArray);
alert(myInstance.property1); // alerts "Hello world!"
alert(myInstance instanceof MyConstructor); // alerts "true"
alert(myInstance.constructor); // alerts "MyConstructor"
Run Code Online (Sandbox Code Playgroud)

我对这段代码有两个问题:

  1. 我知道如果我使用var myInstance = new MyConstructor();它会调用MyConstructor(),但是如何var myInstance = MyConstructor.construct(myArray);调用MyConstructor()

  2. MyConstructor.construct(myArray);被称为方法MyConstructor,但该方法被声明为Function.prototype.construct,而不是MyConstructor.prototype.construct.Function.prototype.construct和之间有什么区别MyConstructor.prototype.construct

Qan*_*avy 11

TL; DR:

Q1:它通过嵌套函数调用,该函数本身由new fNewConstr()调用调用.它只是允许将参数列表作为数组传递,而不修改函数实际如何与其参数一起使用.
Q2:原型上的任何内容都可以被该构造函数的所有实例访问(并且Function是JavaScript中所有本机函数的构造函数),但MyConstructor本身并不是对象实例,这就是它需要定义的原因Function.prototype.

我把答案分成两部分,问题1和问题2:

问题1

Function.prototype.construct意味着允许传递数组作为参数列表,而不使用Function.prototype.bind.它在嵌套函数中调用原始函数,并将传递的参数作为数组调用,并将原型设置为原始函数的原型.

在给出的代码中,Function.prototype.construct以下是什么方法

Function.prototype.construct = function (aArgs) {
    var fConstructor = this, 
Run Code Online (Sandbox Code Playgroud)

第一行允许我们访问被调用的函数 - 该范围内的this(说明中的ThisBinding)的值是我们想要调用的函数.

        fNewConstr = function () { fConstructor.apply(this, aArgs); };
Run Code Online (Sandbox Code Playgroud)

下一行将调用原始函数Function.prototype.apply,它允许该函数的参数作为数组传递.之所以this作为ThisBinding传递,是为了对被调用函数thisThisBinding的属性进行赋值,在这种情况下,它将是由"new"运算符创建的新对象.

    fNewConstr.prototype = fConstructor.prototype;
Run Code Online (Sandbox Code Playgroud)

这简单地使得"new"运算符创建的返回对象的原型与调用函数相同,因为在新构造函数上调用"new"运算符,而不是原始运算符.

    return new fNewConstr();
Run Code Online (Sandbox Code Playgroud)

这几乎是不言自明的 - 它创建了一个新对象,其中包含由构造函数在属性上创建的关联值,this或者返回函数返回的对象(如果有).

什么是返回是一样的,如果new MyConstructor()被直接调用,除了参数被给予的方式(就像如何Function.prototype.call()Function.prototype.apply()同时存在).例如,这两个代码示例是等效的:

new MyConstructor(4, "Hello world!", false); // { property0: 4, property1: 'Hello world!', property2: false }
MyConstructor.construct([4, "Hello world!", false]); // { property0: 4, property1: 'Hello world!', property2: false }
Run Code Online (Sandbox Code Playgroud)

......就像这些是等价的:

function foo(a, b, c, d, e) { return a + b + c + d + e; }
foo.call(null, 1, 2, 3, 4, 5); // 15
foo.apply(null, [1, 2, 3, 4, 5]); // 15
Run Code Online (Sandbox Code Playgroud)

这是两者之间的唯一区别 - 一个用参数列表简单地调用构造函数,另一个用参数列表表示为数组(构造函数本身仍然将参数作为列表而不是数组).

问题2

所有函数实例都从Function原型中借用,因此定义的任何方法(或者就此而言,属性)Function.prototype都可用于所有函数.举个例子:

function foo() { return 1 + 1; }   
Function.prototype.bar = function ()
{   var result = this();
    if (typeof result === 'number' && isFinite(result)) return result + 0.5;
    return NaN;
};

foo.bar(); // 2.5
Run Code Online (Sandbox Code Playgroud)

但是,当你声明的方法上MyConstructor.prototype,它仅适用于实例MyConstructor,而不是MyConstructor它本身,如下图所示:

function MyConstructor(num) { this.x = 1 + num; }
MyConstructor.prototype.bar = function () { return 2 + 2; };

MyConstructor.bar(); // TypeError: MyConstructor.bar is not a function
MyConstructor.x; // undefined
Run Code Online (Sandbox Code Playgroud)

看看这是怎么回事?您需要在以下实例上使用它MyConstructor:

var foo = new MyConstructor(4);
foo.x; // 5
foo.bar(); // 4
Run Code Online (Sandbox Code Playgroud)

如果它在原型上,对原型的任何编辑都将影响对象上的所有原型方法/值:

function MyConstructor(num) { this.x = 1 + num; }
MyConstructor.prototype.bar = function () { return 2 + 2; };

var foo1 = new MyConstructor(6);
foo1.bar(); // 4

MyConstructor.prototype.bar = function () { return 3 + 3; };
var foo2 = new MyConstructor(9);

foo2.bar(); // 6
foo1.bar(); // 6
Run Code Online (Sandbox Code Playgroud)

请记住,如果您完全创建一个新的原型对象,那么在更改之前创建的实例仍将引用旧对象:

var foo1 = new MyConstructor(6);
foo1.bar(); // 4

MyConstructor.prototype = { bar: function () { return 3 + 3; } };

var foo2 = new MyConstructor(9);
foo2.bar(); // 6
foo1.bar(); // 4?!
Run Code Online (Sandbox Code Playgroud)

请记住,直接声明的属性MyConstructor不会传递给实例:

MyConstructor.bar2 = function () { return 42; }; // the answer to everything?
MyConstructor.y = 3.141592653589793238462643383279502881197169399; // I only can remember that much

MyConstructor.bar2(); // 42, as you'd expect
var foo = new MyConstructor(99);

foo.bar2(); // TypeError: foo.bar2 is not defined
foo.y; // undefined
Run Code Online (Sandbox Code Playgroud)

基于原型的编程可能非常强大(实际上我更喜欢它的类,但这是一个不同的故事),但你需要了解它的作用.如果使用得当,你可以用它做很棒的事情 - 继续学习!:)