純值賦值是透過複製的方式,所以前者與後者各自獨立,當後者修改時不會影響前者。
純值:為基本型別 ( number、string、boolean、null、undefined )。
範例
var person = '小明';
var person2 = person;
person2 = '杰倫';
var person2 = person;
為把 person
的值帶過去 person2
就為傳值,純值傳值是一種複製的方式。person2
接收了 person
的值後再另外賦予 person2
新的值就不會和 person
有關聯性。物件賦值是透過傳參考的特性,所以前者與後者都共用同一個記憶體空間的參考路徑,後者修改時前者也會跟著修改。
{}
就會產生一個新的記憶體空間,就不會相互影響 ( 如下方範例 2. 3 )。範例1. 物件傳參考
var person = {
name: '小明',
money: 1000,
};
var person2 = person;
person2.name = '杰倫';
console.log( person === person2 );
解析:
程式碼中我們宣告了一個 person
( var person = { name: '小明', money: 1000,};
) 表示在記憶體準備了一個參考路徑,這個參考位置包含了 name
和 money
。( 如下右圖,0x01 是為了記憶自訂義名稱 )
當定義 person 為一個物件時,person 帶入的會是 0x01 這個記憶體的參考路徑,並不是帶入一個完整的物件內容。當參考路徑指向此物件時 ( 傳參考 ) 就可透過這種方式來取得裡面的 name
與 money
屬性。所以 person
並不會把 name
與 money
存到它的記憶體空間,只會傳入一個參考。
var person2 = person;
時會把 person
原本的參考位置一樣傳到 person2
,所以 person
和 person2
共用的是同一個參考路徑 ( 右邊格子 0x01 ),所以 person2
修改時也會一併修改到 person
。
person2
person
兩者皆共用同一物件。物件賦予值是以傳參考方式進行。
所以答案為 true
。
範例 2. 物件傳參考
var person = {
name: '小明',
money: 1000,
}
****var person2 = person;
person2 = { ... };
console.log( person === person2 );
解析:
person2
指向一個新的空物件 { … }
,它就會產生另一個參考路徑 0x02,這時產生另一個物件也就是產生另一個參考路徑,所以 person
與 person2
就不會有任何關聯,互不影響。false
。範例 3. 物件傳參考
var person = {
name: '小明',
}
****var person2 = person;
person2 = {
name: '小明',
}
console.log( person === person2 );
person2 = { name: '小明' }
中就算 person2
與 person
內屬性與屬性值相同,但因分別為獨立的兩個物件兩個參考路徑,所以不會互相影響。false
。❗ 注意:新增一個新的物件就會產生一個新的記憶體空間,前者與後者不會相互影響。
var family = {
name: '小明家',
members: {
father: '老爸',
mother: '老媽',
ming: '小明',
},
}
var member = family.members;
member = {
ming: '大明',
};
console.log(family);
解析:
在 var member = family.members;
時,member
與 family.members
都還是同個參考路徑。
member
賦予一個新的物件 member = { ming: '大明' }
開始,就拆成兩個參考路徑了,因為「 物件變數中看到新的 {}
就表示會產生一個新的記憶體空間 」。
但是換成另種寫法 :member
直接修改屬性而非產生 {}
,member
就會和 family.members
使用同一個參考路徑,而它們兩個也會相互影響。
var family = {
name: '小明家',
members: {
father: '老爸',
mother: '老媽',
ming: '小明',
},
}
var member = family.members;
member.ming = '大明';
console.log(family);
var a = {
x: 1,
}
a.y = a;
console.log(a);
解析:
此寫法會造成無限循環,不斷指向自己。
{
x: 1,
y: {
x: 1,
y: {
x: 1,
y: {.... 無限循環}
}
}
}
a
產生一個參考路徑 0x01 屬性為 x
→ a
指向 0x01 參考路徑 → a
新增一個 y
屬性且屬性值為 a
,指回原參考路徑 0x01。從 a
裡面找 y
就會指回原參考路徑一直指向自己造成無限循環。
var a = {
x: 1,
}
var b = a;
a.y = a = {
x: 2,
}
console.log(a.y);
console.log(b);
console.log(a === b.y);
拆解:
var b = a;
這時變數 b
與 a
都還是同一個參考路徑 0x01。
a.y = a = { x: 2 };
a = { x: 2 };
為運算式,所以 x: 2
會賦予到 y
上,a.y = a = { x: 2 };
這行是同時執行的沒有執行順序的問題,會等於 a = a.y = { x: 2 };
。所以 a.y
的參考路徑會是原本的 a
參考路徑 0x01 而非 a
新的參考路徑 0x02。a
新增一個 y
屬性,其屬性值為 a ( 參考物件 0x01 ) → 屬性值 a
新增一個物件,只要新增物件就會新增一個參考路徑,這邊 a
新增物件的新參考路徑為 0x02 裡面有 x
屬性與屬性值 2 ( a.y
參考路徑由 0x01 變 0x02 )。答案:
console.log(a.y);
印出 undefined
。
undefined
。console.log(b);
印出 { x:1, y: { x:2 } }
。console.log(a === b.y);
印出 true
。var a = { x: 1};
var b = a;
a.x = { x: 2 };
a.y = a = { y: 1};
console.log(a); // 結果?
console.log(b); // 結果?
解析:
var b = a;
時,a
與 b
都還是同個參考路徑 0x01。a.x = { x: 2 };
,a.x
被賦予新物件,當有新物件就會產生一個新的參考路徑,所以 0x01 的 x
屬性值參考路徑變成 0x02。a.y = a = { y: 1};
為運算式所以它們會同時執行沒有順序問題,而 a.y = a
為最原始的 a 參考路徑 0x01,所以 0x01 多一個 y
屬性,y
的屬性值為新的參考路徑 0x03 ( 有新物件就會產生一個新的參考路徑 )。答案:
//a
{ y: 1 }
//b
x: {
x: 2
}
y: {
y:1
}
由於物件有傳參考的特性,所以當兩物件需要拆開處理,就會產生一些困擾,這時就可使用 for in、淺層複製、深層複製來解決這個問題。
缺點: for in
方式只能做到第一層的複製,第一層以上的都還是會有傳參考特性。
結構: for ( var key in 原物件名稱 ) {}
,當中的 key
為原物件的屬性。
範例 1.
var family = {
name: '小明家',
members: {
father: '老爸',
mom: '老媽',
ming: '小明',
},
}
var newFamily = {};
for (var key in family) {
newFamily[key] = family[key];
}
// 物件內第一層成功拷貝,family與newFamily各自獨立
newFamily.name = '大明家';
console.log(family, newFamily);
// 物件內第二層還是有傳參考特性,所以會一併修改到 family.members.ming
newFamily.members.ming = '大明';
console.log(family.members.ming === newFamily.members.ming);
console.log(family.members.ming === newFamily.members.ming);
會為 true
。記得先載入 jQuery CDN
結構: jQuery.extent({}, 原物件名稱)
範例
var family = {
name: '小明家',
members: {
father: '老爸',
mom: '老媽',
ming: '小明',
},
}
var newFamily = jQuery.extend({}, family);
newFamily.name = '大明';
console.log(family, newFamily);
newFamily.members.ming = '大明';
console.log(family.members.ming === newFamily.members.ming);
console.log(family.members.ming === newFamily.members.ming);
會為 true
。結構: Object.assign({}, 原物件名稱)
範例
var family = {
name: '小明家',
members: {
father: '老爸',
mom: '老媽',
ming: '小明',
},
}
var newFamily = Object.assign({}, family);
newFamily.name = '大明';
console.log(family, newFamily);
newFamily.members.ming = '大明';
console.log(family.members.ming === newFamily.members.ming);
console.log(family.members.ming === newFamily.members.ming);
會為 true
。…
展開的方式 ( 筆者較常用此方式 )結構: { ...原物件名稱 }
範例
var family = {
name: '小明家',
members: {
father: '老爸',
mom: '老媽',
ming: '小明',
},
}
var newFamily = { ...family };
newFamily.name = '大明';
console.log(family, newFamily);
newFamily.members.ming = '大明';
console.log(family.members.ming === newFamily.members.ming);
console.log(family.members.ming === newFamily.members.ming);
會為 true
。不論物件裡面有幾層,都可透過深層拷貝的方式移除物件傳參考的特性。
使用方式: 把原本物件轉為字串再轉回物件,這種方式可以移除傳參考的特性。
結構:
JSON.stringify(原物件名稱)
JSON.parse
把字串再轉回物件 → JSON.parse(JSON.stringify(原物件名稱))
範例
var family = {
name: '小明家',
members: {
father: '老爸',
mom: '老媽',
ming: '小明',
},
}
var newFamily = JSON.parse(JSON.stringify(family));
newFamily.name = '大明';
console.log(family, newFamily);
// 深層拷貝後,改了原物件第二層內的值也不會影響原物值,兩物件 family 與 newFamily各自獨立
newFamily.members.ming = '大明';
console.log(family.members.ming === newFamily.members.ming);
console.log(family.members.ming === newFamily.members.ming);
會為 false
。function changeName(data) {
data.name = '杰倫家';
return data;
}
var family = {
name: '小明',
home: '小明家',
members: {
father: '老爸',
mom: '老媽',
ming: '小明',
}
}
var family2 = Object.assign({}, changeName(family));
family2.members.jay = '杰倫';
console.log(`family.name, ${family.name}`);
console.log(`family.members.jay, ${family.members.jay}`);
console.log(family === family2);
console.log(family.members === family2.members);
Object.assign
執行淺層拷貝時,先執行了 changeName(data)
,物件是傳參考概念而函式也是物件所以也具有傳參考特性。changeName(data)
中 data 參數換成 family,changeName(family)
裡面執行了 family.name = '杰倫家';
會一併修改到 family
的參考路徑中 name
的屬性值,所以 family.name
會印出 杰倫家
。family2.members.jay = '杰倫';
中 members
內為第二層還是保有傳參考特性 ( 0x03 中的 members 參考路徑還是 0 )。答案:
console.log(`family.name, ${family.name}`); // 杰倫家
console.log(`family.members.jay, ${family.members.jay}`); //杰倫
console.log(family === family2); // false
console.log(family.members === family2.members); // true