javascript中的浅复制与深复制

来源:互联网 发布:软件版权声明格式 编辑:程序博客网 时间:2024/05/29 18:04

javascript中的浅复制与深复制

变量基础

ECMAScript变量包含两种不同数据类型的值:基本类型值和引用类型值。基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。

>基本数据类型与引用数据类型的区别

保存方式不同:
Undefined、Null、Boolean、Number、String这5种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。
引用类型的值是保存在内存中的对象,引用类型的值是按引用访问的,但是这种说法不严密,当复制保存着对象的某个变量时,操作的是对象的引用。但是在为对象添加属性的时候,操作的是实际的对象。
复制方式不同:
如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。这两个变量可以参与任何操作而不会相互影响。
当从一个变量向另一个变量复制引用类型的值时,同样会将存储在变量对象中的值复制一份到新变量分配的空间中。不同的是原变量中存储的这个值是一个指针,指向对象所在的堆内存位置,所以新变量的值是一个指针副本,两个变量引用同一个对象。所以对其中一个变量指向的对象进行操作会影响另一个变量指向的对象。

传递参数不同:

传递参数的不同于复制方式不同一样,基本类型的变量将值传递给函数内部参数,该参数的地址与原变量地址不一样,函数的操作不会影响原变量的值。引用类型变量将堆内存的指针传递给函数内部参数,函数对内部参数的操作会影响外部原变量引用的对象。

检测类型不同:

typeof能用于检测基本数据类型,但是检测引用数据类型的时候,除了引用类型是function或者RegExp,一般返回的是Object。引用类型是function返回function,引用类型是RegExp返回function或者object,不同的浏览器返回的不一样。

instanceof返回的boolean值,能用于检测引用类型,但是检测基本类型的时候都返回false。

准确的检测类型用Object.prototype.toString().注意返回的值的大小写。

可以参考https://developer.mozilla.org/enUS/docs/Web/JavaScript/Reference/Global_Objects/Object/toString

浅复制和深复制

基本类型数据和引用类型数据的复制的方式导致存在浅复制和深复制的概念。

浅复制

如下为浅复制的例子:
function extendCopy(p) {    var c = {};    for (var key in p) {        if (p.hasOwnProperty(key)){            c[key] = p[key];        }    }    return c;}var theSameInformation = {    nation:'China',    birthPlace:['beijing','haidian']};
浅复制:
var person1 = extendCopy(theSameInformation);console.log(person1);console.log(theSameInformation);
person1.birthPlace[1]='chaoyang';
person1.nation='USA';console.log(person1);console.log(theSameInformation);
>注意:
person1.birthPlace=[a,b];//此操作导致person1.birthPlace指向的对象不再与theSameInformation.birthPlace相同,而是指向了新创建的数组[a,b]的地址。
person1.nation='USA';//对于基本类型改变的是nation的值
由此,上述两行代码体现了基本类型与引用类型的区别,基本类型是按值传递,引用类型是按
结果:
{ nation: 'China', birthPlace: [ 'beijing', 'haidian' ] }{ nation: 'China', birthPlace: [ 'beijing', 'haidian' ] }{ nation: 'USA', birthPlace: [ 'beijing', 'chaoyang' ] }{ nation: 'China', birthPlace: [ 'beijing', 'chaoyang' ] }
上述操作便是浅复制,定义nation为相对于person1的1层属性,birthPlace[1]为相对于person1的2层属性。
则nation为相对于person1的1层深复制,birthPlace[1]为相对于person1的2层浅复制。

>注意:
若直接用赋值语句:
var person1 = theSameInformation;console.log(person1);console.log(theSameInformation);person1.birthPlace[1]='chaoyang';person1.nation='USA';console.log(person1);console.log(theSameInformation);
结果:
{ nation: 'China', birthPlace: [ 'beijing', 'haidian' ] }{ nation: 'China', birthPlace: [ 'beijing', 'haidian' ] }{ nation: 'USA', birthPlace: [ 'beijing', 'chaoyang' ] }{ nation: 'USA', birthPlace: [ 'beijing', 'chaoyang' ] }
因此上述操作也是浅复制,我定义nation为相对于person1的1层浅复制,birthPlace[1]为相对于person1的2层浅复制。
浅复制除了上述的简单地复制语句之外,还可以利用Object.assign函数,该函数是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性复制给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅复制,复制的是对象的属性的引用,而不是对象本身。
theSameInformation.baseinfo={sex:'male',age:23};//添加属性baseinfovar person2=Object.assign({},theSameInformation);person2.birthPlace[1]='chaoyang';//改变属性为引用类型的birthPlace数组对象中第二个元素的值person2.baseinfo.age=22;//改变属性为引用类型的baseinfo对象中属性age的值person2.nation='USA';//改变属性为基本类型的nation的值console.log(person2);console.log(theSameInformation);
结果:
{ nation: 'USA',  birthPlace: [ 'beijing', 'chaoyang' ],  baseinfo: { sex: 'male', age: 22 } }{ nation: 'China',  birthPlace: [ 'beijing', 'chaoyang' ],  baseinfo: { sex: 'male', age: 22 } }
上述操作实现了nation相对于person1的1层深复制,birthPlace[1]相对于person1的2层浅复制。所以Object.assign() 能实现person1的1层深复制。

深复制

上述的浅复制函数以及Object.assign()函数实际上可以实现只有1层的对象的深复制。实现多层深复制的方法有很多:JSON法,jQuery的$.extend方法,函数库lodash的_.cloneDeep,以及递归拷贝

>JSON法

theSameInformation.fun=function(){};var person3=JSON.parse(JSON.stringify(theSameInformation));person3.birthPlace[1]='changyang';person3.nation='USA';console.log(person3);console.log(theSameInformation);
结果:
{ nation: 'USA', birthPlace: [ 'beijing', 'changyang' ] }{ nation: 'China',  birthPlace: [ 'beijing', 'haidian' ],  fun: [Function] }
该方法的缺点:
1、会抛弃对象theSameInformation的构造函数,复制之后该对象的构造函数变成Object,关于构造函数之后会更新。
2、该方法只适用于可以转成JSON格式的对象,由于JSON.stringify()函数会自动忽略对象中的function,所以类型为function的属性无法进行复制。

>递归拷贝

function deepClone(initalObj, finalObj) {    var obj = finalObj || {};    for (var i in initalObj) {        if (typeof initalObj[i] === 'object') {            obj[i] = (initalObj[i].constructor === Array) ? [] : {};            arguments.callee(initalObj[i], obj[i]);        } else {            obj[i] = initalObj[i];        }    }    return obj;}var person4=deepClone(theSameInformation,{});person4.birthPlace[1]='changyang';person4.nation='USA';console.log(person4);console.log(theSameInformation);
结果:
{ nation: 'USA', birthPlace: [ 'beijing', 'changyang' ] }{ nation: 'China', birthPlace: [ 'beijing', 'haidian' ] }
















原创粉丝点击