在阅读VueJS教程时有这么段示例代码:
function (createElement) {
return createElement('div',
Array.apply(null, { length: 20 }).map(function () {
return createElement('p', 'hi')
})
)
}
Array.apply(null, { length: 20 })让人费解。常规的apply和call均是在调用方法时用于绑定方法内部执行上下文使用; 创建一个长度为20的数组可以使用Array(20),为何代码会如此“复杂”?
Array()和new Array()的区别
js中初始化数组的3种方式,[]直接创建是我们常用且被推荐使用的方式,而new只是一个语法糖。new Array()创建了一个对象,新建的对象arr.__proto__ 指向 Array.prototype。这是标准的一个由Class到实例的创建步骤。体现了JS在面向对象方面向主流语言的过渡。
When Array is called as a function rather than as a constructor, it creates and initialises a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments.
当数组作为函数调用而不是构造函数调用时,它会创建并初始化一个新的数组对象。因此当Array(...)和new Array(...)接收同样的参数时,它们是相同的。所以Array()和new Array()完全一致。
Array(1)和Array(1, 2)的区别
A JavaScript array is initialized with the given elements, except in the case where a single argument is passed to the
Arrayconstructor and that argument is a number (see the arrayLength parameter below). Note that this special case only applies to JavaScript arrays created with theArrayconstructor, not array literals created with the bracket syntax.
使用给定元素初始化 JavaScript 数组,除非将单个参数传递给 Array 构造函数并且该参数是数字(请参阅下面的 arrayLength 参数)。 请注意,这种特殊情况仅适用于使用 Array 构造函数创建的 JavaScript 数组,而不适用于使用括号语法创建的数组文字。
If the only argument passed to the
Arrayconstructor is an integer between 0 and 2^32 - 1 (inclusive), this returns a new JavaScript array with itslengthproperty set to that number (Note: this implies an array ofarrayLengthempty slots, not slots with actualundefinedvalues — see sparse arrays).
如果传递给 Array 构造函数的唯一参数是 0 到 2^32 - 1(含)之间的整数,则返回一个新的 JavaScript 数组,其长度属性设置为该数字(注意:这意味着 arrayLength 空槽数组, 不是具有实际未定义值的插槽 - 请参阅稀疏数组)
console.log(Array(10));
// [empty × 10]
console.log(Array(10, 2));
// [10, 2]
Array为什么能使用apply(它的原型链上并不存在apply)?
Array的创建等于调用其实现上构造函数,而apply以及call均为Function原型链上的方法,自然构造函数也能调用使用apply以及call,故Array.apply的调用方式是可行的。
apply函数
根据MDN的描述{ length: 20 }就是一个类数组对象。
Starting with ECMAScript 5 these arguments can be a generic array-like object instead of an array. See below for browser compatibility information.
从 ECMAScript 5 开始,这些参数可以是一个通用的类数组对象,而不是一个数组。
Since ECMAScript 5th Edition, you can also use any kind of object which is array-like. In practice, this means it's going to have a length property, and integer ("index") properties in the range (0..length - 1). For example, you could use a NodeList, or a custom object like { 'length': 2, '0': 'eat', '1': 'bananas' }.
从 ECMAScript 第 5 版开始,您还可以使用任何类型的类似数组的对象。 在实践中,这意味着它将有一个长度属性,以及范围 (0..length - 1) 内的整数(“索引”)属性。 例如,您可以使用 NodeList 或自定义对象,例如 { 'length': 2, '0': 'eat', '1': 'bananas' }。
map函数执行过程中的点
mapcalls a providedcallbackFnfunction once for each element in an array, in order, and constructs a new array from the results.callbackFnis invoked only for indexes of the array which have assigned values (includingundefined).
map为数组中的每个元素按顺序调用一次提供的callbackFn函数,并根据结果构造一个新数组。仅对分配了值(包括)的数组索引调用。从未设置过的索引以及被delete删除的索引值均为未分配的索引。
再看表达式Array.apply(null, { length: 20})
为了简化代码量,我们将length调整成2做一次拆解
// 1 熟悉一点: {length: 2}作为Array.apply第二个参数等同于[undefined, undefined]作为Array.apply第二个参数
Array.apply(null, [undefined, undefined]);
// 2 再熟悉一点:apply方法的执行结果
Array(undefined, undefined);
// 3 再再熟悉一点:Array方法直接调用和new方式调用等价
new Array(undefined, undefined);
而Array(20)会生成一个长度为20的稀疏数组,且数组的每一项均未被分配值,而map不会对未分配值的索引调用callbackFn函数。故使用Array(20).map(item => {})并不会如我们预期调用20次。