JavaScript 深浅拷贝

深浅拷贝(deep copy, shallow copy)

深浅拷贝的基础知识是关于值类型和引用类型的区分,可参考《JavaScript 数据类型(值类型/引用类型)》一文

(1)什么是拷贝?
注意:一定在内存中有两个数据副本才是拷贝
问题:
var num1 = 123;
var num2 = num1; // 这里内存中数据123有两个副本,因此是拷贝
var obj1 = { name: 'jim' };
var obj2 = obj1; //这里内存中数据只有一个,没有拷贝,因此这不是拷贝

(2) 深拷贝
拷贝对象的所有数据,两个数据副本在内存中完全独立,就是深拷贝

(3) 浅拷贝(相对复杂些)
拷贝的对象不完全,数据副本在内存中还有关联,就是浅拷贝

(4)理解深浅拷贝
深浅拷贝只有在对象含有引用类型的成员时才考虑。
function Person( name ) {
 this.name = name;
 this.copy = function () {
  var tmp = new Person( this.name );
  return tmp;
 }
}
var p1 = new Person( '张三' );
var p2 = p1.copy();
// 这里没有深浅拷贝之分

浅拷贝:
// 浅拷贝
function Person( name ) {
    this.name = name;
    this.car = null;
    this.shallowCopy = function () {
        var tmp = new Person(); //拷贝出来的副本
        for( var k in this ) {
            tmp[ k ] = this[ k ];   // 如果是值类型, 就直接拷贝了, 如果是引用类型, 就没有拷贝
        }
        return tmp;
    };
}
function Car ( name ) {
    this.name = name;
} 
var p1 = new Person( '李四' );
p1.car = new Car( '劳斯莱斯' );
var p2 = p1.shallowCopy();

深拷贝:
// 深拷贝
// 1> 首先有一个函数, 该函数的特点是 深拷贝一个对象, 并将对象的拷贝结果返回
// 2> 在函数内部实现算法, 首先准备一个对象
// 3> 遍历目标对象中的所有属性
// 4> 判断属性是否为值类型, 如果是值类型直接赋值
// 5> 如果是引用类型:
//     -> 再准备一个对象
//     -> 再遍历这个属性
//     -> ...
//     => 如果是引用类型的对象, 就调用一次自己这个方法
function deepCopyHandler( obj ) {
 // 深拷贝 obj 返回新对象
 var tmp = {};
 for ( var k in obj ) {
  if ( typeof obj[ k ] == 'object' ) {
   // 深拷贝 obj[ k ],递归
   tmp[ k ] = deepCopyHandler( obj[ k ] );
  } else {
   tmp[ k ] = obj[ k ];
  }
 }
 return tmp;
}
function Person( name ) {
 this.name = name;
 this.car = null;
 this.deepCopy = function () {
  return deepCopyHandler( this );
 }; 
}
function Car ( name ) {
 this.name = name;
} 
var p1 = new Person( '李四' );
p1.car = new Car( '劳斯莱斯' );
var p2 = p1.deepCopy();

(5)例:为了兼容ie8,使用递归的方法实现 getElementsByClassName方法
/**
 * 递归查找有指定类名的元素(进阶方法)
 *
 * 可以不新建空数组,也不返回,而将空数组作为参数传入函数
 * 且在一开始就限定必须传数组,否则就抛出异常
 * 这样的好处是,省去了拼接数组的过程,因为传入的一直是同一个数组
 *
 * @param className 指定类名
 * @param tag 总元素
 * @returns {Array}
 */
function getByClass(className,arr,tag){
    if(typeof arr == 'undefined' || typeof arr.push != 'function'){
        throw new Error('传入参数不正确!');
    }

    tag = tag || document;//没传tag就用document元素
    var nodes = tag.childNodes;//总元素下的所有子节点

    /*遍历*/
    for(var i=0; i -1){
                arr.push(nodes[i]);//推进数组里
            }
            //递归,接着查下一层子元素,找到就推进数组中
            getByClass(className,arr,nodes[i]);
        }
    }
}

//使用该方法
var divs = [];
getByClass('c',divs);//该函数没有返回值,只能执行后从参数中得到结果
alert(divs.length);
for(var i=0; i

(6)扩展:
深度拷贝对象时,类型不重要,方法是重要的
函数,就是应该共享
遍历的时候应该只考虑当前对象的成员,不考虑原型中的成员(用object.prototype中提供的hasOwnProperty来判断)
如果是数组或伪数组,最好不要一开始就创建{},而是创建[]较好







你可能感兴趣的:(JavaScript,javascript,拷贝)