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}
显然class2
和class3
被标识为构造函数,class3
是class1
的部分应用,可以生成第一个参数为固定值的实例.
另一方面,尽管它们仍然可以充当(穷人的)构造函数,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:
- Let target be F.[[BoundTargetFunction]].
- Assert: IsConstructor(target) is true.
- Let boundArgs be F.[[BoundArguments]].
- 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.
- If SameValue(F, newTarget) is true, set newTarget to target.
- 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
值。
我完全错过了 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}
显然class2
和class3
被标识为构造函数,class3
是class1
的部分应用,可以生成第一个参数为固定值的实例.
另一方面,尽管它们仍然可以充当(穷人的)构造函数,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:
- Let target be F.[[BoundTargetFunction]].
- Assert: IsConstructor(target) is true.
- Let boundArgs be F.[[BoundArguments]].
- 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.
- If SameValue(F, newTarget) is true, set newTarget to target.
- 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
值。