[整理参考文章] Javascript 传值传址&深浅拷贝

前言

因为公司前端资料已经处理成单层结构,所以都没注意到浅拷贝、深拷贝的实际差别。
在读完高手文章后,才发现和自己想的不一样。
也顺手将文章重点整理,分享给大家,别枉费自己写这么多Codehttp://img2.58codes.com/2024/emoticon02.gif

重点

基本型别 ⇒ 传值。stringnumberbooleannullundefined物件型别 ⇒ 传址浅拷贝 Shallow Copy ⇒ 物件若有多层,第一层之下的物件会以传址拷贝。物件的浅拷贝方法Object.assign()...阵列的浅拷贝方法array.slice()array.concat()array.map()array.filter()forEach +push()...深拷贝 Deep Copy ⇒ 返回一个全新的物件且不与被複製物件有关係。物件、阵列深拷贝方式JSON.parse(JSON.stringify())第三方函示库,Lodash ⇒ _.cloneDeep() ...等等

传值

var a = 10;var b = 10;console.log(a === b); // true

ab皆为基本型别,故互相比较时回传true

传址

var obj1 = { a : 1 };var obj2 = { a : 1 };console.log(obj1 === obj2); // false

虽然物件内容相等,但是物件的记忆体位置却不同,故比较结果为false

不同的例子

var obj1 = { a : 1 };var obj2 = obj1;obj1.a = 0;console.log(obj1.a); // 0console.log(obj2.a); // 0obj2.a = 2;console.log(obj1.a); // 2console.log(obj2.a); // 2console.log(obj1 === obj2); // true

当我们修改任何一边的属性时,会一起变动,这是因为物件透过传址的方式指派,所以两个物件会指向同一个记忆体位置,实际上也意味着并未产生新的物件。

但若是这样呢?

var obj1 = { a : 1 };var obj2 = obj1;obj1.a = 0;console.log(obj1.a); // 0console.log(obj2.a); // 0obj2.a = 2;console.log(obj1.a); // 2console.log(obj2.a); // 2obj1 = {}; // 指派新的物件console.log(obj1 === obj2); // false

obj1 被指向新的记忆体位置,但是obj2 依然保持原本的记忆体位置,因此这时obj1obj2 彼此就毫无关係了。

複製物件

複製物件可以分为两种

浅拷贝 Shallow Copy深拷贝 Deep Copy

浅拷贝

物件的浅拷贝

Object.assign()展开运算子...

Object.assign(target, source) 能複製一个或多个物件自身所有可数的属性到另一个目标物件。

将原本的obj 内容複製到另一个空物件

var obj = { a : 1, b : 2 };var obj2 = Object.assign({}, obj);console.log(obj2); // { a : 1, b : 2 }console.log(obj == obj2); // false

展开运算子

var obj = { a : 1, b : 2 };var obj2 = { ...obj1 };console.log(obj2); // { a : 1, b : 2 }console.log(obj === obj2); // false

阵列的浅拷贝

array.slice()array.concat()array.map()array.filter()forEach +push()...

slice() 原本是用在分割阵列,但用参数为0或不传入的话,相当于浅拷贝

var arr = [1, 2, 3, 4];var arr2 = arr;console.log(arr === arr2); // truevar arr3 = arr.slice(0); console.log(arr === arr3); // falseconsole.log(arr3); // [1, 2, 3, 4]

concat() 原本是用在组合阵列,但可以使用空阵列合併,也相当于浅拷贝

var arr = [1, 2, 3, 4];var arr2 = [].concat(arr);console.log(arr === arr2); // falseconsoloe.log(arr2); // [1, 2, 3, 4]

map() 是将执行结果会存至新阵列,若回传原本的元素,也相于浅拷贝

var arr = [1, 2, 3, 4];var arr2 = arr.map(x => x);console.log(arr === arr2); // falseconsole.log(arr2); // [1, 2, 3, 4]

filter 是将符合条件的值存至新阵列,若条件皆为true ,也相当于浅拷贝

var arr = [1, 2, 3, 4];var arr2 = arr.filter(x => { return true; } );console.log(arr === arr2); // falseconsole.log(arr2); // [1, 2, 3, 4]

... 也可用于阵列,效果也是浅拷贝

var arr = [1, 2, 3, 4];var arr2 = [ ...arr ];console.log(arr === arr2); // falseconsole.log(arr2); // [1, 2, 3, 4]

至于为什么叫浅拷贝,可以看以下程式码

var obj1 = {  foo : 10,  bar : {    baz : 20,  },};var obj2 = { ...obj1 };console.log(obj1 === obj2); // falseconsole.log(obj1.bar === obj2.bar); // true

浅拷贝后,obj1obj2 已经是两个不同的物件,但是第二层的物件却是相同的记忆体位置。

再看看阵列

var arr = [{a : 1}, {b : 2}];var arr2 = [...arr];console.log(arr === arr2); // falseconsole.log(arr[0] === arr2[0]); // true

浅拷贝后,arrarr2 已经是不同的阵列,但是里面的物件却是同一个。

上述只是有容器不同,若有巢状或多维的状况,仍然是传址。因为深层的物件或阵列还是传址,不会完全複製一份,所以称为『浅拷贝』。

深拷贝

深拷贝是完全複製一份新的,不会共用记忆体位置。

Json.parse(Json.stringify())第三方函示库,Lodash ⇒ _.cloneDeep() ...等等

利用JSON

var obj1 = {a : 1,b : {val: 5}};var obj2 = JSON.parse(JSON.stringify(obj1));console.log(obj1 === obj2); // falseconsole.log(obj1.b === obj2.b); // false

但JSON的转法须注意

函式、undefinedSymbol 会被忽略转换NaNInfinity 会被转换成 null
var obj1 = {  a: function() {},  b: undefined,  c: Symbol(''),  d: NaN,  e: Infinity,  f: -Infinity,};var obj2 = JSON.parse(JSON.stringify(obj1));console.log(obj2);  // {d: null, e: null, f: null}

参考

参考文章


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章