JavaScript相当于jQuery的extend方法

时间:2012-06-25 20:56:25

标签: javascript jquery object extend

背景

我有一个以config个对象作为参数的函数。在函数中,我也有default个对象。这些对象中的每一个都包含基本上用作函数内其余代码的设置的属性。为了防止必须指定config对象中的所有设置,我使用jQuery的extend方法填充新对象settings,其中包含{{1}的任何默认值如果未在default对象中指定对象:

config

问题

这很好用,但我想在不需要jQuery的情况下重现这个功能。是否有一个同样优雅(或接近)的方法来做普通的'javascript?


编辑:非重复对齐

此问题与“How can I merge properties of two JavaScript objects dynamically?”问题不重复。虽然这个问题只是想创建一个包含来自两个独立对象的所有键和值的对象 - 我特别想要解决如何在两个对象共享一些但不是所有键以及哪个对象将优先的情况下执行此操作(如果存在重复键,则生成的对象的默认值。更具体地说,我想解决使用jQuery的方法来实现这一点,并找到一种没有jQuery的替代方法。虽然这两个问题的答案很多重叠,但这并不意味着问题本身是相同的。

8 个答案:

答案 0 :(得分:126)

要在代码中获得结果,您可以执行以下操作:

function extend(a, b){
    for(var key in b)
        if(b.hasOwnProperty(key))
            a[key] = b[key];
    return a;
}

请记住,您在那里使用扩展的方式将修改默认对象。如果您不想这样,请使用

$.extend({}, default, config)

模仿jQuery功能的更强大的解决方案如下:

function extend(){
    for(var i=1; i<arguments.length; i++)
        for(var key in arguments[i])
            if(arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}

答案 1 :(得分:19)

您可以使用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"}

它将所有可枚举的自有属性的值从一个或多个源对象复制到目标对象,并返回目标对象。

Object.assign(target, ...sources)

它适用于除IE之外的所有桌面浏览器(但包括Edge)。 它减轻了移动支持。

在这里亲自看看: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

关于深层复制

但是,Object.assign没有jQuery的extend方法具有的深度选项。

注意:您通常可以使用JSON来获得类似的效果

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"}

答案 2 :(得分:17)

您可以使用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

答案 3 :(得分:2)

您可以使用for语句循环访问Object的属性。

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;
}

答案 4 :(得分:2)

这是我在尝试消除jQuery依赖项时提出的深度副本的略微不同的方法。它主要是为小而设计的,因此它可能没有人们期望的所有功能。应完全兼容ES5(由于使用Object.keys而从IE9开始):

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;
  }

你可能想知道第五行究竟做了什么......如果obj2.key是一个对象文字(即如果它不是普通类型),我们递归地调用它的扩展。如果obj1中不存在具有该名称的属性,我们首先将其初始化为空对象。否则我们只需将obj1.key设置为obj2.key。

以下是我的一些mocha / chai测试应该证明在这里工作的常见情况:

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',
  });
});

我对此实施的反馈或评论感到高兴。提前谢谢!

答案 5 :(得分:2)

我更喜欢使用我的泛型forEachIn的代码,并不会破坏第一个对象:

{{1}}

如果你真的想将东西合并到第一个对象中,你可以这样做:

{{1}}

答案 6 :(得分:0)

当我使用纯JavaScript进行开发时,它对我有很大的帮助。

function extends(defaults, selfConfig){
     selfConfig = JSON.parse(JSON.stringify(defaults));
     for (var item in config) {
         if (config.hasOwnProperty(item)) {
             selfConfig[item] = config[item];
         }
     }
     return selfConfig;
}

答案 7 :(得分:0)

The approach of Ivan Kuckir's也可用于创建新的对象原型:

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);