JavaScript 实现深拷贝
2014-08-27 17:27:56
写该系列的起因是在网上遇到一套比较不错的应试题目,涉及到了前端的各个角落,以及模块化,HTTP,后端等。题目在此前端题目
第一题,就来讨论一下关于 JS 实现深拷贝的问题。
这是简单的对象引用,这仅仅是将对象的引用地址简单的复制了一份给予变量 obj2,而并不是将真正的对象克隆了一份,原对象依旧是只有一个。影响则为,当我修改 obj2 内部的属性或者添加新属性时会影响 obj ,因为两个变量的值为同一个对象的地址引用,即两者指向的是同一个对象。
这就是所谓深拷贝的意义所在,不是拷贝引用地址,而是实实在在的复制一份新对象。而目前标准中还未提供一个类似的原生方法(主要是由于需求不大,JS 本身已经可以处理的很好了)。所以对于深拷贝,主要遇到的问题就是对于数组,对象以及方法的复制。
对象:一般来说不考虑原型链上对象的属性,均是使用 for in 遍历并且使用,由于浏览器的五花八门,还需要使用 hasOwnPrototype 来进行过滤下。当然也需要考虑某属性值为 对象,数组,函数等情况。
数组:对于数组来说,也可以如对象一样 for in 循环遍历,不过通常情况是使用长度进行循环,对空数组进行不断的 push。
函数:对于一般库来说,一些深拷贝函数基本都不进行处理,实在需要处理的可以调用函数的 toString,再使用 eval 或者 Function 进行处理,得到新的函数。
然后上个实例吧,是自己正在写的库中的简单的一个拷贝方法 mix,由于库的原因扩展了一些功能以及对于方法不做复制处理,使用外部的一个方法 type,是用来判断区分类型,如 array object function。
对于 type 函数,源代码如下
这就是简单的对于 JS 实现深拷贝的例子,有什么错误,欢迎指出。
分类: JavaScript
写该系列的起因是在网上遇到一套比较不错的应试题目,涉及到了前端的各个角落,以及模块化,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|| !(keyin receiver)){
- value= obj[key];
- valueType= type(value);
- if( deep&&( valueType==="object")){
- receiver[key]={};
- mix(receiver[key],value, ride, deep);
- }elseif( 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){
- varkey = ((obj==null || obj!== obj) ? obj +"" : Object.prototype.toString.call( obj )),
- result;
-
- if(typeof(result= class2type[key ]) !=="string" ){
- if( obj.nodeType=== 9){
- result = class2type["Document"];
- }elseif( obj.item&&typeof obj.length==="number" ){
- result = class2type["NodeList"];
- }else{
- result =key.slice(8,-1);
- }
- }
- if( isType){
- return result=== isType.toLowerCase;
- }
- return result;
- }
这就是简单的对于 JS 实现深拷贝的例子,有什么错误,欢迎指出。