JavaScript equivalent of jQuery's extend method

前端 未结 8 2010
小鲜肉
小鲜肉 2020-11-28 02:57

Background

I have a function that takes a config object as an argument. Within the function, I also have default object. Each of those

相关标签:
8条回答
  • 2020-11-28 03:08

    You can loop through Object's properties using for statement.

    var settings = extend(default, config);
    
    function extend(a, b){
        var c = {};
        for(var p in a)
            c[p] = (b[p] == null) ? a[p] : b[p];
        return c;
    }
    
    0 讨论(0)
  • 2020-11-28 03:09

    You can use Object.assign.

    var defaults = {key1: "default1", key2: "default2", key3: "defaults3"};
    var config = {key1: "value1"};
    
    var settings = Object.assign({}, defaults, config); // values in config override values in defaults
    console.log(settings); // Object {key1: "value1", key2: "default2", key3: "defaults3"}
    

    It copies the values of all enumerable own properties from one or more source objects to a target object and returns the target object.

    Object.assign(target, ...sources)
    

    It works in all desktop browsers except IE (but including Edge). It has mitigated mobile support.

    See for yourself here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

    About deep copy

    However, Object.assign does not have the deep option that jQuery's extend method have.

    Note: you can generally use JSON for a similar effect though

    var config = {key1: "value1"};
        var defaults = {key1: "default1", key2: "default2", keyDeep: {
            kd1: "default3",
            kd2: "default4"
        }};
        var settings = JSON.parse(JSON.stringify(Object.assign({}, defaults, config))); 
        console.log(settings.keyDeep); // Object {kd1: "default3", kd2: "default4"}
    
    0 讨论(0)
  • 2020-11-28 03:15

    This is my slightly different approach with deep copy I came up with while trying to eliminate a jQuery dependency. It is mostly designed for being small so it might have not all feature one expects. Should be fully ES5-compatible (starting from IE9 due to usage of Object.keys):

    function extend(obj1, obj2) {
        var keys = Object.keys(obj2);
        for (var i = 0; i < keys.length; i += 1) {
          var val = obj2[keys[i]];
          obj1[keys[i]] = ['string', 'number', 'array', 'boolean'].indexOf(typeof val) === -1 ? extend(obj1[keys[i]] || {}, val) : val;
        }
        return obj1;
      }
    

    You may wonder what the fifth line does exactly do ... If obj2.key is an object literal (i.e. if it's no ordinary type) we recursively call extend on it. If a property with that name doesn't exist in obj1 yet, we initialize it to an empty object first. Otherwise we simply set obj1.key to obj2.key.

    Here are some of my mocha/chai tests that should prove the common cases to work here:

    it('should extend a given flat object with another flat object', () => {
      const obj1 = {
        prop1: 'val1',
        prop2: 42,
        prop3: true,
        prop4: 20.16,
      };
      const obj2 = {
        prop4: 77.123,
        propNew1: 'newVal1',
        propNew2: 71,
      };
      assert.deepEqual(utils.extend(obj1, obj2), {
        prop1: 'val1',
        prop2: 42,
        prop3: true,
        prop4: 77.123,
        propNew1: 'newVal1',
        propNew2: 71,
      });
    });
    
    it('should deep-extend a given flat object with a nested object', () => {
      const obj1 = {
        prop1: 'val1',
        prop2: 'val2',
      };
      const obj2 = {
        propNew1: 'newVal1',
        propNew2: {
          propNewDeep1: 'newDeepVal1',
          propNewDeep2: 42,
          propNewDeep3: true,
          propNewDeep4: 20.16,
        },
      };
      assert.deepEqual(utils.extend(obj1, obj2), {
        prop1: 'val1',
        prop2: 'val2',
        propNew1: 'newVal1',
        propNew2: {
          propNewDeep1: 'newDeepVal1',
          propNewDeep2: 42,
          propNewDeep3: true,
          propNewDeep4: 20.16,
        },
      });
    });
    
    it('should deep-extend a given nested object with another nested object and deep-overwrite members', () => {
      const obj1 = {
        prop1: 'val1',
        prop2: {
          propDeep1: 'deepVal1',
          propDeep2: 42,
          propDeep3: true,
          propDeep4: {
            propDeeper1: 'deeperVal1',
            propDeeper2: 777,
            propDeeper3: 'I will survive',
          },
        },
        prop3: 'lone survivor',
      };
      const obj2 = {
        prop1: 'newVal1',
        prop2: {
          propDeep1: 'newDeepVal1',
          propDeep2: 84,
          propDeep3: false,
          propDeep4: {
            propDeeper1: 'newDeeperVal1',
            propDeeper2: 888,
          },
        },
      };
      assert.deepEqual(utils.extend(obj1, obj2), {
        prop1: 'newVal1',
        prop2: {
          propDeep1: 'newDeepVal1',
          propDeep2: 84,
          propDeep3: false,
          propDeep4: {
            propDeeper1: 'newDeeperVal1',
            propDeeper2: 888,
            propDeeper3: 'I will survive',
          },
        },
        prop3: 'lone survivor',
      });
    });
    

    I'd be happy about feedback or comments on this implementation. Thanks in advance!

    0 讨论(0)
  • 2020-11-28 03:18

    The approach of Ivan Kuckir's can also be adapted to create a new object prototype:

    Object.prototype.extend = function(b){
    for(var key in b)
        if(b.hasOwnProperty(key))
            this[key] = b[key];
        return this;
    }
    
    var settings = default.extend(config);
    
    0 讨论(0)
  • 2020-11-28 03:25

    You can use the ECMA 2018 spread operator in object literals...

    var config = {key1: value1};
    var default = {key1: default1, key2: default2, key 3: default 3};
    
    var settings = {...default, ...config}
    
    //resulting properties of settings:
    settings = {key1: value1, key2: default2, key 3: default 3};
    

    BabelJS support for older browsers

    0 讨论(0)
  • 2020-11-28 03:25

    I prefer this code that uses my generic forEachIn, and does not mangle the first object:

    function forEachIn(obj, fn) {
        var index = 0;
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                fn(obj[key], key, index++);
            }
        }
    }
    
    function extend() {
        var result = {};
        for (var i = 0; i < arguments.length; i++) {
            forEachIn(arguments[i],
                function(obj, key) {
                    result[key] = obj;
                });
        }
        return result;
    }
    

    If you really do want to merge stuff into the first object, you can do:

    obj1 = extend(obj1, obj2);
    
    0 讨论(0)
提交回复
热议问题