合并JSON对象的正确方式
一、 前言
“JSON对象合并”是前端开发和 NodeJS 环境开发中非常常见的操作。开发者通常会通过循环遍历或一些库封装的方法或 JavaScript ECMAScript 2015 定义的 Object.assign() 来实现。
二、 常见合并方式
1. 方法一:循环遍历法
function extend() {
var length = arguments.length;
if(length == 0)return {};
if(length == 1)return arguments[0];
var target = arguments[0] || {};
for (var i = 1; i < length; i++) {
var source = arguments[i];
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
return target;
}
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4, d: 5 };
const result = extend(obj1, obj2);
console.log(result);//{ a: 1, b: 3, c: 4, d: 5 }
2. 方法二:Object.assign()
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4, d: 5 };
const result = Object.assign(obj1, obj2);
console.log(result);//{ a: 1, b: 3, c: 4, d: 5 }
3. 方法三:用jQuery插件
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const { window } = new JSDOM(`<!DOCTYPE html>`);
const $ = require('jQuery')(window);
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4, d: 5 };
const result = $.extend(obj1, obj2);
console.log(result);//{ a: 1, b: 3, c: 4, d: 5 }
从测试结果来看,以上三种方案都能得到预期结果。
三、 进阶
但是在实际项目中,需要合并的对象往往不是那么简单,比如合并以下几个对象:
let chartScheme = {
series: [{
name: '本地',
type: 'bar',
stack: '驻留分析',
},
{
name: '省内',
type: 'bar',
stack: '驻留分析',
}
]
};
let chartStyle = {
grid: {
top: '25%',
bottom: '30',
left: '15%',
right: '5%'
},
series: [{
barWidth: '10px',
itemStyle: {
color: '#4AC6E7'
}
},
{
barWidth: '10px',
itemStyle: {
color: '#73C642'
}
}
]
};
let chartStyle2 = {
series: [{
lineStyle: {
normal: {
color: '#36d0f3'
}
},
itemStyle: {
normal: {
color: '#36d0f3'
}
}
},
{
lineStyle: {
normal: {
color: '#36d0f3'
}
},
itemStyle: {
normal: {
color: '#36d0f3'
}
}
}
]
};
let chartData = {
series: [{
data: [35, 60, 50, 80, 80, 55, 70, 70],
},
{
data: [60, 80, 60, 45, 80, 55, 60, 20],
}
]
};
合并的目标是保留所有非同名属性,同名原子属性用后面的参数覆盖前面的参数。
下面用上述几种方法做合并测试:
1. 测试方法一
let result = extend(chartScheme, chartStyle, chartStyle2, chartData);
console.log(result);
结果如下,非原子属性被覆盖,未达到要求:
result = {
"series": [{
"data": [35, 60, 50, 80, 80, 55, 70, 70]
}, {
"data": [60, 80, 60, 45, 80, 55, 60, 20]
}],
"grid": {
"top": "25%",
"bottom": "30",
"left": "15%",
"right": "5%"
}
}
2. 测试方法二
let result = Object.assign(chartScheme, chartStyle, chartStyle2, chartData);
console.log(result);
结果如下,跟方法一一样未达到要求:
result = {
"series": [{
"data": [35, 60, 50, 80, 80, 55, 70, 70]
}, {
"data": [60, 80, 60, 45, 80, 55, 60, 20]
}],
"grid": {
"top": "25%",
"bottom": "30",
"left": "15%",
"right": "5%"
}
}
3. 测试方法三
let result = $.extend(chartScheme, chartStyle, chartStyle2, chartData);
console.log(JSON.stringify(result));
结果如下,还是没有到要求:
result = {
"series": [{
"data": [35, 60, 50, 80, 80, 55, 70, 70]
}, {
"data": [60, 80, 60, 45, 80, 55, 60, 20]
}],
"grid": {
"top": "25%",
"bottom": "30",
"left": "15%",
"right": "5%"
}
}
很显然,以上三种方法在合并比较复杂的对象时都是有缺陷的,合并后目标对象的非原子属性被后面的源对象的同名属性直接覆盖了。
不过 jQuery 的“extend()”方法有两个原型:
$.extend( target [, object1 ] [, objectN ] )
$.extend( [deep ], target, object1 [, objectN ] )
上面用的是第一个原型,第二个原型还有一个深拷贝选项,即“$.extend()”的第一个参数传“true”,可以试试:
let result = $.extend(true, chartScheme, chartStyle, chartStyle2, chartData);
console.log(JSON.stringify(result));
结果如下:
let result = $.extend(true, chartScheme, chartStyle, chartStyle2, chartData);
console.log(JSON.stringify(result));
result = {
"series": [{
"name": "本地",
"type": "bar",
"stack": "驻留分析",
"barWidth": "10px",
"itemStyle": {
"color": "#4AC6E7",
"normal": {
"color": "#36d0f3"
}
},
"lineStyle": {
"normal": {
"color": "#36d0f3"
}
},
"data": [35, 60, 50, 80, 80, 55, 70, 70]
}, {
"name": "省内",
"type": "bar",
"stack": "驻留分析",
"barWidth": "10px",
"itemStyle": {
"color": "#73C642",
"normal": {
"color": "#36d0f3"
}
},
"lineStyle": {
"normal": {
"color": "#36d0f3"
}
},
"data": [60, 80, 60, 45, 80, 55, 60, 20]
}],
"grid": {
"top": "25%",
"bottom": "30",
"left": "15%",
"right": "5%"
}
}
从结果来看,合并后的数据是完整的,并且合并后再修改源对象,并没有影响到合并结果,这说明合并拷贝是深拷贝,并不是引用拷贝,是一款不错的 API。但由于 jQuery 库在 node 环境使用还是比较麻烦的,在SPA 应用和移动端的效率也不太高,因此有必要整理一个较高效的库供日常使用。
四、 高效可靠的合并方法
1. 关键代码
经过对数个前端 js 库的对比研究,整理了一个库,关键代码如下:
/**
* @Description 克隆对象
* 能被克隆的对象类型:
* Plain object, Array, TypedArray, number, string, null, undefined.
* 直接用原始数据进行赋值的数据类型:
* BUILTIN_OBJECT
* 用户定义类的实例将克隆到一个原型中没有属性的普通对象。
* @method clone
* @param {*} source
* @return {*} new
*/
clone(source) {
if (source == null || typeof source !== 'object') {
return source;
}
var result = source;
var typeStr = this.objToString.call(source);
if (typeStr === '[object Date]') {
result = this.cloneDate(source);
}
else if (typeStr === '[object RegExp]') {
result = this.cloneRegExp(source);
}
else if (typeStr === '[object Function]') {
result = this.cloneFunction(source);
}
else if (typeStr === '[object Array]') {
result = [];
for (var i = 0, len = source.length; i < len; i++) {
result[i] = this.clone(source[i]);
}
}
else if (this.TYPED_ARRAY[typeStr]) {
var Ctor = source.constructor;
if (source.constructor.from) {
result = Ctor.from(source);
}
else {
result = new Ctor(source.length);
for (var i = 0, len = source.length; i < len; i++) {
result[i] = this.clone(source[i]);
}
}
}
else if (!this.BUILTIN_OBJECT[typeStr] && !this.isDom(source)) {
result = {};
for (var key in source) {
if (this.hasOwn(source, key)) {
result[key] = this.clone(source[key]);
}
}
}
return result;
},
/**
* @Description 合并函数
* @method merge
* @param {*} target
* @param {*} source
* @param {boolean} [overwrite=false]
* @return {Object}
*/
merge(target, source, overwrite) {
// We should escapse that source is string
// and enter for ... in ...
if (!this.isObject(source) || !this.isObject(target)) {
return overwrite ? this.clone(source) : target;
}
for (var key in source) {
if (this.hasOwn(source, key)) {
var targetProp = target[key];
var sourceProp = source[key];
if (this.isObject(sourceProp)
&& this.isObject(targetProp)
&& !this.isDom(sourceProp)
&& !this.isDom(targetProp)
&& !this.isBuiltInObject(sourceProp)
&& !this.isBuiltInObject(targetProp)
) {
// 如果需要递归覆盖,就递归调用merge
this.merge(targetProp, sourceProp, overwrite);
}
else if (overwrite || !(key in target)) {
// 否则只处理overwrite为true,或者在目标对象中没有此属性的情况
// NOTE,在 target[key] 不存在的时候也是直接覆盖
target[key] = this.clone(source[key], true);
}
}
}
return target;
}
2. 完整代码与库
完整代码在 http://192.168.x.y/z/merge-util,代码已经经过处理,兼容 node 环境和浏览器环境,npm 包已经发布到公司内部库:http://192.168.x.y:z/#browse/browse:merge-util。
五、 使用方法
1. Node环境
使用 npm 命令用临时 registry 安装包:
npm --registry http://192.168.x.y:z/my-repository/ install merge-util
或设置持久 registry 再安装包:
npm config set registry http://192.168.x.y:z/my-repository/
npm install merge-util
const mergeUtil = require('merge-util');
let result = mergeUtil.mergeAll([{},chartScheme, chartStyle, chartStyle2, chartData], true);
console.log(JSON.stringify( result));
2. 浏览器环境
通过上述 npm 命令将安装包下载到本地,或从 github 下载 index.js 文件,然后在 html 文件里引用即可使用:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Merge-test</title>
<script src="./rs-merge-util.js"></script>
<script src="./testdata.js"></script>
</head>
<body>
<script>
const result = mergeUtil.mergeAll([{}, chartScheme, chartStyle, chartStyle2, chartData0, chartData], true);
console.log(JSON.stringify(result));
</script>
</body>
</html>
合并JSON对象的正确方式的更多相关文章
- jq 合并json对象
一,保存object1和2合并后产生新对象,若2中有与1相同的key,默认2将会覆盖1的值 1 var object = $.extend({}, object1, object2); 二,将2的值合 ...
- JSON字符串转换为JSON对象
一.JSON字符串转换为JSON对象 A:eval函数 eval函数可以直接将本质符合或者近似符合JSON格式的字符串转换为JSON对象,使用方式如: eval('(' + str + ')'); / ...
- json对象和json字符串转换方法
在WEB数据传输过程中,json是以文本,即字符串的轻量级形式传递的,而客户端一般用JS操作的是接收到的JSON对象,所以,JSON对象和JSON字符串之间的相互转换.JSON数据的解析是关键. 先明 ...
- 自定义 JSON 对象
针对 IE9 以下不支持 JSON 对象的处理方式,网上大部分自定义的方式无形之中都会将中文转码为 Unicode 编码格式的字符换,但是在浏览器中我们有无法察觉到(浏览器自己解析成 UTF8 了), ...
- MVC中用ajax提交json对象数组
应用场景:在前端用ajax向服务器提交json对象数组,在controller的以对象数组作为函数的参数,提交的json数组直接转为服务器端的对象数组. 如: 要将json对象数组[{Id:1,Nam ...
- js如何创建JSON对象
js如何创建JSON对象 一.总结 一句话总结:直接创建js数组和js对象即可,然后JSON.stringify就可以获取json字符串,js中的一切都是对象,而且js中的对象都是json对象 js ...
- 字符串和JSON对象互转的方法
采用Ajax的项目开发过程中,经常需要将JSON格式的字符串返回到前端,前端解析成JS对象(JSON ).字符串转JSON对象 1.eval方式解析.function strToJson(str){ ...
- pageParam要求传个JSON对象的方法
pageParam要求传个JSON对象,使用方式:api.openWin({name: 'page1',url: 'page1.html',pageParam: {x: '1000',y: '2000 ...
- Json对象和字符串互相转换 数据拼接 JSON使用方式
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式. 一.JSON字符串转换为JSON对象: eval() 和 JSON.parse eg- json字符串: ...
随机推荐
- css 实现图片灰度
先看效果鼠标移入图片中摁下向左移动 图片由灰度变为原图 向右移动原图变灰度 代码如下:尚未做优化 <style> *{ margin:0; padding:0; } #img{ w ...
- sql中的 where 、group by 和 having 用法解析
--sql中的 where .group by 和 having 用法解析 --如果要用到group by 一般用到的就是“每这个字” 例如说明现在有一个这样的表:每个部门有多少人 就要用到分组的技术 ...
- Spring入门(十五):使用Spring JDBC操作数据库
在本系列的之前博客中,我们从没有讲解过操作数据库的方法,但是在实际的工作中,几乎所有的系统都离不开数据的持久化,所以掌握操作数据库的使用方法就非常重要. 在Spring中,操作数据库有很多种方法,我们 ...
- BF算法(蛮力匹配)
输入主串a,模式b b在a中的位置 1.在串a和串b中设置比较的下标i=0,j=0: 2.重复下述操作,直到a或b的所有字符均比较完毕: 2.1如果a[i]等于b[i],继续比较a和b的下一对字符: ...
- Python数据分析入门与实践 ✌✌
Python数据分析入门与实践 (一个人学习或许会很枯燥,但是寻找更多志同道合的朋友一起,学习将会变得更加有意义✌✌) 这是一个数据驱动的时代,想要从事机器学习.人工智能.数据挖掘等前沿技术,都离不开 ...
- 你也可以写个服务器 - C# Socket学习2
前言 这里说的服务器是Web服务器,是类似IIS.Tomcat之类的,用来响应浏览器请求的服务. Socket模拟浏览器的Url Get请求 首先浏览器的请求是HTTP协议.我们上一篇说过,HTTP是 ...
- DJango错误日志生成
DJango错误日志生成 setting.py设置 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': ...
- 经典的Redis的主从复制搭建
##### 配置服务器 1).打开redis.conf文件修改 bind 指定的ip地址: 》读blog笔记
免责声明 原文地址https://blog.csdn.net/linjiayang2016/article/details/80341958,作者linjiayang2016.\text{linjia ...