日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

php深浅拷贝,JavaScript 中的深浅拷贝

發布時間:2023/12/9 php 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 php深浅拷贝,JavaScript 中的深浅拷贝 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

工作中經常會遇到需要復制 JavaScript 數據的時候,遇到 bug 時實在令人頭疼;面試中也經常會被問到如何實現一個數據的深淺拷貝,但是你對其中的原理清晰嗎?一起來看一下吧!

一、為什么會有深淺拷貝

想要更加透徹的理解為什么 JavaScript 會有深淺拷貝,需要先了解下 JavaScript 的數據類型有哪些,一般分為基本類型(Number、String、Null、Undefined、Boolean、Symbol )和引用類型(對象、數組、函數)。

基本類型是不可變的,任何方法都無法改變一個基本類型的值,也不可以給基本類型添加屬性或者方法。但是可以為引用類型添加屬性和方法,也可以刪除其屬性和方法。

基本類型和引用類型在內存中的存儲方式也大不相同,基本類型保存在棧內存中,而引用類型保存在堆內存中。為什么要分兩種保存方式呢? 因為保存在棧內存的必須是大小固定的數據,引用類型的大小不固定,只能保存在堆內存中,但是我們可以把它的地址寫在棧內存中以供我們訪問。

說來這么多,我們來看個示例:

let num1 = 10;

let obj1 = {

name: "hh"

}

let num2 = num1;

let obj2 = obj1;

num2 = 20;

obj2.name = "kk";

console.log(num1); // 10

console.log(obj1.name); // kk

執行完這段代碼,內存空間里是這樣的:

可以看到 obj1 和 obj2 都保存了一個指向該對象的指針,所有的操作都是對該引用的操作,所以對 obj2 的修改會影響 obj1。

小結:

之所以會出現深淺拷貝,是由于 JS 對基本類型和引用類型的處理不同?;绢愋椭傅氖呛唵蔚臄祿?#xff0c;而引用類型指的是一個對象保存在堆內存中的地址,JS 不允許我們直接操作內存中的地址,也就是說不能操作對象的內存空間,所以,我們對對象的操作都只是在操作它的引用而已。

在復制時也是一樣,如果我們復制一個基本類型的值時,會創建一個新值,并把它保存在新的變量的位置上。而如果我們復制一個引用類型時,同樣會把變量中的值復制一份放到新的變量空間里,但此時復制的東西并不是對象本身,而是指向該對象的指針。所以我們復制引用類型后,兩個變量其實指向同一個對象,所以改變其中的一個對象,會影響到另外一個。

二、深淺拷貝

1. 淺拷貝

淺拷貝只是復制基本類型的數據或者指向某個對象的指針,而不是復制對象本身,源對象和目標對象共享同一塊內存;若對目標對象進行修改,存在源對象被篡改的可能。

我們來看下淺拷貝的實現:

/* sourceObj 表示源對象

* 執行完函數,返回目標對象

*/

function shadowClone (sourceObj = {}) {

let targetObj = Array.isArray(sourceObj) ? [] : {};

let copy;

for (var key in sourceObj) {

copy = sourceObj[key];

targetObj[key] = copy;

}

return targetObj;

}

// 定義 source

let sourceObj = {

number: 1,

string: 'source1',

boolean: true,

null: null,

undefined: undefined,

arr: [{name: 'arr1'}, 1],

func: () => 'sourceFunc1',

obj: {

string: 'obj1',

func: () => 'objFunc1'

}

}

// 拷貝sourceObj

let copyObj = shadowClone(sourceObj);

// 修改 sourceObj

copyObj.number = 2;

copyObj.string = 'source2';

copyObj.boolean = false;

copyObj.arr[0].name = 'arr2';

copyObj.func = () => 'sourceFunc2';

copyObj.obj.string = 'obj2';

copyObj.obj.func = () => 'objFunc2';

// 執行

console.log(sourceObj);

/* {

number: 1,

string: 'source1',

boolean: true,

null: null,

undefined: undefined,

arr: [{name: 'arr2'}],

func: () => 'sourceFunc1',

obj: {

func: () => 'objFunc2',

string: 'obj2'

}

}

*/

2. 深拷貝

深拷貝能夠實現真正意義上的對象的拷貝,實現方法就是遞歸調用“淺拷貝”。深拷貝會創造一個一模一樣的對象,其內容地址是自助分配的,拷貝結束之后,內存中的值是完全相同的,但是內存地址是不一樣的,目標對象跟源對象不共享內存,修改任何一方的值,不會對另外一方造成影響。

/* sourceObj 表示源對象

* 執行完函數,返回目標對象

*/

function deepClone (sourceObj = {}) {

let targetObj = Array.isArray(sourceObj) ? [] : {};

let copy;

for (var key in sourceObj) {

copy = sourceObj[key];

if (typeof(copy) === 'object') {

if (copy instanceof Object) {

targetObj[key] = deepClone(copy);

} else {

targetObj[key] = copy;

}

} else if (typeof(copy) === 'function') {

targetObj[key] = eval(copy.toString());

} else {

targetObj[key] = copy;

}

}

return targetObj;

}

// 定義 sourceObj

let sourceObj = {

number: 1,

string: 'source1',

boolean: true,

null: null,

undefined: undefined,

arr: [{name: 'arr1'}],

func: () => 'sourceFunc1',

obj: {

string: 'obj1',

func: () => 'objFunc1'

}

}

// 拷貝sourceObj

let copyObj = deepClone(sourceObj);

// 修改 source

copyObj.number = 2;

copyObj.string = 'source2';

copyObj.boolean = false;

copyObj.arr[0].name = 'arr2';

copyObj.func = () => 'sourceFunc2';

copyObj.obj.string = 'obj2';

copyObj.obj.func = () => 'objFunc2';

// 執行

console.log(sourceObj);

/* {

number: 1,

string: 'source1',

boolean: true,

null: null,

undefined: undefined,

arr: [{name: 'arr1'}],

func: () => 'sourceFunc1',

obj: {

func: () => 'objFunc1',

string: 'obj1'

}

}

*/

兩個方法可以合并在一起:

/* deep 為 true 表示深復制,為 false 表示淺復制

* sourceObj 表示源對象

* 執行完函數,返回目標對象

*/

function clone (deep = true, sourceObj = {}) {

let targetObj = Array.isArray(sourceObj) ? [] : {};

let copy;

for (var key in sourceObj) {

copy = sourceObj[key];

if (deep && typeof(copy) === 'object') {

if (copy instanceof Object) {

targetObj[key] = clone(deep, copy);

} else {

targetObj[key] = copy;

}

} else if (deep && typeof(copy) === 'function') {

targetObj[key] = eval(copy.toString());

} else {

targetObj[key] = copy;

}

}

return targetObj;

}

三、使用技巧

1. concat()、slice()

(1)若拷貝數組是純數據(不含對象),可以通過concat() 和 slice() 來實現深拷貝;

let a = [1, 2];

let b = [3, 4];

let copy = a.concat(b);

a[1] = 5;

b[1] = 6;

console.log(copy);

// [1, 2, 3, 4]

let a = [1, 2];

let copy = a.slice();

copy[0] = 3;

console.log(a);

// [1, 2]

(2)若拷貝數組中有對象,可以使用 concat() 和 slice() 方法來實現數組的淺拷貝。

let a = [1, {name: 'hh1'}];

let b = [2, {name: 'kk1'}];

let copy = a.concat(b);

copy[1].name = 'hh2';

copy[3].name = 'kk2';

console.log(copy);

// [1, {name: 'hh2'}, 2, {name: 'kk2'}]

無論 a[1].name 或者 b[1].name 改變,copy[1].name 的值都會改變。

let a = [1, {name: 'hh1'}];

let copy = a.slice();

copy[1].name = 'hh2';

console.log(a);

// [1, {name: 'hh2'}]

改變了 a[1].name 后,copy[1].name 的值也改變了。

2. Object.assign()、Object.create()

Object.assign()、Object.create() 都是一層(根級)深拷貝,之下的級別為淺拷貝。

(1) 若拷貝對象只有一級,可以通過 Object.assign()、Object.create() 來實現對象的深拷貝;

let sourceObj = {

str: 'hh1',

number: 10

}

let targetObj = Object.assign({}, sourceObj)

targetObj.str = 'hh2'

console.log(sourceObj);

// {str: 'hh1', number: 10}

let sourceObj = {

str: 'hh1',

number: 10

}

let targetObj = Object.create(sourceObj)

targetObj.str = 'hh2'

console.log(sourceObj);

// {str: 'hh1', number: 10}

(2) 若拷貝對象有多級, Object.assign()、Object.create() 實現的是對象的淺拷貝。

let sourceObj = {

str: 'hh',

number: 10,

obj: {

str: 'kk1'

}

}

let targetObj = Object.assign({}, sourceObj)

targetObj.obj.str = 'kk2'

console.log(sourceObj);

// {

// str: 'hh',

// number: 10,

// obj: {

// str: 'kk2'

// }

// }

let sourceObj = {

str: 'hh',

number: 10,

obj: {

str: 'kk1'

}

}

let targetObj = Object.create(sourceObj)

targetObj.obj.str = 'kk2'

console.log(sourceObj);

// {

// str: 'hh',

// number: 10,

// obj: {

// str: 'kk2'

// }

// }

修改了 targetObj.obj.str 的值之后,sourceObj.obj.str 的值也改變了。

3. 對象的解構

對象的解構同 Object.assign() 和 Object.create(),都是一層(根級)深拷貝,之下的級別為淺拷貝。

(1)若拷貝對象只有一層,可以通過對象的解構來實現深拷貝;

let sourceObj = {

str: 'hh1',

number: 10

}

let targetObj = {...sourceObj};

targetObj.str = 'hh2'

console.log(sourceObj);

// {str: 'hh1', number: 10}

(2)若拷貝對象有多層,通過對象的解構實現的是對象的淺拷貝。

let sourceObj = {

str: 'hh',

number: 10,

obj: {

str: 'kk1'

}

}

let targetObj = {...sourceObj};

targetObj.obj.str = 'kk2'

console.log(sourceObj);

// {

// str: 'hh',

// number: 10,

// obj: {

// str: 'kk2'

// }

// }

4. JSON.parse()

用 JSON.stringify() 把對象轉成字符串,再用 JSON.parse() 把字符串轉成新的對象,可以實現對象的深復制。

let source = ['hh', 1, [2, 3], {name: 'kk1'}];

let copy = JSON.parse(JSON.stringify(source));

copy[2][1] = 4;

copy[3].name = 'kk2';

console.log(source);

// ['hh', 1, [2, 3], {name: 'kk1'}]

可以看出,雖然改變了 copy[2].name 的值,但是 source[2].name 的值沒有改變。

JSON.parse(JSON.stringify(obj)) 不僅能復制數組還可以復制對象,但是幾個弊端:

1)它會拋棄對象的 constructor,深拷貝之后,不管這個對象原來的構造函數是什么,在深拷貝之后都會變成 Object;

2)這種方法能正確處理的對象只有?Number, String, Boolean, Array, 扁平對象,即那些能夠被 json 直接表示的數據結構。RegExp 對象是無法通過這種方式深拷貝。

3)只有可以轉成 JSON 格式的對象才可以這樣用,像 function 沒辦法轉成 JSON。

5. 可以使用的庫

以下兩種庫都能實現深淺拷貝,有各自的使用方法。

jQuery

具體使用可以參考:官方文檔

Lodash

具體使用可以參考:官方文檔

總結

以上是生活随笔為你收集整理的php深浅拷贝,JavaScript 中的深浅拷贝的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。