提问人:kuroi neko 提问时间:7/28/2021 最后编辑:kuroi neko 更新时间:7/28/2021 访问量:163
ecmascript-6 Function.prototype.bind() 如何处理类构造函数?
How does ecmascript-6 Function.prototype.bind() handle a class constructor?
问:
我完全错过了 ES6 革命,7 年后我又回到了 JavaScript,发现发生了许多非常奇怪的事情。
其中一个特别是处理类构造函数的方式。Function.prototype.bind()
考虑一下:
// 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 风格的函数确实提供了 set by 的值,当它们作用于倒霉的全局变量而不是像未绑定的全局变量那样笨拙时,可以看出这一点。this
bind()
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
如果一些善良而精明的灵魂能让我更好地了解正在发生的事情,向我展示 ECMA 规范的哪些部分涉及这种机制,或者更普遍地为我指明正确的方向,我将不胜感激。
说实话,我厌倦了用头撞墙。这段 Chrome 代码似乎表明正在为构造函数做一些特殊的事情,这对我来说是不可理解的。因此,如果其他一切都失败了,我至少想要对此进行解释。bind()
答:
这与类无关,而是与工作方式有关。.bind
你走在正确的轨道上。这里最相关的部分是 9.4.1.2。
回顾一下:ECMAScript 区分了两种类型的函数:可调用函数和可构造函数。函数表达式/声明两者兼而有之,而例如 构造函数只能构造,箭头函数只能调用。class
在规范中,这由函数的内部和方法表示。[[Call]]
[[Construct]]
new
将触发内部方法的调用。[[Construct]]
.bind
将返回一个新的函数对象,该对象具有不同的实现 和 。那么[[Construct]]
的“绑定”版本是什么样子的呢?[[Call]]
[[Construct]]
9.4.1.2 [[构造]] ( argumentsList, newTarget )
当使用参数 argumentsList 和 newTarget 列表调用绑定函数外来对象 F 的绑定函数外来对象 F 的 [[Construct]] 内部方法时,将执行以下步骤:
- 设 target 为 F.[[BoundTargetFunction]]。
- 断言:IsConstructor(target) 为 true。
- 设 boundArgs 为 F.[[BoundArguments]]。
- 设 args 是一个新列表,其中包含与列表 boundArgs 相同的值(顺序相同),后跟与列表参数相同的值,以相同的顺序包含与列表 argumentsList 相同的值。
- 如果 SameValue(F, newTarget) 为 true,则将 newTarget 设置为 target。
- 返回?构造(target, args, newTarget)。
这意味着绑定构造函数将使用绑定参数 () 和传入参数 () “构造”原始函数 (),但它完全忽略了绑定值(即 )。F.[[BoundTargetFunction]]
F.[[BoundArguments]]
argumentsList
this
F.[[BoundThis]]
但是,除了传递给 bind() 的值之外,还有其他类型的函数被馈送了其他东西吗?
是的,箭头功能。箭头函数没有自己的绑定(改用最接近的提供环境中的值),因此绑定箭头函数也会忽略绑定值。this
this
this
评论
bind()
bind()
bind()
new
class
.bind
Reflect
bind
.call()
评论
new class1(1)
this
this
new (class1.bind(null))(1)
null
.bind(null)
bind()
new
new
bind()