我在Firefox-3.5.7/Firebug-1.5.3和Firefox-3.6.16/Firebug-1.6.2中观察到这一点
当我启动Firebug时:
var x = new Array(3)
console.log (x)
// [undefined, undefined, undefined]
Var y = [undefined, undefined, undefined]
console.log (y)
// [undefined, undefined, undefined]
Console.log (x.constructor == y.constructor) // true
console.log (
X.map(函数(){返回0;})
)
// [undefined, undefined, undefined]
console.log (
Y.map(函数(){返回0;})
)
// [0,0,0]
这是怎么回事?这是一个错误,还是我误解了如何使用新数组(3)?
下面是一个简单的实用方法:
简单地图
函数mapFor(toExclusive, callback) {
Callback = Callback || function(){};
Var arr = [];
For (var I = 0;i < toExclusive;我+ +){
arr.push(回调(i));
}
返回arr;
};
var arr = mapFor(3,函数(i){返回i;});
console.log (arr);// [0,1,2]
arr = mapFor(3);
console.log (arr);// [undefined, undefined, undefined]
完整的示例
下面是一个更完整的例子(带有完整性检查),它也允许指定一个可选的起始索引:
function mapFor() {
var from, toExclusive, callback;
if (arguments.length == 3) {
from = arguments[0];
toExclusive = arguments[1];
callback = arguments[2];
} else if (arguments.length == 2) {
if (typeof arguments[1] === 'function') {
from = 0;
toExclusive = arguments[0];
callback = arguments[1];
} else {
from = arguments[0];
toExclusive = arguments[1];
}
} else if (arguments.length == 1) {
from = 0;
toExclusive = arguments[0];
}
callback = callback || function () {};
var arr = [];
for (; from < toExclusive; from++) {
arr.push(callback(from));
}
return arr;
}
var arr = mapFor(1, 3, function (i) { return i; });
console.log(arr); // [1, 2]
arr = mapFor(1, 3);
console.log(arr); // [undefined, undefined]
arr = mapFor(3);
console.log(arr); // [undefined, undefined, undefined]
倒计时
操作传递给回调函数的索引允许向后计数:
var count = 3;
var arr = arrayUtil.mapFor(count, function (i) {
return count - 1 - i;
});
// arr = [2, 1, 0]
既然问题是为什么,这与JS的设计方式有关。
我认为有两个主要原因可以解释这种行为:
性能:给定x = 10000和新Array(x),构造函数最好避免从0到10000循环填充未定义值的数组。
隐式"undefined":给定a = [undefined, undefined]和b = new Array(2), a[1]和b[1]都将返回undefined,但a[8]和b[8]即使超出范围也将返回undefined。
最终,符号empty x3是避免设置和显示一长串未定义值的快捷方式,因为它们没有显式声明。
注意:给定数组a =[0]和[9]= 9,console.log(a)将返回(10)[0,empty x 8,9],通过返回显式声明的两个值之间的差值自动填充空白。
在ECMAScript第6版规范。
new Array(3)只定义属性长度,不定义索引属性如{length: 3}。参见https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-len步骤9。
[undefined, undefined, undefined]将定义索引属性和长度属性,如{0:undefined, 1: undefined, 2: undefined, length: 3}。参见https://www.ecma-international.org/ecma-262/6.0/index.html#sec-runtime-semantics-arrayaccumulation ElementList步骤5。
方法map, every, some, forEach, slice, reduce, reduceRight, Array的filter将通过HasProperty内部方法检查索引属性,因此new Array(3)。Map (v => 1)将不会调用回调函数。
更多详细信息,请参见https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.map
如何修复?
let a = new Array(3);
a.join('.').split('.').map(v => 1);
let a = new Array(3);
a.fill(1);
let a = new Array(3);
a.fill(undefined).map(v => 1);
let a = new Array(3);
[...a].map(v => 1);
数组是不同的。不同之处在于,new Array(3)创建了一个长度为3但没有属性的数组,而[undefined, undefined, undefined]创建了一个长度为3的数组和三个名为“0”、“1”和“2”的属性,每个属性的值都为undefined。你可以用in操作符看出区别:
"0" in new Array(3); // false
"0" in [undefined, undefined, undefined]; // true
这源于一个有点令人困惑的事实,即如果您试图在JavaScript中获取任何原生对象的不存在属性的值,它将返回undefined(而不是抛出错误,当您试图引用不存在的变量时就会发生这种情况),这与之前显式地将属性设置为undefined时得到的结果相同。