1. 程式人生 > 其它 >2021.06.17(理解js中的深拷貝和淺拷貝)

2021.06.17(理解js中的深拷貝和淺拷貝)

在JS當中,資料型別分為基本資料型別引用型別,其中基本資料型別(string,number,boolean,undefined,null,symnol......),引用型別為Object(Array、Object、Function......)。 記憶體分為棧記憶體堆記憶體,其中棧記憶體用來儲存基本資料型別(存取速度快,存放量小)和引用型別的地址(存取速度慢,存放量大,其引用指標存於棧區,並指向引用本身),而堆記憶體則儲存引用資料型別。

基本資料型別:賦值,賦值之後兩個變數互不影響

引用資料型別:賦址,兩個變數具有相同的引用,指向同一個物件,相互之間有影響

<script>
    //
基本型別 var a = 100; var b = a; a = 200; console.log(a, b); // 200, 100 ,a b指向不同的資料 // 引用型別指向同一份資料 var a = { c: 1000 }; var b = a; a.c = 2000; console.log(a.c, b.c); // 2000, 2000 全是2000,a b指向同一份資料 </script>

通常在開發中並不希望改變變數 a 之後會影響到變數 b,這時就需要用到淺拷貝和深拷貝。

淺拷貝

建立一個新物件,這個物件有著原始物件屬性值的一份精確拷貝。如果屬性是基本型別,拷貝的就是基本型別的值,如果屬性是引用型別,拷貝的就是記憶體地址 ,

所以如果其中一個物件改變了這個地址,就會影響到另一個物件。

<script>
  function cloneShallow(source) {
    var target = {};
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
    return target;
  }
  var a1 = { b: { c: {} } };
  var
a2 = cloneShallow(a1); // 淺拷貝 a2.b.c = { d: "1" }; console.log("a1---", a1);  //{b:{c:{d:"1"}}} console.log("a2---", a2);  //{b:{c:{d:"1"}}} var a5 = { b: { d: [1,2] } }; var a6 = cloneShallow(a5); // 淺拷貝 a6.b.d = [3,4]; console.log("a5---", a5);  //{b:{d[3,4])) console.log("a6---", a6);  //{b:{d[3,4])) // 注意:當object只有一層的時候,是深拷貝,例如如下: var a3 = { b:'9'}; var a4 = cloneShallow(a3); a4.b = '10'; console.log("a3---", a3);  //{b:'9'} console.log("a4---", a4);  //{b:'10'} </script>

Object.assign()

Object.assign() 方法可以把任意多個的源物件自身的可列舉屬性拷貝給目標物件,然後返回目標物件。注意,Object.assgin() 進行的是淺拷貝,拷貝的是物件的屬性的引用,而不是物件本身。

Object.assign(target, ...sources)

Array.prototype.concat()

Array.prototype.slice()

...obj 展開運算子

深拷貝

深拷貝會拷貝所有的屬性,並拷貝屬性指向的動態分配的記憶體。當物件和它所引用的物件一起拷貝時即發生深拷貝。深拷貝相比於淺拷貝速度較慢並且花銷較大。拷貝前後兩個物件互不影響

<script>
  function isObject(obj) {
    return typeof obj === "object" && obj != null;
  }
  function cloneDeep(source) {
    if (!isObject(source)) return source; // 非物件返回自身

    var target = Array.isArray(source) ? [] : {};
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        if (isObject(source[key])) {
          target[key] = cloneDeep(source[key]); // 注意這裡
        } else {
          target[key] = source[key];
        }
      }
    }
    return target;
  }
  var obj = {
    title: "study",
    list: ["1", "2", "3"],
  };
  var obj2 = cloneDeep(obj);
  obj2.title = "play";
  obj2.list = ["3", "4"];
  console.log("obj", obj);  //title: "style",list: ["1", "2", "3"]
  console.log("obj2", obj2);  //title: "play",list: ["3", "4"]
</script>

JSON.parse(JSON.stringify(object))

JSON.stringify():將物件轉成 JSON 字串。

JSON.parse():將字串解析成物件。

通過 JSON.parse(JSON.stringify()) 將 JavaScript 物件轉序列化(轉換成 JSON 字串),

再將其還原成 JavaScript 物件,一去一來我們就產生了一個新的物件,而且物件會開闢新的棧,從而實現深拷貝。

注意

注意,該方法的侷限性:

1、不能存放函式或者 Undefined,否則會丟失函式或者 Undefined;

2、不要存放時間物件,否則會變成字串形式;

3、不能存放 RegExp、Error 物件,否則會變成空物件;

4、不能存放 NaN、Infinity、-Infinity,否則會變成 null;

5、……更多請自行填坑,具體來說就是 JavaScript 和 JSON 存在差異,兩者不相容的就會出問題。

函式庫 Lodash

cloneDeep() 方法
npm i --save lodash
var _ = require('lodash');

var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

2021-06-17 11:18:29