背景
我有一个函数,它接受一个config
对象作为参数。 在函数,我也有default
对象。 每个这些对象包含基本上为代码的函数内,与其他设置工作的性质。 为了防止其在中指定的所有设置config
对象,我使用jQuery的extend
方法来填充在一个新的对象, settings
从任何默认值default
对象,如果在没有指定他们config
对象:
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};
问题
这个伟大的工程,但我想重现这一功能,而不需要jQuery的。 是否有一个同样优雅(或接近)指纯醇” JavaScript来做到这一点?
编辑:不重复的理由
这个问题是不是“的副本我怎么能合并的两个属性的JavaScript对象的动态? ”的问题。 而这个问题只是想创建一个包含所有来自两个不同的对象键和值的对象 - 我特别希望解决如何,这两个对象共享一些,但不是所有的按键做在事件和对象将获得优先(在事件的默认值)生成的对象有重复键。 而更具体地讲,我想解决了使用jQuery的方法来实现这一点,并找到一个替代办法,而不jQuery来做到这一点。 虽然许多人的答案这两个问题的重叠,这并不意味着,这些问题本身是相同的。
为了得到结果在你的代码,你会怎么做:
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];
}
您可以使用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以外(但包括边缘)。 它减轻了移动支持。
见自己的位置: 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"}
您可以使用ECMA 2018 价差在操作对象文本 ...
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
您可以通过对象的属性循环使用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的依赖。 它主要设计用于为小,因此它可能不是所有的功能,一个希望。 应充分ES5兼容(IE9从因的使用开始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;
}
你可能不知道的第五行不正是这样做...如果obj2.key是一个对象文字(即,如果它没有普通型),我们递归调用扩展就可以了。 如果使用该名称的属性不存在OBJ1然而,我们首先初始化一个空的对象。 否则,我们只需设置obj1.key到obj2.key。
下面是我的一些摩卡/柴测试应该证明通常情况下在这里工作:
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',
});
});
我很高兴在此实现反馈意见和建议。 提前致谢!
我喜欢这个代码,使用我一般forEachIn,并且不裂伤的第一个对象:
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);
它帮助了我很多,当我使用纯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;
}
伊万Kuckir的这种方法也可以用于创建新对象的原型:
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);