iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 11
3
Modern Web

從0.5開始的JavaScript系列 第 11

Day11 工具包: 物件(2) - 深拷貝vs淺拷貝

我們昨天有提到了物件 call by reference 的特性。但是問題來了,如果我真的只是單純要複製物件的值而已,不希望在複製後,只要更動其值,原變數也會受到影響(淺拷貝),該怎麼做呢?

這就是今天要分享的 深拷貝~

深拷貝

那該怎麼單純的取物件的值呢?

  1. 透過此種方式重新賦值
  • b = {xxx},這種寫法將會使 b 存放的值,指向一個新的記憶體位置
var a = {value: 123}
var b = a;

b = {value: 456};

console.log(a.value); // 123
console.log(b.value); // 456

可以看到 b 的 value 改變為 456,但是 a 的 value 還是 123。

同樣的範例還有這個,我們直接把想從 a 取出的值放到屬性名稱後面,

var a = {name: 'Ted', age: 18};
var b = {name: a.name, age: a.age};

b.name = "changed";

console.log(a.name); // Ted
console.log(b.name); // changed
  1. Object.assign

懶得像上方手動複製,可以使用此方法

var a = {name: 'Ted'};
var b = Object.assign({}, a);

b.name = "Ray";

console.log(a.name); // Ted
console.log(b.name); // Ray

一樣可以觀察到,物件 bname 改變時,物件 a 不會受到影響。

But

但是沒這麼簡單,以上方法只能拷貝一層,也就是可能會發生以下狀況,
請問 Q1Q2 分別為多少?

var a = {
    name: 'Ted',
    language: {
        chinese: 'nice',
        english: 'nice'
    }
};
var b = Object.assign({}, a);

b.name = "Bob";
b.language.chinese = 'bad';

console.log(a.name); // Q1=?
console.log(a.language.chinese); // Q2=?

答案是,
Ans:
Q1 = Ted
Q2 = bad

注意到了嗎?
物件 b 改變 name 並不會影響物件 aname,但是改變再深一層的物件的值,就會影響到原本 alanguage.chinese 了!

當初遇到這個問題爬了一下文,才知道原來這是很熱門的問題,也是面試常常會被問到的XD

那該怎麼解決呢?

解決方法

  1. 原生 js 寫個 function,裡面用遞迴或 for 不斷取值,真正實現深拷貝。
// 網路上自己找QAQ
  1. 轉換成 JSONparse 回來
var a = {
    name: 'Ted',
    language: {
        chinese: 'nice',
        english: 'nice'
    }
};
var b = JSON.parse(JSON.stringify(a));

b.name = "Bob";
b.language.chinese = 'bad';

console.log(a.name); // Ted
console.log(a.language.chinese); // nice
  1. 使用第三方套件(ex: JQuery、lodash)

    • lodash
    var a = {
      name: 'Ted',
      language: {
        chinese: 'nice',
        english: 'nice'
      }
    };
    var cloneA = cloneDeep(a);
    
    • JQuery
      深、淺拷貝對應的參數是可選的,為 truefalse
      預設情況是 false(淺拷貝),而且 false 不能夠寫出來,也就是你想要淺拷貝,那就什麼參數都不用帶,預設就是 false
      如果想帶入參數,只能傳入 true(深拷貝)
    var a = {};
    var b = {
        name: 'Ted',
        language: {
          chinese: 'nice',
          english: 'nice'
        }
    };
    
    // 深拷貝
    $.extend(true, a, b);
    
    a.language.chinese = 'bad';
    
    // b 並沒有因為 a 的改動也跟著改動
    console.log(b.language.chinese); // nice
    
    // ----------我是分隔線-----------
    
    // 至於沒有加上參數就是淺拷貝
    $.extend(a, b);
    

那今日的分享就到這邊,明天會介紹一些物件在 ES6 中的操作。

我們明天見/images/emoticon/emoticon51.gif


上一篇
Day10 工具包: 物件(1) - call by value、reference?
下一篇
Day12 工具包: 物件(3) - 解構賦值、縮寫、For...in
系列文
從0.5開始的JavaScript30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言