Try   HackMD

JS 直播班 求值策略、淺層複製及深層複製

求值策略

Call by Reference 還是 Call by Sharing

求值策略

  • 傳值呼叫(Call by value): JavaScript 純值屬於此類
    • 把值複製到新記憶體區域
  • 傳參照呼叫(Call by reference)
    • 傳遞給函式的是它的實際參數的隱式參照而不是實參的拷貝
  • 傳共享物件呼叫(Call by sharing): JavaScript 物件屬於此類
    • 把物件定義出來給不同的變數使用
    • 如果要達成傳參照呼叫的效果就需要傳一個共享物件,一旦被呼叫者修改了物件,呼叫者就可以看到變化(因為物件是共享的,沒有拷貝)

總結

object 的話,基本上是 call by reference,唯一要注意的就是重新賦值這個動作造成 reference 改變的影響 → 變成 call by sharing


淺層複製及深層複製

物件有傳參考特性,有時會在我們想要複製物件,並且想要這個複製的物件獨立於原本的物件時會造成一些困擾 (需將兩物件完全拆離開來) 這樣要如何解決此問題呢 ?? 以下提供幾種複製的方法,解決這個問題。

淺層複製 Shallow Copy

透過 for in 語法,做物件的淺層複製

只能做第一層的複製,第二層仍舊是用傳參考的方式傳入的( call by reference)

var family = { name: '小明家', members: { father: '老爸', mom: '老媽', ming: '小明' }, } var newFamily = {}; // 先新建一個物件

key 指的是物件中的第一層屬性名稱第一層有幾個屬性、就跑幾次
淺層複製只會複製純值,淺層複製也會宣告新的記憶體空間
內層物件只會傳參考

for (var key in family) { // 宣告一個 key,key 值包含物件的第一層屬性內容 console.log(key); // name members ( key 值包含 family 物件第一層的屬性 ) console.log(family[key]); // 讀出屬性所對應的內容 // 小明家 // {father: "老爸", mom: "老媽", ming: "小明"} newFamily[key] = family[key]; // 將值塞入 newFamily 之後,就完成淺拷貝 } console.log(family, newFamily); // 兩個物件內容一樣 newFamily.name = '杰倫家'; // 只能更改第一層內容 console.log(family, newFamily); // (如下圖) console.log(family === newFamily); // false

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

newFamily 和 family.members 物件其實仍指向同一個參考位置,所以 family 的 members 內容也被修改了。

newFamily.members.ming = '大明'; // 第二層還是「傳參考」模式 console.log(family, newFamily);

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

除了for in 語法外,也可以透過 jQuery 或是 ES6 寫法可以達到同樣的目的。

插入空物件,再把 family 物件傳入

☞ jQuery 寫法

var newFamily2 = jQuery.extend({}, family);

☞ ES6 寫法

var newFamily3 = Object.assign({}, family);

☞ ES6 object.forEach() 寫法

var array = [];
family.forEach((item)=>{  // 淺層複製,相當於 for in 的語法
    array.push(item);
})

深層複製 Deep Copy

將原本的物件轉為字串,再轉回物件,這樣傳參考的特性就消失了。

使用 JSON 方式把物件轉為字串,再轉回物件

var family = {
  name: '小明家',
  members: {
    father: '老爸',
    mom: '老媽',
    ming: '小明'
  },
}
console.log(family, JSON.stringify(family));  
// 轉為字串,此 family 字串和原本的 family 物件已無參考關係
console.log(family, JSON.parse(JSON.stringify(family)));  
// 再把 family 字串轉為物件 (和原本的 family 物件已無參考關係)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

var newFamily = JSON.parse(JSON.stringify(family));
console.log(family === newFamily);  // false,兩者已無關聯性
newFamily.name = '杰倫家';
newFamily.members.ming = '大明';
console.log(family, newFamily);  // (如下圖) 兩者已無關聯,參考位置也不一樣

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

如果"類陣列"想要當陣列用,可以使用展開語法 [arguments] 列舉並深層複製成一個新的陣列

function updateEasyCard() {	
 let arg = [...arguments];
 let sum = arg.reduce(function(accumulator, currentValue) {
  return accumulator + currentValue;
 }, 0);
 console.log('我有 ' + sum + ' 元');
}
updateEasyCard(0); // 我有 0 元
updateEasyCard(10, 50, 100, 50, 5, 1, 1, 1, 500); // 我有 718 元

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
學習回顧

  • 傳值呼叫(Call by value): 純值屬於此類
    • 把值複製到新記憶體區域
  • 傳參照呼叫(Call by reference) : 物件屬於此類
  • 傳共享物件呼叫(Call by sharing): 物件屬於此類
    • 重新賦值這個動作造成 reference 改變的影響 → 變成 call by sharing
  • 淺層複製 Shallow Copy : 只能做第一層的複製,第二層仍舊是用傳參考的方式傳入的
    • 透過 for(var key in object) 語法,做物件的淺層複製
    • key 指的是物件中的第一層屬性名稱

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
相關參考文件