写该系列的起因是在网上遇到一套比较不错的应试题目,涉及到了前端的各个角落,以及模块化,HTTP,后端等。题目在此
第一题,就来讨论一下关于 JS 实现深拷贝的问题。
-
var obj = { a: 1 };
-
var obj2 = obj;
-
-
obj2.a = 3;
-
obj.a; // 3
这是简单的对象引用,这仅仅是将对象的引用地址简单的复制了一份给予变量 obj2,而并不是将真正的对象克隆了一份,原对象依旧是只有一个。影响则为,当我修改 obj2 内部的属性或者添加新属性时会影响 obj ,因为两个变量的值为同一个对象的地址引用,即两者指向的是同一个对象。
这就是所谓深拷贝的意义所在,不是拷贝引用地址,而是实实在在的复制一份新对象。而目前标准中还未提供一个类似的原生方法(主要是由于需求不大,JS 本身已经可以处理的很好了)。所以对于深拷贝,主要遇到的问题就是对于数组,对象以及方法的复制。
对象:一般来说不考虑原型链上对象的属性,均是使用 for in 遍历并且使用,由于浏览器的五花八门,还需要使用 hasOwnPrototype 来进行过滤下。当然也需要考虑某属性值为 对象,数组,函数等情况。
数组:对于数组来说,也可以如对象一样 for in 循环遍历,不过通常情况是使用长度进行循环,对空数组进行不断的 push。
函数:对于一般库来说,一些深拷贝函数基本都不进行处理,实在需要处理的可以调用函数的 toString,再使用 eval 或者 Function 进行处理,得到新的函数。
然后上个实例吧,是自己正在写的库中的简单的一个拷贝方法 mix,由于库的原因扩展了一些功能以及对于方法不做复制处理,使用外部的一个方法 type,是用来判断区分类型,如 array object function。
-
/** 对象扩展 mix
-
*
-
* @method mix 不扩展原型属性
-
* @param {obj} receiver 可选 扩展的目标对象 如果无 则扩展到外围对象
-
* @param {obj} obj 必选 要扩展到目标对象的对象数据
-
* @param {boolean} ride 可选 主要是标识是否覆盖原有对象属性 默认为true
-
* @param {boolean} deep 可选 主要是标识是否需要简单的深度拷贝 默认为false
-
*
-
* @return {Object} 返回目标对象
-
*
-
*/
-
var hasOwn = Object.prototype.hasOwnProperty;
-
function mix(receiver, obj){
-
var args = [].slice.call(arguments), key, i = 1, deep, ride, value, valueType;
-
-
if( typeof args[args.length-2] === "boolean" ){
-
deep = args.pop();
-
ride = args.pop();
-
}else{
-
ride = (typeof args[args.length-1] === "boolean")?args.pop():true;
-
deep = false;
-
if(args.length < 2){
-
receiver = ( this !== global ) ? this : {};
-
if( args.length === 0 ){
-
return receiver;
-
}
-
}
-
}
-
-
while( obj = args[ i++ ] ){
-
for( key in obj ){
-
if( hasOwn.call(obj, key) ){
-
if( ride || !(key in receiver) ){
-
value = obj[key];
-
valueType = type(value);
-
if( deep && ( valueType==="object")){
-
receiver[key]={};
-
mix(receiver[key], value, ride, deep);
-
}else if( deep && ( valueType==="array" )){
-
receiver[key]=[];
-
mix(receiver[key], value, ride, deep);
-
}else{
-
receiver[key] = obj[key];
-
}
-
}
-
}
-
}
-
}
-
return receiver;
-
}
对于 type 函数,源代码如下
-
// 类型判定对象
-
var class2type = {
-
"[objectHTMLDocument]" : "document",
-
"[objectHTMLCollection]" : "nodeList",
-
"[objectStaticNodeList]" : "nodeList",
-
"[objectIXMLDOMNodeList]" : "nodeList",
-
"null" : "null",
-
"NaN" : "NaN",
-
"undefined" : "undefined"
-
};
-
-
"Boolean, Number, String, Function, Array, Date, RegExp, Document, Arguments, NodeList"
-
.replace( /[^, ]+/g, function( type ){
-
class2type["[object " + type + "]"] = type.toLowerCase();
-
} );
-
-
// 类型判定
-
function type( obj, isType ){
-
var key = ((obj == null || obj !== obj ) ? obj + "" : Object.prototype.toString.call( obj )),
-
result;
-
-
if( typeof(result = class2type[ key ]) !== "string" ){
-
if( obj.nodeType === 9 ){
-
result = class2type["Document"];
-
}else if( obj.item && typeof obj.length === "number" ){
-
result = class2type["NodeList"];
-
}else{
-
result = key.slice(8, -1);
-
}
-
}
-
-
-
if( isType ){
-
return result === isType.toLowerCase;
-
}
-
-
-
return result;
-
}
这就是简单的对于 JS 实现深拷贝的例子,有什么错误,欢迎指出。
阅读(10514) | 评论(0) | 转发(3) |