12

我一直在寻找使用任意数量的参数调用 Javascript 构造函数的解决方案,并找到了一些不错的 SO 帖子,这让我相信这三个调用应该是一样的。但是,至少在 rhino 和 node.js 中,它们不会:

1. f = Date.bind(Date, 2000,0,1)
2. g = Date.bind.call(Date, 2000, 0, 1)
3. h = Date.bind.apply(Date, [2000, 0, 1])

第一个具有预期的结果:

print(new f()) //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)

但其他两个没有:

print(new g()) //=> Thu Feb 01 1900 00:00:00 GMT-0500 (EST)
print(new h()) //=> Wed Jun 01 1904 00:00:00 GMT-0400 (EST)

所以有些地方出了问题。想什么?apply将,bind和/或call与混合在一起是不是一个坏主意new

4

2 回答 2

31

先前接受的答案不正确。您可以使用 bind、call 和 apply 构造函数来创建新的构造函数——测试中唯一的问题是您忘记了 bind.apply 和 bind.call 正在应用和调用bind,而不是构造函数本身,所以你给出了错误的论点。

f = Date.bind(null, 2000,0,1)
g = Function.bind.call(Date, null, 2000, 0, 1)
h = Function.bind.apply(Date, [ null, 2000, 0, 1 ])

new f() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
new g() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)
new h() //=> Sat Jan 01 2000 00:00:00 GMT-0500 (EST)

这三个都是instanceof日期。

调用的参数是执行上下文,后跟要应用的参数。Apply 的参数是执行上下文和参数数组。Bind 的参数是执行上下文,后跟要绑定的参数。

例如,要应用的参数是应用 绑定的上下文(日期),后跟一个数组,该数组是绑定的参数因此第一个数组成员是绑定的上下文参数)。这就是调用或应用绑定令人困惑的原因;为两者提供上下文参数感觉很奇怪。

请注意,当使用带有构造函数的绑定时,上下文参数总是被忽略,因为“new”显式地创建了一个新上下文。当上下文参数与保持清晰无关时,我使用 null ,但它可以是任何东西。

同时,这些示例中的 apply 和 call 确实需要知道它们要应用/调用绑定的上下文是 Date 函数。我在可能的情况下将“日期”切换为“功能”,以帮助阐明实际在哪里提供上下文。当我们在 Date.bind 上调用 apply 或调用时,我们实际上是在调用 apply 或调用未附加到 Date 对象的 bind 方法。这种情况下的 bind 方法可能来自任何函数。它可能是 Number.bind.call(Date, null, 2000, 0, 1) 并且结果将完全相同。

如果原因不明显,请考虑以下示例之间的区别:

context.method();

var noLongerAMethod = context.method;
noLongerAMethod();

在第二种情况下,该方法已经脱离了它的原始上下文(......除非它以前被绑定)并且如果它在内部依赖“this”将表现不同。当我们将任何给定函数作为属性拉出绑定时,而不是直接执行它,它只是另一个指向 Function.prototype 上的通用绑定方法的指针。

就我个人而言,我认为我从来不需要调用或应用 bind,而且很难想象它会成为一个好的解决方案的情况,但是绑定构造函数以创建新的构造函数是我发现有时非常有用的东西. 无论如何,这是一个有趣的谜题。

于 2014-04-21T04:24:49.630 回答
4

bindand apply/call仅对function 起作用,但对constructor不起作用,因此基本上使用本机方法您不能这样做,一种方法是编写bindConstruct方法,但它可能涉及额外的复杂性:

function bindConstruct(fn) {
    // since constructor always accepts a static this value
    // so bindConstruct cannot specify this
    var extraArgs = [].slice.call(arguments, 1);

    // create a 'subclass' of fn
    function sub() {
        var args = extraArgs.concat([].slice.call(arguments));
        fn.apply(this, args);
    }
    sub.prototype = fn.prototype;
    sub.prototype.constructor = sub;

    return sub;
}

实际上,这为您的构造函数创建了一个子类。

然后你的代码:

var MyClass = function(x, y) {
    console.log(arguments);
    console.log(x + y);
}
var BindedMyClass = bindConstruct(MyClass, 1, 2, 3);
var c = new BindedMyClass(4, 5);
console.log(c instanceof MyClass);
console.log(c instanceof BindedMyClass);

您也可以将此函数写入本机函数Function.prototype或作为本机bind函数的扩展。

于 2012-05-15T06:15:06.547 回答