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;
}
这是最简单的一种拷贝对象的函数了,这只能完成最简单的字面量的拷贝,并且还有很多的缺点,比如我们如果不是传入一个对象,就会报错,接下来我们对这个函数进行优化
/**
* 判断参数是否是对象,在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;
}
在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
};
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)
}
一般来说,我们处理的数据,基本上都是列表之类的,如下:
[
{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 传送门