在 JavaScript 中创建范围 - 奇怪的语法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18947892/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 13:47:37  来源:igfitidea点击:

Creating range in JavaScript - strange syntax

javascriptecmascript-5

提问by Benjamin Gruenbaum

I've run into the following code in the es-discuss mailing list:

我在 es-discuss 邮件列表中遇到了以下代码:

Array.apply(null, { length: 5 }).map(Number.call, Number);

This produces

这产生

[0, 1, 2, 3, 4]

Why is this the result of the code? What's happening here?

为什么这是代码的结果?这里发生了什么事?

回答by Zirak

Understanding this "hack" requires understanding several things:

理解这个“hack”需要理解几件事:

  1. Why we don't just do Array(5).map(...)
  2. How Function.prototype.applyhandles arguments
  3. How Arrayhandles multiple arguments
  4. How the Numberfunction handles arguments
  5. What Function.prototype.calldoes
  1. 为什么我们不只是做 Array(5).map(...)
  2. 如何Function.prototype.apply处理参数
  3. 如何Array处理多个参数
  4. Number函数如何处理参数
  5. 有什么Function.prototype.call作用

They're rather advanced topics in javascript, so this will be more-than-rather long. We'll start from the top. Buckle up!

它们是 javascript 中相当高级的主题,所以这将是相当长的。我们将从顶部开始。系好安全带!

1. Why not just Array(5).map?

1. 为什么不只是Array(5).map

What's an array, really? A regular object, containing integer keys, which map to values. It has other special features, for instance the magical lengthvariable, but at it's core, it's a regular key => valuemap, just like any other object. Let's play with arrays a little, shall we?

什么是数组,真的吗?一个常规对象,包含映射到值的整数键。它还有其他特殊功能,例如魔法length变量,但它的核心是普通key => value地图,就像任何其他对象一样。让我们来玩玩数组,好吗?

var arr = ['a', 'b', 'c'];
arr.hasOwnProperty(0); //true
arr[0]; //'a'
Object.keys(arr); //['0', '1', '2']
arr.length; //3, implies arr[3] === undefined

//we expand the array by 1 item
arr.length = 4;
arr[3]; //undefined
arr.hasOwnProperty(3); //false
Object.keys(arr); //['0', '1', '2']

We get to the inherent difference between the number of items in the array, arr.length, and the number of key=>valuemappings the array has, which can be different than arr.length.

我们得到了数组中的项arr.lengthkey=>value与数组具有的映射数之间的内在差异,这可能不同于arr.length

Expanding the array via arr.lengthdoes notcreate any new key=>valuemappings, so it's not that the array has undefined values, it does not have these keys. And what happens when you try to access a non-existent property? You get undefined.

扩展数组 viaarr.length不会创建任何新key=>value映射,因此并不是数组具有未定义的值,而是没有这些键。当您尝试访问不存在的属性时会发生什么?你得到undefined

Now we can lift our heads a little, and see why functions like arr.mapdon't walk over these properties. If arr[3]was merely undefined, and the key existed, all these array functions would just go over it like any other value:

现在我们可以稍微抬起头,看看为什么像arr.map这样的函数不遍历这些属性。如果arr[3]只是未定义,并且键存在,所有这些数组函数将像任何其他值一样遍历它:

//just to remind you
arr; //['a', 'b', 'c', undefined];
arr.length; //4
arr[4] = 'e';

arr; //['a', 'b', 'c', undefined, 'e'];
arr.length; //5
Object.keys(arr); //['0', '1', '2', '4']

arr.map(function (item) { return item.toUpperCase() });
//["A", "B", "C", undefined, "E"]

I intentionally used a method call to further prove the point that the key itself was never there: Calling undefined.toUpperCasewould have raised an error, but it didn't. To prove that:

我特意使用了一个方法调用来进一步证明密钥本身并不存在这一点:调用undefined.toUpperCase会引发错误,但它没有。为了证明

arr[5] = undefined;
arr; //["a", "b", "c", undefined, "e", undefined]
arr.hasOwnProperty(5); //true
arr.map(function (item) { return item.toUpperCase() });
//TypeError: Cannot call method 'toUpperCase' of undefined

And now we get to my point: How Array(N)does things. Section 15.4.2.2describes the process. There's a bunch of mumbo jumbo we don't care about, but if you manage to read between the lines (or you can just trust me on this one, but don't), it basically boils down to this:

现在我们得到我的观点:Array(N)事情如何。第 15.4.2.2 节描述了该过程。有一堆我们不关心的笨蛋,但如果你设法读懂字里行间(或者你可以相信我,但不要),它基本上可以归结为:

function Array(len) {
    var ret = [];
    ret.length = len;
    return ret;
}

(operates under the assumption (which is checked in the actual spec) that lenis a valid uint32, and not just any number of value)

(在假设(在实际规范中进行了检查)len是有效的 uint32,而不仅仅是任意数量的值的假设下运行)

So now you can see why doing Array(5).map(...)wouldn't work - we don't define lenitems on the array, we don't create the key => valuemappings, we simply alter the lengthproperty.

所以现在你可以明白为什么这样做Array(5).map(...)行不通了——我们没有len在数组上定义项目,我们没有创建key => value映射,我们只是改变了length属性。

Now that we have that out of the way, let's look at the second magical thing:

现在我们已经解决了这个问题,让我们看看第二件神奇的事情:

2. How Function.prototype.applyworks

2.Function.prototype.apply工作原理

What applydoes is basically take an array, and unroll it as a function call's arguments. That means that the following are pretty much the same:

什么apply确实基本上是采取一个数组,并展开其作为函数调用的参数。这意味着以下内容几乎相同:

function foo (a, b, c) {
    return a + b + c;
}
foo(0, 1, 2); //3
foo.apply(null, [0, 1, 2]); //3

Now, we can ease the process of seeing how applyworks by simply logging the argumentsspecial variable:

现在,我们可以apply通过简单地记录arguments特殊变量来简化查看工作原理的过程:

function log () {
    console.log(arguments);
}

log.apply(null, ['mary', 'had', 'a', 'little', 'lamb']);
 //["mary", "had", "a", "little", "lamb"]

//arguments is a pseudo-array itself, so we can use it as well
(function () {
    log.apply(null, arguments);
})('mary', 'had', 'a', 'little', 'lamb');
 //["mary", "had", "a", "little", "lamb"]

//a NodeList, like the one returned from DOM methods, is also a pseudo-array
log.apply(null, document.getElementsByTagName('script'));
 //[script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script, script]

//carefully look at the following two
log.apply(null, Array(5));
//[undefined, undefined, undefined, undefined, undefined]
//note that the above are not undefined keys - but the value undefined itself!

log.apply(null, {length : 5});
//[undefined, undefined, undefined, undefined, undefined]

It's easy to prove my claim in the second-to-last example:

在倒数第二个例子中很容易证明我的主张:

function ahaExclamationMark () {
    console.log(arguments.length);
    console.log(arguments.hasOwnProperty(0));
}

ahaExclamationMark.apply(null, Array(2)); //2, true

(yes, pun intended). The key => valuemapping may not have existed in the array we passed over to apply, but it certainly exists in the argumentsvariable. It's the same reason the last example works: The keys do not exist on the object we pass, but they do exist in arguments.

(是的,双关语)。该key => value映射可能不存在于我们传递给 的数组中apply,但它肯定存在于arguments变量中。这与上一个示例工作的原因相同:我们传递的对象上不存在键,但它们确实存在于arguments.

Why is that? Let's look at Section 15.3.4.3, where Function.prototype.applyis defined. Mostly things we don't care about, but here's the interesting portion:

这是为什么?让我们看看第 15.3.4.3 节,其中Function.prototype.apply定义了。大多数我们不关心的事情,但这里是有趣的部分:

  1. Let len be the result of calling the [[Get]] internal method of argArray with argument "length".
  1. 令 len 为参数“length”调用 argArray 的 [[Get]] 内部方法的结果。

Which basically means: argArray.length. The spec then proceeds to do a simple forloop over lengthitems, making a listof corresponding values (listis some internal voodoo, but it's basically an array). In terms of very, very loose code:

这基本上意味着:argArray.length。然后规范继续对项目做一个简单的for循环length,生成一个list对应的值(list是一些内部的巫毒教,但它基本上是一个数组)。就非常非常松散的代码而言:

Function.prototype.apply = function (thisArg, argArray) {
    var len = argArray.length,
        argList = [];

    for (var i = 0; i < len; i += 1) {
        argList[i] = argArray[i];
    }

    //yeah...
    superMagicalFunctionInvocation(this, thisArg, argList);
};

So all we need to mimic an argArrayin this case is an object with a lengthproperty. And now we can see why the values are undefined, but the keys aren't, on arguments: We create the key=>valuemappings.

因此,argArray在这种情况下,我们只需要模拟一个具有length属性的对象。现在我们可以看到为什么值是未定义的,但键不是,在arguments:我们创建key=>value映射。

Phew, so this might not have been shorter than the previous part. But there'll be cake when we finish, so be patient! However, after the following section (which'll be short, I promise) we can begin dissecting the expression. In case you forgot, the question was how does the following work:

呼,所以这可能不会比上一部分短。但是我们完成后会有蛋糕,所以请耐心等待!但是,在接下来的部分(我保证会很短)之后,我们可以开始剖析表达式。如果您忘记了,问题是以下如何工作:

Array.apply(null, { length: 5 }).map(Number.call, Number);

3. How Arrayhandles multiple arguments

3. 如何Array处理多个参数

So! We saw what happens when you pass a lengthargument to Array, but in the expression, we pass several things as arguments (an array of 5 undefined, to be exact). Section 15.4.2.1tells us what to do. The last paragraph is all that matters to us, and it's worded reallyoddly, but it kind of boils down to:

所以!我们看到了当您将length参数传递给 时会发生什么Array,但是在表达式中,我们将一些东西作为参数传递(undefined准确地说是 5 的数组)。15.4.2.1 节告诉我们该怎么做。最后一段对我们来说很重要,它的措辞非常奇怪,但归结为:

function Array () {
    var ret = [];
    ret.length = arguments.length;

    for (var i = 0; i < arguments.length; i += 1) {
        ret[i] = arguments[i];
    }

    return ret;
}

Array(0, 1, 2); //[0, 1, 2]
Array.apply(null, [0, 1, 2]); //[0, 1, 2]
Array.apply(null, Array(2)); //[undefined, undefined]
Array.apply(null, {length:2}); //[undefined, undefined]

Tada! We get an array of several undefined values, and we return an array of these undefined values.

多田!我们得到一个包含多个未定义值的数组,然后返回一个包含这些未定义值的数组。

The first part of the expression

表达式的第一部分

Finally, we can decipher the following:

最后,我们可以破译以下内容:

Array.apply(null, { length: 5 })

We saw that it returns an array containing 5 undefined values, with keys all in existence.

我们看到它返回一个包含 5 个未定义值的数组,所有的键都存在。

Now, to the second part of the expression:

现在,到表达式的第二部分:

[undefined, undefined, undefined, undefined, undefined].map(Number.call, Number)

This will be the easier, non-convoluted part, as it doesn't so much rely on obscure hacks.

这将是更简单、不复杂的部分,因为它不太依赖于晦涩的黑客。

4. How Numbertreats input

4. 如何Number对待输入

Doing Number(something)(section 15.7.1) converts somethingto a number, and that is all. How it does that is a bit convoluted, especially in the cases of strings, but the operation is defined in section 9.3in case you're interested.

Number(something)第 15.7.1 节)会转换something为数字,仅此而已。它是如何做到的有点令人费解,尤其是在字符串的情况下,但如果您感兴趣,该操作在第 9.3 节中定义。

5. Games of Function.prototype.call

5. 游戏 Function.prototype.call

callis apply's brother, defined in section 15.3.4.4. Instead of taking an array of arguments, it just takes the arguments it received, and passes them forward.

callapply的兄弟,在第 15.3.4.4 节中定义。它不采用参数数组,而是采用接收到的参数,并将它们向前传递。

Things get interesting when you chain more than one calltogether, crank the weird up to 11:

当您将多个链接call在一起时,事情会变得有趣,将奇怪的事情增加到 11:

function log () {
    console.log(this, arguments);
}
log.call.call(log, {a:4}, {a:5});
//{a:4}, [{a:5}]
//^---^  ^-----^
// this   arguments

This is quite wtf worthy until you grasp what's going on. log.callis just a function, equivalent to any other function's callmethod, and as such, has a callmethod on itself as well:

在您了解正在发生的事情之前,这是非常值得的。log.call只是一个函数,相当于任何其他函数的call方法,因此,它call本身也有一个方法:

log.call === log.call.call; //true
log.call === Function.call; //true

And what does calldo? It accepts a thisArgand a bunch of arguments, and calls its parent function. We can define it via apply(again, very loose code, won't work):

有什么作用call?它接受一个thisArg和一堆参数,并调用它的父函数。我们可以通过apply(同样,非常松散的代码,行不通)来定义它:

Function.prototype.call = function (thisArg) {
    var args = arguments.slice(1); //I wish that'd work
    return this.apply(thisArg, args);
};

Let's track how this goes down:

让我们跟踪它是如何下降的:

log.call.call(log, {a:4}, {a:5});
  this = log.call
  thisArg = log
  args = [{a:4}, {a:5}]

  log.call.apply(log, [{a:4}, {a:5}])

    log.call({a:4}, {a:5})
      this = log
      thisArg = {a:4}
      args = [{a:5}]

      log.apply({a:4}, [{a:5}])

The later part, or the .mapof it all

后半部分,或.map全部

It's not over yet. Let's see what happens when you supply a function to most array methods:

还没结束。让我们看看当您为大多数数组方法提供函数时会发生什么:

function log () {
    console.log(this, arguments);
}

var arr = ['a', 'b', 'c'];
arr.forEach(log);
//window, ['a', 0, ['a', 'b', 'c']]
//window, ['b', 1, ['a', 'b', 'c']]
//window, ['c', 2, ['a', 'b', 'c']]
//^----^  ^-----------------------^
// this         arguments

If we don't provide a thisargument ourselves, it defaults to window. Take note of the order in which the arguments are provided to our callback, and let's weird it up all the way to 11 again:

如果我们自己不提供this参数,则默认为window. 记下参数提供给回调的顺序,让我们再次将其设置为 11:

arr.forEach(log.call, log);
//'a', [0, ['a', 'b', 'c']]
//'b', [1, ['a', 'b', 'c']]
//'b', [2, ['a', 'b', 'c']]
// ^    ^

Whoa whoa whoa...let's back up a bit. What's going on here? We can see in section 15.4.4.18, where forEachis defined, the following pretty much happens:

哇哇哇哇...让我们退后一点。这里发生了什么?我们可以在15.4.4.18 节中看到,在forEach定义的地方,几乎会发生以下情况:

var callback = log.call,
    thisArg = log;

for (var i = 0; i < arr.length; i += 1) {
    callback.call(thisArg, arr[i], i, arr);
}

So, we get this:

所以,我们得到了这个:

log.call.call(log, arr[i], i, arr);
//After one `.call`, it cascades to:
log.call(arr[i], i, arr);
//Further cascading to:
log(i, arr);

Now we can see how .map(Number.call, Number)works:

现在我们可以看到它是如何.map(Number.call, Number)工作的:

Number.call.call(Number, arr[i], i, arr);
Number.call(arr[i], i, arr);
Number(i, arr);

Which returns the transformation of i, the current index, to a number.

它返回i当前索引的转换为数字。

In conclusion,

综上所述,

The expression

表达方式

Array.apply(null, { length: 5 }).map(Number.call, Number);

Works in two parts:

分两部分工作:

var arr = Array.apply(null, { length: 5 }); //1
arr.map(Number.call, Number); //2

The first part creates an array of 5 undefined items. The second goes over that array and takes its indices, resulting in an array of element indices:

第一部分创建一个包含 5 个未定义项的数组。第二个遍历该数组并获取其索引,从而生成元素索引数组:

[0, 1, 2, 3, 4]

回答by Benjamin Gruenbaum

Disclaimer: This is a very formal description of the above code - this is how Iknow how to explain it. For a simpler answer - check Zirak's great answer above. This is a more in depth specification in your face and less "aha".

免责声明:这是对上述代码的非常正式的描述 - 这就是知道如何解释它的方式。要获得更简单的答案 - 请查看上面 Zirak 的精彩答案。这是您面对的更深入的规范,而不是“啊哈”。



Several things are happening here. Let's break it up a bit.

这里正在发生几件事情。让我们把它分解一下。

var arr = Array.apply(null, { length: 5 }); // Create an array of 5 `undefined` values

arr.map(Number.call, Number); // Calculate and return a number based on the index passed

In the first line, the array constructor is called as a functionwith Function.prototype.apply.

在第一行中,数组构造函数作为带有Function.prototype.apply.

  • The thisvalue is nullwhich does not matter for the Array constructor (thisis the same thisas in the context according to 15.3.4.3.2.a.
  • Then new Arrayis called being passed an object with a lengthproperty - that causes that object to be an array like for all it matters to .applybecause of the following clause in .apply:
    • Let len be the result of calling the [[Get]] internal method of argArray with argument "length".
  • As such, .applyis passing arguments from 0 to .length, since calling [[Get]]on { length: 5 }with the values 0 to 4 yields undefinedthe array constructor is called with five arguments whose value is undefined(getting an undeclared property of an object).
  • The array constructor is called with 0, 2 or more arguments. The length property of the newly constructed array is set to the number of arguments according to the specification and the values to the same values.
  • Thus var arr = Array.apply(null, { length: 5 });creates a list of five undefined values.
  • thisnull对于 Array 构造函数无关紧要(thisthis根据 15.3.4.3.2.a 的上下文中的相同。
  • 然后new Array被称为传递一个具有length属性的对象-.apply由于以下子句 in ,这导致该对象成为一个数组,就像所有重要的一样.apply
    • 令 len 为参数“length”调用 argArray 的 [[Get]] 内部方法的结果。
  • 因此,.apply被传递参数从0到.length,由于主叫[[Get]]{ length: 5 }具有值0〜4的产率undefined的阵列构造函数被调用与五个参数,其值是undefined(获得的对象的未声明属性)。
  • 使用 0、2 或更多参数调用数组构造函数。新构造的数组的长度属性根据规范设置为参数的数量,并将值设置为相同的值。
  • 因此var arr = Array.apply(null, { length: 5 });创建了一个包含五个未定义值的列表。

Note: Notice the difference here between Array.apply(0,{length: 5})and Array(5), the first creating five times the primitive value type undefinedand the latter creating an empty array of length 5. Specifically, because of .map's behavior (8.b)and specifically [[HasProperty].

注意:注意这里Array.apply(0,{length: 5})和之间的区别Array(5),第一个创建原始值类型的五倍,undefined后者创建一个长度为 5 的空数组。具体来说,因为.map的行为 (8.b)和具体[[HasProperty]

So the code above in a compliant specification is the same as:

因此,符合规范的上述代码与以下代码相同:

var arr = [undefined, undefined, undefined, undefined, undefined];
arr.map(Number.call, Number); // Calculate and return a number based on the index passed

Now off to the second part.

现在进入第二部分。

  • Array.prototype.mapcalls the callback function (in this case Number.call) on each element of the array and uses the specified thisvalue (in this case setting the thisvalue to `Number).
  • The second parameter of the callback in map (in this case Number.call) is the index, and the first is the this value.
  • This means that Numberis called with thisas undefined(the array value) and the index as the parameter. So it's basically the same as mapping each undefinedto its array index (since calling Numberperforms type conversion, in this case from number to number not changing the index).
  • Array.prototype.mapNumber.call在数组的每个元素上调用回调函数(在本例中为)并使用指定的this值(在本例中将this值设置为 `Number)。
  • map 中回调的第二个参数(在本例中Number.call)是索引,第一个是 this 值。
  • 这意味着Number使用thisas undefined(数组值)和索引作为参数调用。所以它与将每个映射undefined到其数组索引基本相同(因为调用Number执行类型转换,在这种情况下从数字到数字不改变索引)。

Thus, the code above takes the five undefined values and maps each to its index in the array.

因此,上面的代码采用五个未定义的值并将每个值映射到它在数组中的索引。

Which is why we get the result to our code.

这就是为什么我们将结果发送到我们的代码中。

回答by Tal Z

As you said, the first part:

正如你所说,第一部分:

var arr = Array.apply(null, { length: 5 }); 

creates an array of 5 undefinedvalues.

创建一个包含 5 个undefined值的数组。

The second part is calling the mapfunction of the array which takes 2 arguments and returns a new array of the same size.

第二部分是调用map数组的函数,它接受 2 个参数并返回一个相同大小的新数组。

The first argument which maptakes is actually a function to apply on each element in the array, it is expected to be a function which takes 3 arguments and returns a value. For example:

第一个参数map实际上是一个应用于数组中每个元素的函数,它应该是一个接受 3 个参数并返回一个值的函数。例如:

function foo(a,b,c){
    ...
    return ...
}

if we pass the function foo as the first argument it will be called for each element with

如果我们将函数 foo 作为第一个参数传递,它将为每个元素调用

  • a as the value of the current iterated element
  • b as the index of the current iterated element
  • c as the whole original array
  • a 作为当前迭代元素的值
  • b 作为当前迭代元素的索引
  • c 作为整个原始数组

The second argument which maptakes is being passed to the function which you pass as the first argument. But it would not be a, b, nor c in case of foo, it would be this.

需要的第二个参数map被传递给您作为第一个参数传递的函数。但它不会是 a、b 或 c,在 的情况下foo,它会是this

Two examples:

两个例子:

function bar(a,b,c){
    return this
}
var arr2 = [3,4,5]
var newArr2 = arr2.map(bar, 9);
//newArr2 is equal to [9,9,9]

function baz(a,b,c){
    return b
}
var newArr3 = arr2.map(baz,9);
//newArr3 is equal to [0,1,2]

and another one just to make it clearer:

另一个只是为了让它更清楚:

function qux(a,b,c){
    return a
}
var newArr4 = arr2.map(qux,9);
//newArr4 is equal to [3,4,5]

So what about Number.call ?

那么 Number.call 呢?

Number.callis a function that takes 2 arguments, and tries to parse the second argument to a number (I'm not sure what it does with the first argument).

Number.call是一个接受 2 个参数的函数,并尝试将第二个参数解析为一个数字(我不确定它对第一个参数的作用)。

Since the second argument that mapis passing is the index, the value that will be placed in the new array at that index is equal to the index. Just like the function bazin the example above. Number.callwill try to parse the index - it will naturally return the same value.

由于map传递的第二个参数是索引,因此将放置在该索引处的新数组中的值等于该索引。就像baz上面例子中的函数一样。Number.call将尝试解析索引 - 它自然会返回相同的值。

The second argument you passed to the mapfunction in your code doesn't actually have an effect on the result. Correct me if I'm wrong, please.

map在代码中传递给函数的第二个参数实际上对结果没有影响。如果我错了,请纠正我。

回答by shex

An array is simply an object comprising the 'length' field and some methods (e.g. push). So arr in var arr = { length: 5}is basically the same as an array where the fields 0..4 have the default value which is undefined (i.e. arr[0] === undefinedyields true).
As for the second part, map, as the name implies, maps from one array to a new one. It does so by traversing through the original array and invoking the mapping-function on each item.

数组只是一个包含“长度”字段和一些方法(例如推送)的对象。所以 arr invar arr = { length: 5}基本上与数组相同,其中字段 0..4 具有未定义的默认值(即arr[0] === undefined产生真)。
至于第二部分,map,顾名思义,就是从一个数组映射到一个新的数组。它通过遍历原始数组并在每个项目上调用映射函数来实现。

All that's left is to convince you that the result of mapping-function is the index. The trick is to use the method named 'call'(*) which invokes a function with the small exception that the first param is set to be the 'this' context, and the second becomes the first param (and so on). Coincidentally, when the mapping-function is invoked, the second param is the index.

剩下的就是让你相信 mapping-function 的结果是索引。诀窍是使用名为 'call'(*) 的方法调用一个函数,但有一个小例外,即第一个参数被设置为 'this' 上下文,第二个参数成为第一个参数(依此类推)。巧合的是,当调用映射函数时,第二个参数是索引。

Last but not least, the method which is invoked is the Number "Class", and as we know in JS, a "Class" is simply a function, and this one (Number) expects the first param to be the value.

最后但并非最不重要的是,调用的方法是数字“类”,正如我们在 JS 中所知,“类”只是一个函数,这个(数字)期望第一个参数是值。

(*) found in Function's prototype (and Number is a function).

(*) 在 Function 的原型中找到(而 Number 是一个函数)。

MASHAL

马沙尔