JavaScript 深浅拷贝

来源:互联网 发布:淘宝diy电脑哪家好 编辑:程序博客网 时间:2024/05/20 00:38

深浅拷贝(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<nodes.length; i++){        //找到元素节点        if(nodes[i].nodeType == 1){            //找到有类名且包含指定类名的元素            if(nodes[i].className && (' '+nodes[i].className+' ').indexOf(' '+className+' ') > -1){                arr.push(nodes[i]);//推进数组里            }            //递归,接着查下一层子元素,找到就推进数组中            getByClass(className,arr,nodes[i]);        }    }}//使用该方法var divs = [];getByClass('c',divs);//该函数没有返回值,只能执行后从参数中得到结果alert(divs.length);for(var i=0; i<divs.length; i++){    divs[i].style.borderColor = 'green';}

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







0 0
原创粉丝点击