在JavaScript中绑定/应用构造函数

时间:2012-05-15 06:03:08

标签: javascript constructor variadic-functions

我一直在寻找使用任意数量的参数调用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)

所以有些东西在某处乱了。关于什么的想法?将applybind和/或callnew等内容混合在一起是不是一个坏主意?

2 个答案:

答案 0 :(得分:27)

previously accepted answer不正确。您可以使用bind,call和apply with constructor来创建新的构造函数 - 测试中唯一的问题是您已经忘记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 (Date)的上下文,后跟一个数组,该数组是的参数< / em> bind(所以第一个数组成员是bind的上下文参数)。这就是调用或应用绑定令人困惑的原因;向两者提供上下文参数感觉很奇怪。

请注意,在使用带有构造函数的bind时,始终会忽略context参数,因为&#39; new&#39;显式创建一个新的上下文。当context参数与保持清楚无关时,我使用null,但它可以是任何东西。

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

如果不明白为什么,请考虑以下示例之间的区别:

context.method();

var noLongerAMethod = context.method;
noLongerAMethod();

在第二种情况下,该方法已脱离其原始上下文(...除非之前已被绑定),并且如果它依赖于&#39;这种情况将表现不同。内部。当我们将任何给定函数作为属性绑定时,而不是直接执行它,它只是指向Function.prototype上泛型绑定方法的另一个指针。

我个人认为我不需要打电话或申请绑定,很难想象它会成为一个好的解决方案的情况,但绑定构造函数来创建新的构造函数是我偶尔发现非常有用的东西。无论如何,它是一个有趣的谜题。

答案 1 :(得分:4)

bindapply / call仅适用于功能而非构造函数,因此基本上使用原生方法不能这样做,一种方法是编写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功能的扩展名。