扩展对象

2023-03-26  本文已影响0人  欢西西西
// Spread可以在构造字面量对象时,将对象表达式按 key-value 的方式展开


// 情况1:
// let a = { test: '', ...obj }; // 编译前
// var a = _objectSpread({ test: '' }, obj); // 编译后


// 情况2:
// let a = { ...obj, test: '' }; // 编译前
// var a = _objectSpread(_objectSpread({}, obj), {}, { test: '' }); // 编译后    中间的{}??

/**
 * 看了_objectSpread:_objectSpread({}, obj, {test: ''})其实也能达到同样的效果
 */

function _typeof(obj) {
    "@babel/helpers - typeof";
    return _typeof =
        "function" == typeof Symbol && "symbol" == typeof Symbol.iterator // 检查原生js是否支持Symbol类型
            ? function (obj) { return typeof obj; }
            : function (obj) {
                return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype
                    ? "symbol" : typeof obj;
            },
        _typeof(obj);
}
function ownKeys(object, enumerableOnly) {
    var keys = Object.keys(object); // 只能拿到String类型的key,已过滤不可枚举的key
    // 获取类型为Symbol的key-并手动过滤enumerable为false的
    if (Object.getOwnPropertySymbols) {
        var symbols = Object.getOwnPropertySymbols(object);
        enumerableOnly && (symbols = symbols.filter(function (sym) {
            return Object.getOwnPropertyDescriptor(object, sym).enumerable;
        })), keys.push.apply(keys, symbols);
    }
    return keys;
}
function _objectSpread(target) {
    // 第一个入参为target,从第二个参数开始,属性都扩展到target上
    for (var i = 1; i < arguments.length; i++) {
        var source = null != arguments[i] ? arguments[i] : {};

        i % 2
            ?
            ownKeys(Object(source), !0).forEach(function (key) { // 请先查看——备用1
                _defineProperty(target, key, source[key]);
            })
            :
            Object.getOwnPropertyDescriptors
                ?
                Object.defineProperties(target, Object.getOwnPropertyDescriptors(source))
                :
                ownKeys(Object(source)).forEach(function (key) {
                    Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
                });

        /**
         * 将一个对象的所有属性都扩展给另一个对象,这里使用了两种方式:
         * 
         * 1. 枚举key,设置每一个属性
         * |__Object.keys() / ownKeys()
         * |__没有直接使用=进行赋值
         * 
         * 2. 批量设置:Object.defineProperties
         * |__Object.getOwnPropertyDescriptors可以获取到所有描述(包括Symbol类型的key)
         * |__但是扩展运算符扩展出来的属性的描述全都是默认的,enumerable\configurable\writable都为true,而且没有setter和getter
         * |__而且会获取到所有属性描述,包括不可枚举的
         * |__但是用户的对象无法保证descriptor是什么,所以对于用户要扩展的对象不能使用这种批量设置的方式,只能枚举key
         * |__请先查看——备用2
         * |__奇数:用户的——枚举key; 偶数:自创的——批量设置
         * 
         * 
         * let a = { test: '', ...obj }; // 编译前
         * var a = _objectSpread({ test: '' }, obj); // 编译后
         * 
         * let a = { ...obj, test: '' }; // 编译前
         * var a = _objectSpread(_objectSpread({}, obj), {}, { test: '' }); // 编译后
         */

        /**
         * 为啥要使用2种,不能全都枚举key吗
         */
    }
    return target;
}
function _defineProperty(obj, key, value) {
    // 修改obj的key属性值为value
    key = _toPropertyKey(key); // 校验key的合法性:Symbol和String类型
    if (key in obj) {
        Object.defineProperty(obj, key, {
            value: value, enumerable: true, configurable: true, writable: true
        });
    } // 请先查看——备用4
    else { obj[key] = value; }
    return obj;

    /**
     * configurable: true才能重复defineProperty,不然会报错 
     * 不过由于target都是我们自己创建的对象,所以都是可配置的
     */
}
function _toPropertyKey(arg) {
    var key = _toPrimitive(arg, "string");
    return _typeof(key) === "symbol" ? key : String(key);
}
function _toPrimitive(input, hint) {
    if (_typeof(input) !== "object" || input === null)
        return input;
    var prim = input[Symbol.toPrimitive];
    if (prim !== undefined) {
        var res = prim.call(input, hint || "default");
        if (_typeof(res) !== "object")
            return res;
        throw new TypeError("@@toPrimitive must return a primitive value.");
    }
    return (hint === "string" ? String : Number)(input);
}

// #region 备用1-ownKeys

var symb = Symbol('name'),
    symb1 = Symbol('fixed'),
    obj = { name: 'wxm', [symb]: 'key为symbol' };
Object.defineProperty(obj, 'fixed', { value: true, configurable: true, writable: true, enumerable: false });
Object.defineProperty(obj, symb1, { value: 'readonly', configurable: true, writable: true, enumerable: false });

// #endregion

// #region 备用2-getOwnPropertyDescriptors
var obj = {};
var v = '';
Object.defineProperty(obj, 'width', {
    enumerable: true,
    configurable: true,
    set(value) {
        console.log('setting width');
        v = value;
    },
    get() {
        return v;
    }
});
Object.getOwnPropertyDescriptors(obj);

var obj2 = { ...obj };
Object.getOwnPropertyDescriptors(obj2);


// #endregion

// #region 备用3-批量还是循环
function keysTest(target, source) {
    ownKeys(Object(source), !0).forEach(function (key) {
        _defineProperty(target, key, source[key]);
    });
}

function batchTest(target, source) {
    Object.getOwnPropertyDescriptors
        ?
        Object.defineProperties(target, Object.getOwnPropertyDescriptors(source))
        :
        ownKeys(Object(source)).forEach(function (key) {
            Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
        });
}

var object = {};
for (let i = 0; i < 10000; i++) {
    object[i] = {};
}

console.time('enum');
keysTest({}, object);
console.timeEnd('enum');

console.time('batch');
batchTest({}, object);
console.timeEnd('batch');
// #endregion

// #region 备用4-赋值失败
var obj = {};
Object.defineProperty(obj, 'width', {
    value: 100,
    enumerable: true,
    configurable: true,
    writable: false
});
obj.width = 200; // 修改失败

Object.defineProperty(obj, 'width', {
    value: 200,
    enumerable: true,
    configurable: true,
    writable: true
});
// #endregion

// 1. 解构对象的时候,enumerable为false的属性以及Descriptor的getter和setter都是不会被复制的
// 2. 获取对象的所有key时不要忘了Symbol类型的key
// 3. 复制一个对象的属性还有这种方式:Object.defineProperties(target, Object.getOwnPropertyDescriptors(source))
// 4. 给属性值赋值时也许会不成功


上一篇 下一篇

猜你喜欢

热点阅读