对象没有 Symbol.Iterator 方法为什么能解构?

1,071 阅读2分钟

解构原理

const obj = {
	id: 1
	name: 'xxx',
	age: 20
	children: {
		id: 2,
		name: 'yyy',
		age: 18
	}
}

const { id, children, ...rest } = obj

在这里,解构出来的 id 和 children 其实是一个全新的赋值,等同于:

let id = obj.id, children = obj.children

所以不管怎么修改 id,都不会对原对象的 id 造成改变。但值得一提的是,解构出来的对象属于是浅拷贝,指向的都是同一个地址,当对解构出来的 children 对象里的值进行修改,在原对象的 children 里的值也会被改变。

children.id = 3

// 输出 obj
const obj = {
	id: 1,
	name: 'xxx',
	children: {
		id: 3,
		name: 'yyy',
		age: 18
	}
}

剩余参数的解构原理

const obj = {
    id: 7,
    name: 'xxx',
    age: 18,
    [Symbol("syb")]: 123,
};

为了更清楚的说明剩余参数的解构原理,我们可以通过 babel 把上述代码编译为 ES5 代码来看一下。

function _objectWithoutProperties(source, excluded)
{
    if (source == null) return {};
    // 获取除了 symbol 的所有参数
    var target = _objectWithoutPropertiesLoose(source, excluded);
    var key, i;
    if (Object.getOwnPropertySymbols)
    {
        var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
        for (i = 0; i < sourceSymbolKeys.length; i++)
        {
            key = sourceSymbolKeys[i];
            if (excluded.indexOf(key) >= 0) continue;
            if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
            target[key] = source[key];
        }
    }
    return target;
}

function _objectWithoutPropertiesLoose(source, excluded)
{
    if (source == null) return {};
    var target = {};
    var sourceKeys = Object.keys(source);
    var key, i;
    for (i = 0; i < sourceKeys.length; i++)
    {
        key = sourceKeys[i];
        if (excluded.indexOf(key) >= 0) continue;
        target[key] = source[key];
    }
    return target;
}

var obj = _defineProperty(
    {
        id: 7,
        name: 'xxx',
        age: 18,
    },
    Symbol('syb'),
    123
);

var name = obj.name,
    rest = _objectWithoutProperties(obj, ['name']);

_objectWithoutPropertiesLoose 函数中可以很清楚的看到,这里先是把传进来的对象先通过 Object.keys 提取出 key ,再遍历取出除了 excluded 以外的所有 key 的值。然后因为 Object.keys 方法只会返回一个对象的自身可枚举属性组成的数组,它不会返回对象的 Symbol 属性。这是因为 Symbol 属性被设计为不可枚举的,所以它们不会被 Object.keys() 方法遍历到。

所以需要通过 Object.getOwnPropertySymbols() 方法获取对象中的所有 Symbol 属性,再进行比较赋值。最后得到除了 exclude 外的所有属性。

总结

对象结构语法是创建对象 -> 枚举属性 -> 复制属性,跟迭代器没有关系。