简单的js对象复制

一、前言

javascript 中的对象(Object),和其它编程语言中的对象一样,可以比照现实生活中的对象(物体)来理解它。 javascript 中对象(物体)的概念可以比照着现实生活中实实在在的物体来理解。
在javascript中,一个对象可以是一个单独的拥有属性和类型的实体。我们拿它和一个杯子做下类比。一个杯子是一个对象(物体),拥有属性。杯子有颜色,图案,重量,由什么材质构成等等。同样,javascript对象也有属性来定义它的特征。

以上定义来自MDN,有兴趣的可以MDN了解更多关于js对象的相关知识,本篇文章做的是一个对json对象的拷贝,涉及的内容比较简单。
本来是考虑实现lodash中的clone函数的,但是一看lodash中的源码,我放弃了,lodash中涉及所有js对象的拷贝,涉及Map,Set等等
对象的拷贝。实现的方式很值得学习,后期会做一期lodash中_.clone函数的解析。

二、构建拷贝函数

/**
 * 这是我能想到的最简单的方法了
 * @param value
 * @returns {Object} return clone result
 *
 * @Example
 * //return {a:1,b:2}
 * var obj={a:1,b:2}
 * clone(obj)
 */
function clone(value) {
    var result = {};
    for (key in value) {
        result[key] = value[key]
    }
    return result;
}

这是最简单的一种拷贝对象的函数了,这只能完成最简单的字面量的拷贝,并且还有很多的缺点,比如我们如果不是传入一个对象,就会报错,接下来我们对这个函数进行优化

2.1、判断参数是否是对象

/**
 * 判断参数是否是对象,在js中可以用函数创建对象,所以,type为function的也可能是对象。
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 * @example
 *
 * _.isObject({});
 * // => true
 *
 * _.isObject(null);
 * // => false
 */
function isObject(value) {
    var type = typeof value;
    return value != null && (type == 'object' || type == 'function');
}

这样我们就对传入的参数进行了一个是否是对象的判断,我们的clone函数变成了下面这样子

function clone(value) {
    var result = {};
    if (!isObject(value)) {
        return value
    }
    for (key in value) {
        result[key] = value[key]
    }
    return result;
}

2.2、枚举对象的所有属性

clone函数中,我们用for...in来枚举对象的属性,但从 ECMAScript 5 开始,有三种原生的方法用于列出或枚举对象的属性:

  • for…in 循环
    该方法依次访问一个对象及其原型链中所有可枚举的属性。
  • Object.keys(o)
    该方法返回一个对象 o 自身包含(不包括原型中)的所有属性的名称的数组。
  • Object.getOwnPropertyNames(o)
    该方法返回一个数组,它包含了对象 o 所有拥有的属性(无论是否可枚举)的名称。

以上引用自MDN。

本文只讨论json对象的深拷贝,所以我们直接用for...in就可以了,如果对于有构造函数的对象,我们就必须通过对象的原型链构造一个新对象。
本文的对象就是简单的var result={}等价于var result=new Object(),如果我们有个名为Foo的对象,我们想复制这个对象的实例,那么对复制对象的申明就是var result=new Foo()

/**
  *利用对象的实例,来创建一个该对象的空实例。这个
  *实例原型链指向原对象的原型链。
  *createObject的实现方式可以参考MDN上的Object.create()相关文章。
  */
function clone(value) {
    var result = createObject(value);
    if (!isObject(value)) {
        return value
    }
    for (key in value) {
        result[key] = value[key]
    }
    return result;
}

这样我们就实现了,对于有构造函数对象的拷贝,虽然本文并不需要,因为json对象的构造函数就是Object(),可以说是众多构造函数中的一个而已,但也可以触类旁通的了解其他拷贝的实现。

现在我们枚举对象的的属性,也就是获取key的数组,直接用for...in来实现

var objectProto = Object.prototype;
var hasOwnProperty = objectProto.hasOwnProperty; //判断对象是否有这个属性。
function getKeys(object){
    var result = [];
    for (key in object) {
        if (hasOwnProperty.call(object, key)) {
            result.push(key);
        }
    }
    return result
};

2.3、遍历可枚举属性实现拷贝

function clone(value) {
    var result={},
        keys = []
    if (!isObject(value)) {
        return value
    }
    keys = getKeys(value)
    for (var i = 0; i < keys.length; i++) {
        var prop = keys[i]
        result[prop] = value[prop]
    }
    return result;
}

这就简单的实现了对对象的拷贝,但一般来说仅仅这样还是不够的,因为对象是有层级的,我们可以用递归来实现这样的对象的遍历

/**
  * 直接在值作为参数传给自身就行了。这样就实现的递归的遍历
  */
function baseClone(value) {
    var result={},
        keys = []
    if (!isObject(value)) {
        return value
    }
    keys = getKeys(value)
    for (var i = 0; i < keys.length; i++) {
        var prop = keys[i]
        result[prop] = baseClone(value[prop])
    }
    return result;
}
function clone(value){
	return baseClone(value)
}

2.4、对象数组的支持

一般来说,我们处理的数据,基本上都是列表之类的,如下:

[
	{name:'zhangsan',age:23,parent:[
		{name:'xiaoming',age:54},
		{name:'xiaohong',age:53}
	]},
	{name:'lisi',age:23,parent:[
		{name:'xiaoming',age:54},
		{name:'xiaohong',age:53}
	]}
]

处理这样的数据我们的拷贝函数就无能为力了,为了实用性,我们填上一笔,考虑对象数组复制

/**
  * 简单的处理了下,数组的复制,
  */
function copyArray(source,result){
	var index = -1,
		length = source.length

	result || (result = Array(length))
	while (++index < length) {
		result[index] = baseClone(source[index])
	}
	return result;
}

/**
  * 简单写下isArray函数
  */
function isArray(value) {
    return value instanceof Array
}

function baseClone(value) {
    var result,
        keys = []
    if (!isObject(value)) {
        result=value
    }else if(isArray(value)){
		result=[]
		return copyArray(value,result)
	}else{
		result={}
		keys = getKeys(value)
		for (var i = 0; i < keys.length; i++) {
			var prop = keys[i]
			result[prop] = baseClone(value[prop])
		}
	}
    return result;
}
function clone(value){
	return baseClone(value)
}

OK,到这里,我们已经完成了,对象的深拷贝,复制出来的对象来,完全是一个新的对象,
不会影响原对象的值。

三、小结

这个就是一个基础的对象深拷贝函数。

四、参考

Object - JavaScript | MDN
lodash gitbub 传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值