I have a function that takes a config
object as an argument. Within the function, I also have default
object. Each of those
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;
}
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
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"}
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!
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);
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
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);