Latar Belakang
Saya memiliki fungsi yang mengambil config
objek sebagai argumen. Di dalam fungsinya, saya juga punya default
objek. Masing-masing objek tersebut berisi properti yang pada dasarnya berfungsi sebagai pengaturan untuk sisa kode di dalam fungsi tersebut. Untuk mencegah keharusan menentukan semua pengaturan dalam config
objek, saya menggunakan extend
metode jQuery untuk mengisi objek baru, settings
dengan nilai default apa pun dari default
objek jika tidak ditentukan dalam config
objek:
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};
Masalah
Ini berfungsi dengan baik, tetapi saya ingin mereproduksi fungsionalitas ini tanpa perlu jQuery. Apakah ada cara yang sama elegan (atau mendekati) untuk melakukan ini dengan javascript biasa?
Edit: Justifikasi Non-Duplikat
Pertanyaan ini bukan duplikat dari pertanyaan " Bagaimana cara menggabungkan properti dua objek JavaScript secara dinamis? ". Sedangkan pertanyaan itu hanya ingin membuat sebuah objek yang berisi semua kunci dan nilai dari dua objek yang terpisah - Saya secara khusus ingin membahas bagaimana melakukan ini jika kedua objek berbagi beberapa tetapi tidak semua kunci dan objek mana yang akan diutamakan ( default) untuk objek yang dihasilkan jika ada kunci duplikat. Dan bahkan lebih khusus lagi, saya ingin membahas penggunaan metode jQuery untuk mencapai ini dan menemukan cara alternatif untuk melakukannya tanpa jQuery. Meskipun banyak dari jawaban untuk kedua pertanyaan tersebut tumpang tindih, itu tidak berarti bahwa pertanyaan itu sendiri sama.
sumber
jQuery.isPlainObject
danjQuery.isFunction
Jawaban:
Untuk mendapatkan hasil dalam kode Anda, Anda akan melakukan:
function extend(a, b){ for(var key in b) if(b.hasOwnProperty(key)) a[key] = b[key]; return a; }
Perlu diingat bahwa cara Anda menggunakan perluasan di sana akan mengubah objek default. Jika Anda tidak menginginkannya, gunakan
$.extend({}, default, config)
Solusi yang lebih kuat yang meniru fungsionalitas jQuery adalah sebagai berikut:
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]; }
sumber
Anda dapat menggunakan 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"}
Ini menyalin nilai dari semua properti enumerable sendiri dari satu atau lebih objek sumber ke objek target dan mengembalikan objek target.
Object.assign(target, ...sources)
Ia bekerja di semua browser desktop kecuali IE (tetapi termasuk Edge). Ini telah mengurangi dukungan seluler.
Lihat sendiri di sini: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Tentang deep copy
Namun, Object.assign tidak memiliki opsi dalam yang dimiliki metode perluasan jQuery.
Catatan: Anda biasanya dapat menggunakan JSON untuk efek yang serupa
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"}
sumber
Anda dapat menggunakan operator penyebaran ECMA 2018 dalam literal objek ...
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};
Dukungan BabelJS untuk browser lama
sumber
Ini adalah pendekatan saya yang sedikit berbeda dengan deep copy yang saya buat saat mencoba menghilangkan ketergantungan jQuery. Ini sebagian besar dirancang untuk menjadi kecil sehingga mungkin tidak semua fitur yang diharapkan. Harus sepenuhnya kompatibel dengan ES5 (mulai dari IE9 karena penggunaan 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; }
Anda mungkin bertanya-tanya apa yang sebenarnya dilakukan oleh baris kelima ... Jika obj2.key adalah sebuah objek literal (yaitu jika itu bukan tipe biasa) kita secara rekursif memanggil perluasan padanya. Jika properti dengan nama itu belum ada di obj1, kami menginisialisasi ke objek kosong terlebih dahulu. Jika tidak, kita cukup mengatur obj1.key ke obj2.key.
Berikut adalah beberapa tes moka / chai saya yang seharusnya membuktikan kasus umum berfungsi di sini:
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', }); });
Saya akan senang dengan umpan balik atau komentar tentang penerapan ini. Terima kasih sebelumnya!
sumber
Anda dapat melakukan loop melalui properti Object menggunakan
for
pernyataan.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; }
sumber
a
. Meskipun tidak ada dalam contoh,$.extend
sebenarnya berfungsi seperti itu, menggabungkan semua properti dari semua objek.Saya lebih suka kode ini yang menggunakan forEachIn generik saya, dan tidak merusak objek pertama:
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; }
Jika Anda benar-benar ingin menggabungkan barang ke objek pertama, Anda dapat melakukan:
sumber
Ini sangat membantu saya ketika saya mengembangkan dengan javascript murni.
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; }
sumber
Pendekatan Ivan Kuckir juga dapat diadaptasi untuk membuat prototipe objek baru:
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);
sumber