【问题标题】:How does ecmascript-6 Function.prototype.bind() handle a class constructor?ecmascript-6 Function.prototype.bind() 如何处理类构造函数?
【发布时间】:2021-07-28 02:51:39
【问题描述】:

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

显然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() 正在为构造函数做一些特别的事情,这对我来说是不可理解的。因此,如果其他一切都失败了,我至少想要一个解释。

【问题讨论】:

  • new class1(1) 中,this 对决定调用哪个构造函数没有影响,因为this 是正在创建的东西。在决定构造实例时将运行什么时,是什么导致您期望new (class1.bind(null))(1) 使用null 而不是绑定函数本身(从.bind(null) 返回的对象,而不是调用它时传递的参数)?
  • 我真的不期待什么,我只是想知道它是如何工作的。正常绑定函数的行为方式,使用bind() 设置的值。通过new 调用时,情况有所不同。我看到关于“this”的 TB 级的光泽,而不是关于它在 new 调用上下文中的值的字眼。我什至没有看到任何关于它的规范或官方理由。而且我发现bind() 的接口有点混乱(构造函数会忽略“this”值)。我已经修改了我的示例以使其更清晰。

标签: ecmascript-6 this


【解决方案1】:

这与具体的类没有任何关系,而是与 .bind 的工作方式有关。

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

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

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

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

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

9.4.1.2 [[Construct]] (argumentsList, newTarget)

当绑定函数外来对象的 [[Construct]] 内部方法时,使用绑定函数创建的 F 会使用参数列表 argumentsListnewTarget,采取以下步骤:

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

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


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

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

【讨论】:

  • 非常感谢,先生。我不想过分欢迎,但如果我编辑我的帖子以要求澄清,你是否可以详细说明,或者我应该发布新问题?
  • 取决于它的扩展:D 你也可以用你的问题评论我的回答。
  • 好吧,我非常想测试 bind() 的天真重新实现,看看内在的 bind() 做了什么,几个扩展运算符和对 apply() 的调用可以'达不到。我的意思是,只是在概念层面。我假设bind() 将执行非常有用的健全性检查,并且可能比手工替换运行得更快。这只是为了了解我的方位。有太多关于 JS 引擎应该如何在互联网上晃荡的童话故事,我觉得有必要深入了解这个故事。
  • @kuroineko:很多这些东西只是为了方便。您也可以自己实现newclass 的工作方式(最后是基本行为)。还要记住,.bind 在传播参数和休息参数或Reflect 之前就已经存在。现在可能更容易实现,但我认为没有一种简单的方法可以区分 constructor 调用和 “普通” 调用。
  • 还有其他类型的函数正在被其他东西提供吗? - 是的,箭头函数。” - 我不同意。 bind 不关心函数是否为箭头函数。它仍然将绑定的 thisValue 提供给调用。只是箭头函数忽略了thisArgument,就像它作为方法调用或使用.call()时忽略它一样。
猜你喜欢
  • 2015-06-02
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 2018-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-13
相关资源
最近更新 更多