ecmascript-6 Function.prototype.bind() 如何处理 class 构造函数?

How does ecmascript-6 Function.prototype.bind() handle a class constructor?

我完全错过了 ES6 革命,我在 7 年后回到 JavaScript,发现发生了许多非常奇怪的事情。

特别是 Function.prototype.bind() 处理 class 构造函数的方式。

考虑一下:

// an ES6 class
class class1 {
  constructor (p) {
    this.property = p;
  }
}

var class2 = class1.bind(passer_by);
var class3 = class2.bind(passer_by,3);

class2() // exception, calling a constructor like a function
class3() // idem

console.log (new class1(1)) // class1 {property: 1}
console.log (new class2(2)) // class1 {property: 2}
console.log (new class3() ) // class1 {property: 3}

// An ES5-style pseudo-class
function pseudoclass1 (p) {
  this.property = p;
}

var property = 0;
var passer_by = { huh:"???" }

var pseudoclass2 = pseudoclass1.bind(passer_by);
var pseudoclass3 = pseudoclass1.bind(passer_by,3);

pseudoclass1(1); console.log (property)  // 1 (this references window)
pseudoclass2(2); console.log (passer_by) // Object { huh: "???", property: 2 }
pseudoclass3() ; console.log (passer_by) // Object { huh: "???", property: 3 }

console.log (new pseudoclass1(1)) // pseudoclass1 {property: 1}
console.log (new pseudoclass2(2)) // pseudoclass1 {property: 2}
console.log (new pseudoclass3() ) // pseudoclass1 {property: 3}

显然class2class3被标识为构造函数,class3class1的部分应用,可以生成第一个参数为固定值的实例.

另一方面,尽管它们仍然可以充当(穷人的)构造函数,ES5 风格的函数确实提供了由 bind() 设置的 this 的值,可以看出当它们作用于倒霉的 passer_by 而不是像未绑定的 pseudoclass1.

那样破坏全局变量时

显然,所有这些构造函数都以某种方式访问​​ this 的值,从而允许它们构造对象。然而他们的 this 据说绑定到另一个对象。

所以我想一定有某种机制在起作用,将正确的 this 提供给构造函数,而不是传递给 bind().

的任何参数

现在我的问题是,我可以在这里和那里找到一些关于它的知识,甚至一些代码显然来自 Chrome 的 V8 的某些版本(其中函数 bind() 本身似乎做了一些事情)关于构造函数的特殊内容),或关于插入原型链中的神秘 FNop 函数的讨论,以及,如果我可以补充的话,偶尔的货物崇拜 bu[beep]it.

但是我找不到关于这里实际发生的事情的解释,关于为什么实施这种机制的理由(我的意思是,使用新的传播运算符和解构等等,不会'是否有可能产生相同的结果(将一些参数应用于构造函数)而不必对 bind() 进行适度记录的黑客攻击?)及其范围(它适用于构造函数,但还有其他类型的函数除了传递给 bind() 的值之外,它们正在被喂食其他东西?)

我试图阅读 2015 年和 2022 年的 ECMA 262 规范,但当我的大脑开始从我的耳朵漏出时不得不停止。我将调用堆栈追溯为:
19.2.3.2
9.4.1.3
9.4.1.2
7.3.13 其中关于构造函数的说法是:“如果未传递 newTarget,则此操作等效于:new F(...argumentsList)”。啊哈。所以这个伪递归调用应该允许以某种方式模拟 new...Erf...

如果有仁慈而精明的人可以让我更好地了解正在发生的事情,告诉我 ECMA 规范的哪一部分处理这种机制,或者更笼统地指出我在正确的方向。

老实说,我已经厌倦了用头撞墙。这个 bit of Chrome code 似乎表明 bind() 正在为构造函数做一些特殊的事情,这对我来说是不可理解的。所以我至少想要一个解释,如果一切都失败了。

这与 类 没有任何关系,但与 .bind 的工作方式有关。

你一直在正确的轨道上。这里最相关的部分是 9.4.1.2.

回顾一下:ECMAScript 区分两种类型的函数:callable 函数和 constructable 函数。函数 expressions/declarations 两者都是,而例如class 构造函数只能构造,箭头函数只能调用。

在规范中,这由函数的内部 [[Call]][[Construct]] 方法表示。

new 将触发调用内部 [[Construct]] 方法。

.bind 将 return 一个新的函数对象,对 [[Call]][[Construct]] 有不同的实现。那么 the "bound" version of [[Construct]] 是什么样子的呢?

9.4.1.2 [[Construct]] ( argumentsList, newTarget )

When the [[Construct]] internal method of a bound function exotic object, F that was created using the bind function is called with a list of arguments argumentsList and newTarget, the following steps are taken:

  1. Let target be F.[[BoundTargetFunction]].
  2. Assert: IsConstructor(target) is true.
  3. Let boundArgs be F.[[BoundArguments]].
  4. Let args be a new list containing the same values as the list boundArgs in the same order followed by the same values as the list argumentsList in the same order.
  5. If SameValue(F, newTarget) is true, set newTarget to target.
  6. Return ? Construct(target, args, newTarget).

这意味着绑定构造函数将使用绑定参数 (F.[[BoundArguments]]) 和传入参数 (argumentsList) “构造”原始函数 (F.[[BoundTargetFunction]]) ,但它完全忽略了绑定的 this 值(即 F.[[BoundThis]])。


but are there other sorts of functions that are being fed something else than the value passed to bind() ?

是的,箭头函数。箭头函数没有自己的 this 绑定(取而代之的是最近的 this 提供环境的值),因此绑定箭头函数也会忽略绑定的 this 值。