ecmascript-6 Function.prototype.bind() 如何处理类构造函数? [英] How does ecmascript-6 Function.prototype.bind() handle a class constructor?

查看:24
本文介绍了ecmascript-6 Function.prototype.bind() 如何处理类构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I totally missed the ES6 revolution and I'm returning to JavaScript after 7 years, to find a host of very strange things happening.

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

One in particular is the way Function.prototype.bind() handles class constructors.

考虑一下:

// 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的部分应用,可以生成具有固定值的第一个参数的实例.

Apparently class2 and class3 are identified as constructors, and class3 is a partial application of class1 that can generate instances with a fixed value of the first parameter.

另一方面,虽然它们仍然可以充当(穷人的)构造函数,但 ES5 风格的函数确实提供了 bind()this 的值>,当它们作用于不幸的 passer_by 而不是像未绑定的 pseudoclass1 那样破坏全局变量时可以看出.

On the other hand, though they can still act as (poor man's) constructors, the ES5-style functions are indeed served the value of this set by bind(), as can be seen when they act on the hapless passer_by instead of clobbering global variables as does the unbound pseudoclass1.

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

Obviously, all these constructors somehow access a value of this that allows them to construct an object. And yet their this are supposedly bound to another object.

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

So I guess there must be some mechanism at work to feed the proper this to a constructor instead of whatever parameter was passed to bind().

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

Now my problem is, I can find bits of lore about it here and there, even some code apparently from some version of Chrome's V8 (where the function bind() itself seems to do something special about constructors), or a discussion about a cryptic FNop function inserted in the prototype chain, and, if I may add, the occasional piece of cargo cult bu[beep]it.

但是我找不到关于这里实际发生的事情的解释,以及为什么实施这种机制的基本原理(我的意思是,使用新的扩展运算符和解构等等,不是吗?可以产生相同的结果(对构造函数应用一些参数),而不必对 bind()?除了传递给 bind() ?)

But what I can't find is an explanation about what is actually going on here, a rationale as to why such a mechanism has been implemented (I mean, with the new spread operator and destructuring and whatnot, wouldn't it be possible to produce the same result (applying some arguments to a constructor) without having to put a moderately documented hack into bind()?), and its scope (it works for constructors, but are there other sorts of functions that are being fed something else than the value passed to 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...

I tried to read both the 2015 and 2022 ECMA 262 specifications, but had to stop when my brains started leaking out of my ears. I traced back the call stack as:
19.2.3.2
9.4.1.3
9.4.1.2
7.3.13 where something is said about constructors, in a way: "If newTarget is not passed, this operation is equivalent to: new F(...argumentsList)". Aha. So this pseudo-recursive call should allow to emulate a new somehow... Erf...

如果一些善良而精明的人能让我更好地了解正在发生的事情,向我展示 ECMA 规范的哪些部分处理这种机制,或者更普遍地指出我正确的方向,我将不胜感激方向.

I'd be grateful if some kind and savvy soul could give me a better idea of what is going on, show me which part(s) of the ECMA specs deal with this mechanism, or more generally point me in the right direction.

说实话,我已经厌倦了用头撞墙.这个 Chrome 代码位似乎表明 bind() 正在为构造函数做一些特别的事情对我来说简直无法理解.所以我至少想要一个关于它的解释,如果其他一切都失败了.

I'm tired of banging my head against a wall, truth be told. This bit of Chrome code that seems to indicate bind() is doing something special for constructors is just incomprehensible for me. So I would at least like an explanation about it, if everything else fails.

推荐答案

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

This doesn't have anything to do with classes specifically but with how .bind works.

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

You have been on the right track. The most relevan section here is 9.4.1.2.

总结一下:ECMAScript 区分了两种类型的函数:可调用 函数和可构造 函数.函数表达式/声明两者都是,而例如class 构造函数只能构造,箭头函数只能调用.

Just as a recap: ECMAScript distinguishes between two types of functions: callable functions and constructable functions. Function expressions/declarations are both, whereas e.g. class constructors are only constructable and arrow functions are only callable.

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

In the specification this is represented by function's internal [[Call]] and [[Construct]] methods.

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

new will trigger the invocation of the internal [[Construct]] method.

.bind 将返回一个新的函数对象,其中 [[Call]][[Construct]] 的实现不同.那么绑定"是什么?;[[Construct]] 的版本是什么样的?

.bind will return a new function object with different implementations for [[Call]] and [[Construct]]. So what does the "bound" version of [[Construct]] look like?

9.4.1.2 [[Construct]] ( argumentsList, newTarget )

9.4.1.2 [[Construct]] ( argumentsList, newTarget )

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

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. target 为 F.[[BoundTargetFunction]].
  2. 断言:IsConstructor(target) 为真.
  3. boundArgs 为 F.[[BoundArguments]].
  4. args 成为一个新列表,其中包含与列表 boundArgs 相同的值,顺序相同,后跟与列表相同的值 argumentsList 以相同的顺序.
  5. 如果 SameValue(F, newTarget) 为真,则将 newTarget 设置为 target.
  6. 返回?构造(target, args, newTarget).
  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.[[BoundTargetFunction]]) 与绑定参数 (F.[[BoundArguments]]) 和传入的参数 (argumentsList),但它完全忽略绑定的 this 值(即 F.[[BoundThis]]).

What this means is that the bound constructor will "construct" the original function (F.[[BoundTargetFunction]]) with the bound arguments (F.[[BoundArguments]]) and the passed in arguments (argumentsList), but it completely ignores the bound this value (which would be F.[[BoundThis]]).

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

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

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

Yes, arrow functions. Arrow functions do not have their own this binding (the value from the closest this providing environment is used instead), so bound arrow functions also ignore the bound this value.

这篇关于ecmascript-6 Function.prototype.bind() 如何处理类构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆