对数组、对象进行深复制

大家曾否想过这样的问题:当你要对一个数组,或者对象进行复制的时候,怎么进行复制?
我们都知道,除了三种基本的数据类型外(Number,Boolean,String)的数据赋给另外一个变量的时候,这样该变量得到的不是这些数据的副本,而是一个指向该数据的引用,它的变化,会影响原来的数据对象。这可怎么得到这种数据对象的引用呢?
看下面的代码:

var arr1 = [ [1,2,3] , [1,2,3] , [1,2,3] ];
var arr2 = arr1;

//arr2的改变会使arr1也同时改变
arr2[0] = 'replaced';
// arr2 = [ 'replaced' , [1,2,3] , [1,2,3] ];
// arr1 = [ 'replaced' , [1,2,3] , [1,2,3] ];
// arr2 === arr1; // This is TRUE

那下面用我们平时的做法来重新声明一个数组,通过循环来赋值:

var theCopy = []; // An new empty array
for (var i = 0, len = arr1.length; i < len; i++) {
    theCopy[i] = arr1[i];
}

// &amp;quot;theCopy&amp;quot; = [ 'replaced' , [1,2,3] , [1,2,3] ]

但是大家注意到没有,arr1的第二和第三个元素还是一个数组,这样theCopy的第二第三个元素还是得到了arr1中相应位置的数组元素的引用,还是有关联。还没有实现深度赋值。接着看下面的代码:

function deepCopy(obj){
    //如果obj是数组
    if (Object.prototype.toString.call(obj) === '[object Array]') {
        var out = [], i = 0, len = obj.length;
        for (; i < len; i++) {
            //arguments.callee == deepCopy
            //当然可以用deepCopy代替arguments.callee
            out[i] = arguments.callee(obj[i]);
        }
        return out;
    }
    //如果是对象
    if (typeof obj === 'object') {
        var out = {}, i;
        for (i in obj) {
            out[i] = arguments.callee(obj[i]);
        }
        return out;
    }
    return obj;
}

上面的deepCopy才真正的实现了深度复制,通过递归的技巧来循环遍历数组或者对象的每一个属性。这样就能够深入到数组或者对象的“里面”,实现真正的深度复制。

有些人或许会问这个方法有什么实际使用价值,当然,这样做的人其实不多,但是当你需要使用一个已经存在的数组里的数据,而又不想因为操作这些数据的时候改变原来数组的元素内容,这个时候你就需要得到数组的副本,而不是引用了。

享元类ShareClass

今天打开自己以前编写过的一些Javascript代码,看到了一段关于享元类的代码,觉得思路不错,就黏上来分享一下,体现一个本人的淫格,:)

“享元类”这个概念和思维我也是从《Javascript设计模式》这本书里得知的,实现方法虽然很简单,可是它所体现出来的思维是不错的:通过将一个类的方法传递给另外的一个类,而且可以有选择的来传递。这个思路或许也不新鲜了,可是从这里扩展开来,就给予了我们一种非常好的思维方式了,比如目前流行的“模块编写方式”,模块于模块之间的方法共通以及集合数组方式添加方法到类里面都是可行的,不用通过prototpe的方式来写每一个方法属性。享元类的思路就是将通用的方法组合起来到一个类里面,之后哪一个对象或者类想要这些方法了,就可以扩展到那里了。同时这也是减少代码编写量的一种不错的方式吧。

我参考了书中的享元类的设计方式,扩展到第二个参数可以是对象,也可以是类,具体请看下面的代码:

//将指定的类或者对象全部的方法添加到第一个参数的类里面。
var ShareClass = function(classObj){ //享元类
    var a = arguments;
    //如果存在第二个参数,则将第二个参数内指定的obj或者class类内的属性和方法传递给第一个参数内的类
    //注意:第一个参数需要为类,当然,可以做判断,再分支写代码,这些都是类似来实现的,就不多写了
    if (a[1]) {
        //第二个参数为对象或者类都可以
        var refObj = a[1].prototype === undefined ? a[1] : a[1].prototype;
        for (var o in refObj) {
            //如果classObj类里已经存在了该方法,将会被覆盖
            classObj.prototype[o] = refObj[o];
            /*
             * 做判断,如果classObj类里已经存在了该方法,则跳过,这样将不会被覆盖
             * if(classObj.prototype[o]){
             *      continue;
             * }
             * classObj.prototype[o] = a[1][o];
             */
        }
    }
    return classObj;
};

上面的ShareClass类基本说明了我上面所要表达的意思,但是上面的ShareClass还缺少一个功能:就是有选择的传递方法属性。这个也是比较关键的,提供了更多的选择。请接着看下面的代码:

//可以将指定的方法从一个对象或者类传递给另外一个类。
var ShareClass = function(classObj){ //享元类
    var a = arguments;
	//如果参数只有两个,则将第二个参数的类或者对象的全部的方法传递给第一个参数的类
    if (!argument[2]) {
        for (var i = 1, n = a.length; i < n; i++) {
            var refObj = a[1].prototype === undefined ? a[1] : a[1].prototype;
            for (var o in a[i]) {
                obj[o] = a[i][o];
            }
        }
    } else {    //如果存在第三个参数,则表示从类或者对象中指定的方法添加到另一个类里面
        var refObj = a[1].prototype === undefined ? a[1] : a[1].prototype;
        for (var i = 2, l = a.length; i < l; i++) {
			if(refObj[a[i]]){
				classObj.prototype[a[i]]=refObj[a[i]];
			}else{
				throw new Error("There is no this method in the class or object");
			}
        }
    }
	return classObj;
};

经过改进后的ShareClass就可以有选择的来传递方法属性了,:)