javascript实现合并对象的方法有很多种,比如:
1、Object.assign
2、jQuery.extend(jQuery也是用javascript写的,谢谢)
3、lodash系列(lodash.merge、lodash.assign等,至于区别自己看文档,文档地址:https://lodash.com/docs)
4、Immutable.js(fackbook打造的一个不可变数据结构JS库)的 merge 方法
其中,Object.assign为javascript原生方法,但是存在以下两个在具体应用场景上的缺点:
1、浏览器兼容性问题
2、只能进行浅合并(关于浅合并深合并,码友之述备矣,这里就不赘言,戳:https://juejin.im/entry/58df4c8b61ff4b006b131792)
PS: 之所以说具体应用场景的缺点,是因为假如项目是在高版本浏览器运行,并且只要对数据结构进行浅合并,那就不存在上述两个问题
而为了实现合并对象,特意引入上述的 jQuery、lodash、immutable这些库,就有点夸张了(项目本身需要用到这些库,那当我什么也没说)
好了,进入正题,下面是我自己实现的一个可配置的合并多个对象的方法
function EXToptions) { return new EXT.prototype.initoptions); } EXT.fn = EXT.prototype = { type: functiono) { return Object.prototype.toString.callo).slice8, -1).toLowerCase); }, typeMap: { object: function) { return {}; }, array: function) { return []; } }, // 默认配置项 defaults: { // 是否深合并 isDeep: true, // 是否遍历合并源对象原型链上的属性 includePrototype: true, // 用于对每个合并项进行自定义修正 forEach: functiontarget, name, sourceItem) { target[name] = sourceItem; return target; } }, // 将配置项合并到默认配置项 init: functionoptions) { for let name in options) { this.defaults[name] = options[name]; } return this; }, merge: function) { let self = this, _default = self.defaults, i = 1, length = arguments.length, target = arguments[0] || {}, source, targetItem, sourceItem, tiType, siType, clone, name; for ; i < length; i++) { // 判断源对象是否为空 if source = arguments[i]) != null) { for name in source) { // 是否遍历源对象的原型链 if source.hasOwnPropertyname) || _default.includePrototype) { targetItem = target[name]; sourceItem = source[name]; tiType = self.typetargetItem); siType = self.typesourceItem); // 防止出现回环 if target === sourceItem) { continue; } // 如果复制的是对象或者数组 if _default.isDeep && sourceItem != null && self.typeMap[siType]) { clone = targetItem != null && tiType === siType ? targetItem : self.typeMap[siType]); // 递归 target[name] = self.mergeclone, sourceItem); } else { // 处理每一个合并项 target = _default.forEach.callself, target, name, sourceItem); } } } } } return target; } }; EXT.fn.init.prototype = EXT.fn;
撸个demo先,先定义两份数据
function Foo) { this.a = 1; } function Bar) { this.c = 3; } Foo.prototype.b = 2; Bar.prototype.d = 4; let data = { info: { name: 'Leslie', age: 26, scores: [60, 66, 70, 80] } }; let data2 = { info: { name: 'Leslie', age: 32, scores: [99, 66, 70, { name: 'john', age: 18 }, new Foo)] } };
1、普通合并
let target = EXT).mergedata1, data2);
结果为:
2、自定义配置进行合并
isDeep:选择是否进行深合并,设置为 false 则只进行浅合并,默认为 true
let target = EXT{ isDeep: false }).mergedata1, data2);
includePrototype:选择是否要遍历对象的原型链,默认为 true
let target = EXT{ includePrototype: false }).mergedata1, data2);
forEach:对每个合并项进行自定义处理
let target = EXT{ forEach: functiontarget, name, sourceItem) { target[name] = sourceItem + ’hello, 自定义每个合并项‘; return target; } }).mergedata1, data2);
好了,这个就是这个方法的使用方法,怎么样?是不是很灵活?是不是很好用?
但是!(最怕的就是但是)这个方法还是有问题的,不知道电视机前的你发现没有,就是合并 new Foo) 的时候,会把 Foo 原型链上的属性一起拷贝到目标对象,而不是拷贝到目标对象的原型链
这个问题暂时没想到解决方法,后面想到会更新上来,或者你有什么解决方案也可以留言,帮忙解决一下,最后祝大家加班愉快!
(胖虎是我偶像)