美文网首页
扩展对象

扩展对象

作者: 欢西西西 | 来源:发表于2023-03-26 17:21 被阅读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. 给属性值赋值时也许会不成功
    
    
    

    相关文章

      网友评论

          本文标题:扩展对象

          本文链接:https://www.haomeiwen.com/subject/gviirdtx.html