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

binding/applying constructors in JavaScript

本文关键字:应用 构造函数 绑定 JavaScript      更新时间:2023-09-26

我一直在寻找使用任意数量的参数调用Javascript构造函数的解决方案,并找到了一些很好的SO帖子,这让我相信这三个调用应该工作相同。 然而,至少在犀牛和节点.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混合在一起只是一个坏主意吗?

之前接受的答案是不正确的。你可以使用绑定,调用和应用构造函数来创建新的构造函数 - 测试中唯一的问题是你忘记了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 的参数是执行上下文和参数数组。绑定的参数是执行上下文,后跟要绑定的参数。

因此,例如,要应用的参数是要应用绑定的上下文(Date(,后跟一个数组,该数组是绑定的参数

(因此第一个数组成员是绑定上下文参数(。这就是为什么调用或应用绑定令人困惑的原因;为两者提供上下文参数感觉很奇怪。

请注意,当与构造函数一起使用绑定时,上下文参数始终被忽略,因为"new"显式创建新上下文。当上下文参数与保持清晰无关时,我使用 null,但它可以是任何东西。

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

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

context.method();

var noLongerAMethod = context.method;
noLongerAMethod();

在第二种情况下,该方法已脱离其原始上下文(...除非它以前被绑定(,如果它在内部依赖于"this",则行为会有所不同。当我们把任何给定函数的绑定作为属性拉取时,而不是直接执行它,它只是指向 Function.prototype 上泛型绑定方法的另一个指针。

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

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函数的扩展。