Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

类数组对象的表现 #2

Closed
francecil opened this issue Sep 30, 2019 · 3 comments
Closed

类数组对象的表现 #2

francecil opened this issue Sep 30, 2019 · 3 comments
Labels
JavaScript JS相关面试、笔试题,不涉及算法

Comments

@francecil
Copy link
Owner

题目来源 #76
问题:

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)

请问输出结果是什么,为什么

@francecil francecil added the JavaScript JS相关面试、笔试题,不涉及算法 label Sep 30, 2019
@francecil
Copy link
Owner Author

francecil commented Sep 30, 2019

输出结果

在 chrome 中表现为

[empty × 2, 1, 2, splice: ƒ, push: ƒ]

在其他浏览器表现为

{2: 1, 3: 2, length: 4, push: f, splice: f}

乍一看好像不一样,其实结果是一样的,展开 chrome 上的 obj 的输出就发现其原型还是 Object。chrome 上的输出是由于 ChromeDevTools 特殊处理导致显示不一样。

分析

chrome 的特殊显示

ChromeDevTools 中可以看到,当对象含有 splice 方法且 length 为32位非负整数,判断该对象为类数组对象,会给予特殊显示

举一些例子:

{1:'a',length:2,splice:()=>{}} // Object(2) [empty, "a", splice: ƒ]
{1:'a',length:2,splice:'a'} // {1: "a", length: 2, splice: "a"}
{1:'a',length:1.5,splice:()=>{}} // {1: "a", length: 1.5, splice: ƒ}

push

接下来分析 push,当调用 push 方法的时候,会发生什么?
这里引用 MDN 中的一段话

push 方法有意具有通用性。该方法和 call()apply() 一起使用时,可应用在类似数组的对象上。push 方法根据 length 属性来决定从哪里开始插入给定的值。如果 length 不能被转成一个数值,则插入的元素索引为 0,包括 length 不存在时。当 length 不存在时,将会创建它。

由于一开始 length === 2 故 执行 obj.push(1) 时,相当于执行的是 obj[2] = 1

同理第二次 push 相当于执行 obj[3]=2

最后,举个在 chrome DevTools 上输出的有趣例子

var obj = {
    'splice': Array.prototype.splice,
    'push': Array.prototype.push
}
obj // {splice: ƒ, push: ƒ}
obj.push("a")
obj // Object ["a", splice: ƒ, push: ƒ]

@francecil
Copy link
Owner Author

ChromeDevTools 源码中有这样一段代码

typeof len === 'number' && (len >>> 0 === len && (len > 0 || 1 / len > 0))

其作用是判断一个变量为数字,并且该数字为32位非负整数

>>> (无符号右移)

该操作符会将第一个操作数向右移动指定的位数。向右被移出的位被丢弃,左侧用0填充。因为符号位变成了 0,所以结果总是非负的。(即便右移 0 个比特,结果也是非负的。)
位运算结果是32位的

(2**32-1) >>> 0 === 4294967295 // (0)11111111111111111111111111111111 -> (0)11111111111111111111111111111111
(2**32) >>> 0 === 0 //  100000000000000000000000000000000(33位) -> (0)00000000000000000000000000000000(32位无符号)
-1 >>> 0 ===  4294967295 // 11111111111111111111111111111111 -> (0)11111111111111111111111111111111
1.1 >>> 0 === 1 // 小数部分直接丢弃

那看来 len >>> 0 === len 就能用来判断 len 为32位非负整数了,为什么后面还要加个 (len > 0 || 1 / len > 0) ?

其实是为了判断值为 -0 的情况,(当然 0、+0 这里是可以的)

-0 >>> 0 === -0 // true
(-0 > 0 || 1 / -0 > 0) // false, 1 / -0  === -Infinity

所以会有这样的差异

{1:'a',length:-0,splice:()=>{}} // {1: "a", length: -0, splice: ƒ}
{1:'a',length:0,splice:()=>{}} // Object [1: "a", splice: ƒ]

那为什么要特殊判断一个 -0 呢 。。

不知道,如果你知道的话 欢迎评论。。

@francecil francecil reopened this Oct 8, 2019
@francecil
Copy link
Owner Author

暂时没有明白特殊判断 -0 含义,只能认为是 ChromeDevTools 的需求吧

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
JavaScript JS相关面试、笔试题,不涉及算法
Projects
None yet
Development

No branches or pull requests

1 participant