伪数组

输出以下代码的执行结果,并加以解释。

1
2
3
4
5
6
7
8
9
10
var obj = {
'2': 3,
'3': 4,
'length': 2,
'splice': Array.prototype.splice,
'push': Array.prototype.push,
};
obj.push(1);
obj.push(2);
console.log(obj);

一、数组 VS 类数组(Array-like)对象

在 JavaScript 中,数组就是由 Array 构造出来的对象。区分方式:判断其原型链是否指向 Array.prototype(push、pop、shift和join)。

二、Array.prototype.push

MDN解释为:
push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。同时 push 是特意设计为通用的。

push() 根据 length 属性决定从哪里开始插入给定的值。如果 length 不能被转成一个数值,则插入的元素索引为 0,包括 length 不存在时。当 length 不存在时,将会创建它。

三、问题回归

当一个对象调用 push() 时,对象的 length 属性和插入元素会发生变化。

因此 obj 返回结果为:

1
2
3
4
5
6
7
Object {
'2': 1,
'3': 2,
'length': 4,
'splice': Array.prototype.splice,
'push': Array.prototype.push,
}

但是当一个对象存在 lengthsplice 时,输出会被转换为一个伪数组。因此最终结果会变为:

1
Object(4) [empty × 2, 1, 2, splice: ƒ, push: ƒ]

变形题:

1
2
3
4
5
6
var obj = {
'splice': Array.prototype.splice,
'push': Array.prototype.push,
};
obj.push(1);
console.log(obj); // Object [1, splice: ƒ, push: ƒ] obj.length -> 1

1
2
3
4
5
6
7
var obj = {
'length': 'a',
'splice': Array.prototype.splice,
'push': Array.prototype.push,
};
obj.push(1);
console.log(obj); // Object [1, splice: ƒ, push: ƒ] obj.length -> 1

四、伪数组打印

当一个对象的 length 属性为数字,同时splice属性为函数时, 对象的函数输出结果就会变成 伪数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obj1 = {
length: 1,
splice: function () {},
}; // Object [empty, splice: ƒ]

var obj2 = {
length: '1',
splice: function () {},
}; // {length: "1", splice: ƒ}

var obj3 = {
length: 1,
splice: {},
}; // {length: 1, splice: {…}}

在 DevTools,就是通过这些来进行判断对象是否为类数组。

1
2
3
4
5
6
7
8
9
10
11
12
 function isArrayLike(obj) {
if (!obj || typeof obj !== 'object')
return false;
try {
if (typeof obj.splice === 'function') {
const len = obj.length;
return typeof len === 'number' && (len >>> 0 === len && (len > 0 || 1 / len > 0));
}
} catch (e) {
}
return false;
}

https://github.com/ChromeDevTools/devtools-frontend/blob/master/front_end/event_listeners/EventListenersUtils.js#L330

五、相关讨论

https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/76

六、对象转数组

  • Array.from() 方法从一个类似数组或可迭代对象中创建一个新的数组实例。
  • Array.prottype.slice.call(arguments) 生成新数组
-------------本文结束感谢您的阅读-------------
0%