亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

JavaScript基礎(chǔ)心法——深淺拷貝

keithxiaoy / 1030人閱讀

摘要:原文地址基礎(chǔ)心法深淺拷貝歡迎。上面的代碼是最簡單的利用賦值操作符實現(xiàn)了一個淺拷貝,可以很清楚的看到,隨著和改變,和也隨著發(fā)生了變化。展開運算符結(jié)論實現(xiàn)的是對象第一層的深拷貝。

原文地址:JavaScript基礎(chǔ)心法——深淺拷貝

歡迎star。

如果有錯誤的地方歡迎指正。

淺拷貝和深拷貝都是對于JS中的引用類型而言的,淺拷貝就只是復(fù)制對象的引用,如果拷貝后的對象發(fā)生變化,原對象也會發(fā)生變化。只有深拷貝才是真正地對對象的拷貝。

前言

說到深淺拷貝,必須先提到的是JavaScript的數(shù)據(jù)類型,之前的一篇文章JavaScript基礎(chǔ)心法——數(shù)據(jù)類型說的很清楚了,這里就不多說了。

需要知道的就是一點:JavaScript的數(shù)據(jù)類型分為基本數(shù)據(jù)類型和引用數(shù)據(jù)類型。

對于基本數(shù)據(jù)類型的拷貝,并沒有深淺拷貝的區(qū)別,我們所說的深淺拷貝都是對于引用數(shù)據(jù)類型而言的。

淺拷貝

淺拷貝的意思就是只復(fù)制引用,而未復(fù)制真正的值。

const originArray = [1,2,3,4,5];
const originObj = {a:"a",b:"b",c:[1,2,3],d:{dd:"dd"}};

const cloneArray = originArray;
const cloneObj = originObj;

console.log(cloneArray); // [1,2,3,4,5]
console.log(originObj); // {a:"a",b:"b",c:Array[3],d:{dd:"dd"}}

cloneArray.push(6);
cloneObj.a = {aa:"aa"};

console.log(cloneArray); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,6]

console.log(cloneObj); // {a:{aa:"aa"},b:"b",c:Array[3],d:{dd:"dd"}}
console.log(originArray); // {a:{aa:"aa"},b:"b",c:Array[3],d:{dd:"dd"}}

上面的代碼是最簡單的利用 = 賦值操作符實現(xiàn)了一個淺拷貝,可以很清楚的看到,隨著 cloneArraycloneObj 改變,originArrayoriginObj 也隨著發(fā)生了變化。

深拷貝

深拷貝就是對目標的完全拷貝,不像淺拷貝那樣只是復(fù)制了一層引用,就連值也都復(fù)制了。

只要進行了深拷貝,它們老死不相往來,誰也不會影響誰。

目前實現(xiàn)深拷貝的方法不多,主要是兩種:

利用 JSON 對象中的 parsestringify

利用遞歸來實現(xiàn)每一層都重新創(chuàng)建對象并賦值

JSON.stringify/parse的方法

先看看這兩個方法吧:

The JSON.stringify() method converts a JavaScript value to a JSON string.

JSON.stringify 是將一個 JavaScript 值轉(zhuǎn)成一個 JSON 字符串。

The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string.

JSON.parse 是將一個 JSON 字符串轉(zhuǎn)成一個 JavaScript 值或?qū)ο蟆?/p>

很好理解吧,就是 JavaScript 值和 JSON 字符串的相互轉(zhuǎn)換。

它能實現(xiàn)深拷貝呢?我們來試試。

const originArray = [1,2,3,4,5];
const cloneArray = JSON.parse(JSON.stringify(originArray));
console.log(cloneArray === originArray); // false

const originObj = {a:"a",b:"b",c:[1,2,3],d:{dd:"dd"}};
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj === originObj); // false

cloneObj.a = "aa";
cloneObj.c = [1,1,1];
cloneObj.d.dd = "doubled";

console.log(cloneObj); // {a:"aa",b:"b",c:[1,1,1],d:{dd:"doubled"}};
console.log(originObj); // {a:"a",b:"b",c:[1,2,3],d:{dd:"dd"}};

確實是深拷貝,也很方便。但是,這個方法只能適用于一些簡單的情況。比如下面這樣的一個對象就不適用:

const originObj = {
  name:"axuebin",
  sayHello:function(){
    console.log("Hello World");
  }
}
console.log(originObj); // {name: "axuebin", sayHello: ?}
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj); // {name: "axuebin"}

發(fā)現(xiàn)在 cloneObj 中,有屬性丟失了。。。那是為什么呢?

MDN 上找到了原因:

If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array). JSON.stringify can also just return undefined when passing in "pure" values like JSON.stringify(function(){}) or JSON.stringify(undefined).

undefined、functionsymbol 會在轉(zhuǎn)換過程中被忽略。。。

明白了吧,就是說如果對象中含有一個函數(shù)時(很常見),就不能用這個方法進行深拷貝。

遞歸的方法

遞歸的思想就很簡單了,就是對每一層的數(shù)據(jù)都實現(xiàn)一次 創(chuàng)建對象->對象賦值 的操作,簡單粗暴上代碼:

function deepClone(source){
  const targetObj = source.constructor === Array ? [] : {}; // 判斷復(fù)制的目標是數(shù)組還是對象
  for(let keys in source){ // 遍歷目標
    if(source.hasOwnProperty(keys)){
      if(source[keys] && typeof source[keys] === "object"){ // 如果值是對象,就遞歸一下
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      }else{ // 如果不是,就直接賦值
        targetObj[keys] = source[keys];
      }
    } 
  }
  return targetObj;
}

我們來試試:

const originObj = {a:"a",b:"b",c:[1,2,3],d:{dd:"dd"}};
const cloneObj = deepClone(originObj);
console.log(cloneObj === originObj); // false

cloneObj.a = "aa";
cloneObj.c = [1,1,1];
cloneObj.d.dd = "doubled";

console.log(cloneObj); // {a:"aa",b:"b",c:[1,1,1],d:{dd:"doubled"}};
console.log(originObj); // {a:"a",b:"b",c:[1,2,3],d:{dd:"dd"}};

可以。那再試試帶有函數(shù)的:

const originObj = {
  name:"axuebin",
  sayHello:function(){
    console.log("Hello World");
  }
}
console.log(originObj); // {name: "axuebin", sayHello: ?}
const cloneObj = deepClone(originObj);
console.log(cloneObj); // {name: "axuebin", sayHello: ?}

也可以。搞定。

是不是以為這樣就完了?? 當然不是。

JavaScript中的拷貝方法

我們知道在 JavaScript 中,數(shù)組有兩個方法 concatslice 是可以實現(xiàn)對原數(shù)組的拷貝的,這兩個方法都不會修改原數(shù)組,而是返回一個修改后的新數(shù)組。

同時,ES6 中 引入了 Object.assgn 方法和 ... 展開運算符也能實現(xiàn)對對象的拷貝。

那它們是淺拷貝還是深拷貝呢?

concat

The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.

該方法可以連接兩個或者更多的數(shù)組,但是它不會修改已存在的數(shù)組,而是返回一個新數(shù)組。

看著這意思,很像是深拷貝啊,我們來試試:

const originArray = [1,2,3,4,5];
const cloneArray = originArray.concat();

console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];

看上去是深拷貝的。

我們來考慮一個問題,如果這個對象是多層的,會怎樣。

const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2; 
console.log(originArray); // [1,[1,2,3,4],{a:2}]

originArray 中含有數(shù)組 [1,2,3] 和對象 {a:1},如果我們直接修改數(shù)組和對象,不會影響 originArray,但是我們修改數(shù)組 [1,2,3] 或?qū)ο?{a:1} 時,發(fā)現(xiàn) originArray 也發(fā)生了變化。

結(jié)論:concat 只是對數(shù)組的第一層進行深拷貝。

slice

The slice() method returns a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified.

解釋中都直接寫道是 a shallow copy 了 ~

但是,并不是!

const originArray = [1,2,3,4,5];
const cloneArray = originArray.slice();

console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];

同樣地,我們試試多層的數(shù)組。

const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2; 
console.log(originArray); // [1,[1,2,3,4],{a:2}]

果然,結(jié)果和 concat 是一樣的。

結(jié)論:slice 只是對數(shù)組的第一層進行深拷貝。

Object.assign()

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

復(fù)制復(fù)制復(fù)制。

那到底是淺拷貝還是深拷貝呢?

自己試試吧。。

結(jié)論:Object.assign() 拷貝的是屬性值。假如源對象的屬性值是一個指向?qū)ο蟮囊茫仓豢截惸莻€引用值。

... 展開運算符
const originArray = [1,2,3,4,5,[6,7,8]];
const originObj = {a:1,b:{bb:1}};

const cloneArray = [...originArray];
cloneArray[0] = 0;
cloneArray[5].push(9);
console.log(originArray); // [1,2,3,4,5,[6,7,8,9]]

const cloneObj = {...originObj};
cloneObj.a = 2;
cloneObj.b.bb = 2;
console.log(originObj); // {a:1,b:{bb:2}}

結(jié)論:... 實現(xiàn)的是對象第一層的深拷貝。后面的只是拷貝的引用值。

首層淺拷貝

我們知道了,會有一種情況,就是對目標對象的第一層進行深拷貝,然后后面的是淺拷貝,可以稱作“首層淺拷貝”。

我們可以自己實現(xiàn)一個這樣的函數(shù):

function shallowClone(source) {
  const targetObj = source.constructor === Array ? [] : {}; // 判斷復(fù)制的目標是數(shù)組還是對象
  for (let keys in source) { // 遍歷目標
    if (source.hasOwnProperty(keys)) {
      targetObj[keys] = source[keys];
    }
  }
  return targetObj;
}

我們來測試一下:

const originObj = {a:"a",b:"b",c:[1,2,3],d:{dd:"dd"}};
const cloneObj = shallowClone(originObj);
console.log(cloneObj === originObj); // false
cloneObj.a="aa";
cloneObj.c=[1,1,1];
cloneObj.d.dd="surprise";

經(jīng)過上面的修改,cloneObj 不用說,肯定是 {a:"aa",b:"b",c:[1,1,1],d:{dd:"surprise"}} 了,那 originObj 呢?剛剛我們驗證了 cloneObj === originObjfalse,說明這兩個對象引用地址不同啊,那應(yīng)該就是修改了 cloneObj 并不影響 originObj

console.log(cloneObj); // {a:"aa",b:"b",c:[1,1,1],d:{dd:"surprise"}}
console.log(originObj); // {a:"a",b:"b",c:[1,2,3],d:{dd:"surprise"}}

What happend?

originObj 中關(guān)于 a、c都沒被影響,但是 d 中的一個對象被修改了。。。說好的深拷貝呢?不是引用地址都不一樣了嗎?

原來是這樣:

shallowClone 的代碼中我們可以看出,我們只對第一層的目標進行了 深拷貝 ,而第二層開始的目標我們是直接利用 = 賦值操作符進行拷貝的。

so,第二層后的目標都只是復(fù)制了一個引用,也就是淺拷貝。

總結(jié)

賦值運算符 = 實現(xiàn)的是淺拷貝,只拷貝對象的引用值;

JavaScript 中數(shù)組和對象自帶的拷貝方法都是“首層淺拷貝”;

JSON.stringify 實現(xiàn)的是深拷貝,但是對目標對象有要求;

若想真正意義上的深拷貝,請遞歸。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/89429.html

相關(guān)文章

  • 前端進擊的巨人(二):棧、堆、隊列、內(nèi)存空間

    摘要:中有三種數(shù)據(jù)結(jié)構(gòu)棧堆隊列。前端進擊的巨人一執(zhí)行上下文與執(zhí)行棧,變量對象中解釋執(zhí)行棧時,舉了一個乒乓球盒子的例子,來演示棧的存取方式,這里再舉個栗子搭積木。對于基本類型,棧中存儲的就是它自身的值,所以新內(nèi)存空間存儲的也是一個值。 面試經(jīng)常遇到的深淺拷貝,事件輪詢,函數(shù)調(diào)用棧,閉包等容易出錯的題目,究其原因,都是跟JavaScript基礎(chǔ)知識不牢固有關(guān),下層地基沒打好,上層就是豆腐渣工程,...

    edgardeng 評論0 收藏0
  • js深淺復(fù)制

    摘要:總結(jié)綜上所述,數(shù)組的深拷貝比較簡單,方法沒有什么爭議,對象的深拷貝,比較好的方法是用的方法實現(xiàn),或者遞歸實現(xiàn),比較簡單的深復(fù)制可以使用實現(xiàn)參考資料知乎中的深拷貝和淺拷貝深入剖析的深復(fù)制 深淺復(fù)制對比 因為JavaScript存儲對象都是存地址的,所以淺復(fù)制會導(dǎo)致 obj 和obj1 指向同一塊內(nèi)存地址。我的理解是,這有點類似數(shù)據(jù)雙向綁定,改變了其中一方的內(nèi)容,都是在原來的內(nèi)存基礎(chǔ)上做...

    Apollo 評論0 收藏0
  • Javascript對象的深淺拷貝

    摘要:開門見山,有人叫對象的復(fù)制為深復(fù)制淺復(fù)制,也有人叫深拷貝淺拷貝。高級屬性修改深拷貝滿足對象的復(fù)制,淺拷貝影響原數(shù)組。關(guān)于對象的深淺拷貝,暫且探索到這里,后續(xù)有新發(fā)現(xiàn)再進行補充。 showImg(https://segmentfault.com/img/remote/1460000014305581); 開門見山,有人叫對象的復(fù)制為深復(fù)制淺復(fù)制,也有人叫深拷貝淺拷貝。其實都是copy。 ...

    qieangel2013 評論0 收藏0
  • JavaScript專題之深淺拷貝

    摘要:專題系列第六篇,講解深淺拷貝的技巧和以及實現(xiàn)深淺拷貝的思路前言拷貝也是面試經(jīng)典吶數(shù)組的淺拷貝如果是數(shù)組,我們可以利用數(shù)組的一些方法比如返回一個新數(shù)組的特性來實現(xiàn)拷貝。所以我們可以看出使用和是一種淺拷貝。 JavaScript 專題系列第六篇,講解深淺拷貝的技巧和以及實現(xiàn)深淺拷貝的思路 前言 拷貝也是面試經(jīng)典吶! 數(shù)組的淺拷貝 如果是數(shù)組,我們可以利用數(shù)組的一些方法比如:slice、co...

    RancherLabs 評論0 收藏0
  • JavaScript深淺拷貝

    摘要:什么是深淺概念深拷貝淺拷貝只針對像這樣的對象,對于基本類型而言,可以理解為是沒有深淺的區(qū)別的。和指向了同一塊內(nèi)存深拷貝重新開辟了一個空間,修改對象的屬性,彼此不會影響。并不會更改使用遞歸適用于對象里面有對象 什么是深淺 概念 深拷貝、淺拷貝只針對像Object/Array這樣的對象,對于基本類型而言,可以理解為是沒有深淺的區(qū)別的。 淺拷貝復(fù)制的是引用,修改對象的屬性,會彼此影響。 ju...

    zhunjiee 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<