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

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

提问人:kuroi neko 提问时间:7/28/2021 最后编辑:kuroi neko 更新时间:7/28/2021 访问量:163

问:

我完全错过了 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}

显然,并被标识为构造函数,并且是可以生成具有第一个参数的固定值的实例的部分应用。class2class3class3class1

另一方面,尽管它们仍然可以充当(穷人的)构造函数,但 ES5 风格的函数确实提供了 set by 的值,当它们作用于倒霉的全局变量而不是像未绑定的全局变量那样笨拙时,可以看出这一点。thisbind()passer_bypseudoclass1

显然,所有这些构造函数都以某种方式访问了一个值,该值允许它们构造一个对象。然而,它们应该被绑定到另一个物体上。thisthis

所以我想一定有一些机制在起作用,将适当的内容提供给构造函数,而不是传递给的任何参数。thisbind()

现在我的问题是,我可以在这里和那里找到一些关于它的传说,甚至是一些显然来自某个版本的 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()

ecmascript-6 这个

评论

0赞 loganfsmyth 7/28/2021
在 中,对决定调用哪个构造函数没有影响,因为是正在创建的事物。是什么导致您在决定构造实例时要运行的内容时期望使用绑定函数本身(从 返回的对象,而不是调用它时传递的参数)?new class1(1)thisthisnew (class1.bind(null))(1)null.bind(null)
0赞 kuroi neko 7/28/2021
我真的不期望任何事情,我只是想知道它是如何工作的。正常绑定函数的行为方式,使用 set by 设置的值。当通过 调用时,情况就不同了。我看到关于“this”的数 TB 光泽,而不是关于它在调用上下文中的价值的字眼。我什至没有看到一些关于它的规范或官方理由。而且我发现接口有点令人困惑(构造函数忽略了“this”值)。我修改了我的示例,试图更清楚地说明这一点。bind()newnewbind()

答:

1赞 Felix Kling 7/28/2021 #1

这与类无关,而是与工作方式有关。.bind

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

回顾一下:ECMAScript 区分了两种类型的函数:可调用函数和可构造函数。函数表达式/声明两者兼而有之,而例如 构造函数只能构造,箭头函数只能调用。class

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

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

.bind将返回一个新的函数对象,该对象具有不同的实现 和 。那么[[Construct]]的“绑定”版本是什么样子的呢?[[Call]][[Construct]]

9.4.1.2 [[构造]] ( argumentsList, newTarget )

当使用参数 argumentsListnewTarget 列表调用绑定函数外来对象 F 的绑定函数外来对象 F 的 [[Construct]] 内部方法时,将执行以下步骤:

  1. target 为 F.[[BoundTargetFunction]]。
  2. 断言:IsConstructor(target) 为 true。
  3. boundArgs 为 F.[[BoundArguments]]。
  4. args 是一个新列表,其中包含与列表 boundArgs 相同的值(顺序相同),后跟与列表参数相同的值,以相同的顺序包含与列表 argumentsList 相同的值。
  5. 如果 SameValue(FnewTarget) 为 true,则将 newTarget 设置为 target
  6. 返回?构造(targetargsnewTarget)。

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


但是,除了传递给 bind() 的值之外,还有其他类型的函数被馈送了其他东西吗?

是的,箭头功能。箭头函数没有自己的绑定(改用最接近的提供环境中的值),因此绑定箭头函数也会忽略绑定值。thisthisthis

评论

0赞 kuroi neko 7/28/2021
非常感谢你,先生。我不想逾期停留我的欢迎,但是如果我编辑我的帖子以要求一些澄清,或者我应该发布新问题,您能详细说明一些吗?
0赞 Felix Kling 7/28/2021
取决于它的扩展:D你也可以用你的问题评论我的回答。
0赞 kuroi neko 7/28/2021
好吧,我非常想测试一个幼稚的重新实现,只是为了看看内部函数做了什么,而一些扩展运算符和调用 apply() 无法实现。我的意思是,只是在概念层面上。我认为将执行非常有用的健全性检查,并且可能比手工制作的替代品运行得更快。只是为了了解我的方位。关于JS引擎应该如何工作的童话故事太多了,我觉得有必要深入了解这个故事。bind()bind()bind()
0赞 Felix Kling 7/28/2021
@kuroineko:很多这些东西只是为了方便。你也可以实现你自己或工作方式(最后是基本行为)。还要记住,在传播参数和 rest 参数或 .现在可能更容易实现,但我认为没有一种简单的方法来区分构造函数调用和“正常”调用。newclass.bindReflect
1赞 Bergi 7/28/2021
"有没有其他类型的函数被喂给其他东西? - 是的,箭头功能。- 我不同意那里。 不在乎一个函数是否是箭头函数。它仍然会将绑定的 thisValue 馈送到调用中。只是箭头函数忽略了thisArgument,就像当它作为方法调用或使用时忽略它一样。bind.call()