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

输出以下代码执行的结果并解释为什么 #1

Open
CuriosityLxn opened this issue Jun 10, 2021 · 0 comments
Open

输出以下代码执行的结果并解释为什么 #1

CuriosityLxn opened this issue Jun 10, 2021 · 0 comments

Comments

@CuriosityLxn
Copy link
Owner

CuriosityLxn commented Jun 10, 2021

题目

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)

考点: Array-Like Object

答案:
image

题目来源

解析

在执行 push 操作之前,先来看看 obj 是什么情况:

var obj = {
    '2': 3,
    '3': 4,
    'length': 2,
    'splice': Array.prototype.splice,
    'push': Array.prototype.push
}

console.log(obj);
/*
* Object(2) [empty × 2, 2: 3, 3: 4, splice: ƒ, push: ƒ]
*      2: 3
*      3: 4
*      length: 2
*      push: ƒ push()
*      splice: ƒ splice()
*      __proto__: Object
*/
console.log(obj.length); // 2
console.log(obj[0], obj[1], obj[2], obj[3]); // undefined undefined 3 4

push 方法根据 length 属性来决定从哪里开始插入给定的值,并且改变数组长度,当 length 不存在时,会自动创建 length。

类数组对象特点是有 length 属性且可以用索引访问,但没有数组方法,所以执行 push 操作之前的 obj 是个类数组,从 obj 的各种打印结果也能看出。obj 前两位都是空值,length 为 2,push 会从 obj[2] 开始插入给定值。

  1. 执行第一次 push, obj[2] = 1, obj.length++, obj.length = 2++ = 3;
  2. 执行第二次 push, obj[3] = 2, obj.length++, obj.length = 3++ = 4;
  3. obj[0]、obj[1] 维持空值不变。

其他问题

Q:为什么 push 可以被用在 Object 上?
A:push 是特意设计为通用的,Array.prototype.push 可以用在对象上。当将该集合存储在对象本身上,如题这样,使用 Array.prototype.push.call(obj) ,obj 会被当成在数组上执行 push 操作。

题中

var obj = {
    ...,
    'push': Array.prototype.push
}
obj.push(1)

也可以写成

var obj = {
    ...
}
Array.prototype.push.call(obj, 1);
Array.prototype.push.call(obj, 2);

输出结果是一样的。

Q:为什么对象添加了splice属性后并没有调用就会变成类数组对象?这个问题比较无聊🙄️
A:据大佬分析,是 chrome 的 Devtools 做了优化,还扒出了代码:

  /**
     * @param {?Object} obj
     * @return {boolean}
     */
    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;
    }

判断的过程:

  1. 存在且是对象
  2. 对象上的splice 属性是函数类型
  3. 对象上有 length 属性且为正整数

延伸

  1. 以下这段代码输出什么?
var obj = {
    '2': 3,
    '3': 4,
    'length': 6
}
Array.prototype.push.call(obj, 1);
console.log(obj);

答案:{2: 3, 3: 4, 6: 1, length: 7}

  1. 有哪些方式可以将类数组对象转化为数组?
  • Array.prototype.slice.call(arrayLikeObject),call 将 this 绑定到类数组对象上,slice 在未接收任何参数时会,创建类数组的一个拷贝,然后由 slice 返回一个新数组。
  • Array.prototype.forEach.call(arrayLikeObject, fn) 或 for 循环
Array.prototype.forEach.call(arrayLikeObject, (item, index) => console.log(`arrayLikeObject[${index}]:`, item));

for (let i = 0; i < arrayLikeObject.length; i++) {
    console.log(`arrayLikeObject[${i}]:`, arrayLikeObject[i]);
}
  • ES6 Array.from,
Array.from(arrayLikeObject)

本篇参考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant