# ❓ 输出以下代码执行的结果并解释为什么
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)
2
3
4
5
6
7
8
9
10
首先看上去这肯定是对类数组的考题
# 数组对象
JavaScript 数组的有一些特性是其他对象所没有的:
当有新的元素添加到列表中的时候,自动更新
length
属性设置
length
为一个较小值的时候将会截断数组从
Array.prototype
中继承一些有用的方法其类属性为
Array
这些特性让 JavaScript
数组和常规对象有明显的区别。但是它们不是定义数组的本质特性。
# 类数组对象
把拥有一个数值 length
属性和对应非负整数属性的对象看作一种类型的数组
我们定义的 function
函数中 Arguments
对象就是一个类数组对象,同样在客户端 JavaScript 中,一些 DOM 的方法比如 document.getElementsByTagName()
也是返回的类数组对象
// 判断是否是类数组对象
const isArrayLike = obj =>
obj && // 非 null undefined
typeof obj === 'object' && // 是对象
isFinite(obj.length) && // 是有穷数
obj.length >= 0 && // 为非负数
obj.length === Math.floor(obj.length) && // 整数
obj.length < Math.pow(2, 32) // < 4294967296
2
3
4
5
6
7
8
同时 JavaScript 数组方法是特意定义为通用的,因此它们不仅应用在真正的数组而且在类数组对象上都能正确的工作。类数组对象没有继承自 Array.prototype
,不能直接直接调用数组的方法,但是也是可以通过 Function.call
方法调用。
那么我们改变一下题目
var obj2 = {
'2': 3,
'3': 4,
length: 2
}
Array.prototype.push.call(obj2, 1)
Array.prototype.push.call(obj2, 2)
// 这样的到的有效结果是一样的 {2: 1, 3: 2, length: 4}
// 通过数组的方法 // 1 2
Array.prototype.forEach.call(obj2, item => console.log(item))
// 其本质还是一个类数组
2
3
4
5
6
7
8
9
10
11
12
13
# 结果
这是题目在 Chrome 浏览器控制台输出结果
我们改变的题目输出结果
我们可以看到有效结果是一样的,那么为什么结果会是如此呢?
push()
方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。push
方法根据length
属性来决定从哪里开始插入给定的值。如果length
不能被转成一个数值,则插入的元素索引为 0,包括length
不存在时。当length
不存在时,将会创建它。MDN
so ~
// obj.push(1)
// 等同于
obj['2'] = 1
obj.length++ // length = 3
// obj.push(2)
// 等同于
obj['3'] = 2
obj.length++ // length = 4
// 那么之后的结果就是我们看到的了
2
3
4
5
6
7
8
9
但是还是有不一样的地方比如
[empty × 2, 1, 2, splice: ƒ, push: ƒ]
和 {2: 1, 3: 2, length: 4}
那么我们接下来继续看
只有当对象 splice
属性是一个 Function
的时候输出才为 [empty × 2, 1, 2, splice: ƒ, push: ƒ]
那么为此我又去 Firefox 控制台下面试了一下,结果如下图:
跟 Chrome 没有定义 splice 为 Function 是一致的
所以说可能是 Chrome 对其做的优化吧。