相当于jQuery扩展方法的JavaScript

可能重复:
如何dynamic合并两个JavaScript对象的属性?

背景

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

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

问题

这很好,但我想重现这个function,而不需要jQuery。 是否有一个同样优雅(或接近)的手段来做到这一点纯朴的JavaScript?


编辑:非重复的理由

这个问题不是“ 如何dynamic合并两个JavaScript对象的属性 ”的问题。 而这个问题只是想创build一个对象,其中包含来自两个单独的对象的所有键和值 – 我特别想要解决如何做到这一点,如果两个对象共享一些但不是所有的键和哪个对象将优先在有重复键的情况下,生成对象的默认值)。 更具体地说,我想解决使用jQuery的方法来实现这一点,并find另一种方式来做到这一点,没有jQuery。 虽然这两个问题的许多答案重叠,但这并不意味着问题本身是相同的。

要在你的代码中得到结果,你应该这样做:

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

请记住,您使用的扩展方式将修改默认对象。 如果你不想要,使用

 $.extend({}, default, config) 

模仿jQueryfunction的更健壮的解决scheme如下:

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

你可以使用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扩展方法的深层选项。

注意:你通常可以使用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"} 

你可以使用for语句循环对象的属性。

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

这是我在尝试消除jQuery依赖时试图使用深度复制的稍微不同的方法。 它主要是为小而devise的,所以它可能并不是所有人都期望的。 应完全兼容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是一个对象字面量(即它不是普通types),我们recursion地调用extend。 如果一个具有该名称的属性在obj1中不存在,我们首先将它初始化为空对象。 否则,我们只需将obj1.key设置为obj2.key即可。

这里是我的一些摩卡/柴testing,应该certificate在这里工作的常见情况:

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

对于这个实现的反馈或评论,我会很高兴。 提前致谢!

我更喜欢这个使用我的genericsforEachIn的代码,并且不会破坏第一个对象:

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

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

 obj1 = extend(obj1, obj2); 

Ivan Kuckir的方法也可以用来创build一个新的对象原型:

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

当我使用纯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; }