javascript核心篇 - 物件 partII

javascript核心篇 - 物件 partII

最近在上六角學院的js核心篇,將筆記整理出來跟大家分享。

物件與純值

在javascript中只有兩種型別:純值和物件。純值(字串, 數字和布林)無法新增屬性,但物件可以。陣列和函式也是物件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var string = 'a string'
//建構式
var newString = new String('a new string')

console.log(string) //'a string'
console.log(newString)
/*String {"a new string"}
0: "a"
1: " "
2: "n"
3: "e"
4: "w"
5: " "
6: "s"
7: "t"
8: "r"
9: "i"
10: "n"
11: "g"*/

newString.name = 'a name'
console.log(newString)
/*
String {"a new string", name: "a name"}
0: "a"
1: " "
2: "n"
3: "e"
4: "w"
5: " "
6: "s"
7: "t"
8: "r"
9: "i"
10: "n"
11: "g"
name: "a name"
* */

newString是使用建構式產生的,此時它就不是純值(字串),而是物件,可以被賦予新的屬性和方法

*陣列也是物件,陣列是物件底下的一種子型別

未定義的物件屬性預設值

若是查找物件下沒有定義過的屬性,會得到undefined的結果,在undefined上無法新增任何屬性。

1
2
3
4
5
6

var family = {
name:'貓貓家'
}

console.log(family.dad) //undefined

物件的參考特性

傳值 vs 傳參考

傳值
person和person2是兩個完全不同的變數,person2只是得到跟person的複製品,其本身與person已無關

1
2
3
4
5
6
var person = '小花'
var person2 = person;
person2 = '花花'

console.log(person, person2) //小花 花花
console.log(person === person2) //false

傳參考
person和person2這兩個物件共享同一個參考位置,參考位置上的屬性變,共享這個位置的物件的相應屬性也會改變

1
2
3
4
5
6
7
8
9
var person = {
name:'小花'
}
var person2 = person;
person2.name='花花'

console.log(person, person2) // {name:花花} {name:花花}
console.log(person === person2) //true
//這兩個物件共享同一個參考位置,參考位置上的屬性變,共享這個位置的物件的相應屬性也會改變

純值傳值(布林/數字/字串/undefined/null),物件傳參考(物件/陣列/函式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var person = {
name:'小花'
}
var person2 = person;
//此時person和person2共享參考位置, 兩者嚴格相等

//這時person2被重新宣告,指向另一個參考路徑
//此時person和person2已不相等
person2.name={
name:'小花'
}

console.log(person, person2) // {name:小花} {name:小花}
console.log(person === person2) //false
//這兩個物件共享同一個參考位置,參考位置上的屬性變,共享這個位置的物件的相應屬性也會改變

一些觀念練習題:

1
2
3
4
5
6
7

var x = {x:1}
x.y = x;

console.log(x)
/* 不斷指向自己本身
* x: 1 ,y: {x: 1, y: {x:1, y:{x:1, .....}*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

var a = {x:1}
var b = a
//此時a === b


a.y = a = {x:2}
//同時執行兩件事:a.y = {x:2} a = {x:2}
//此時a = {x:2} 參考位置已不同 a = {x:2}這個新位置
//而a.y這時的參考位置還沒有變,此時的a.y === b.y,b.y被賦予{x:2}這個位置

//這時a!==b,在a中並沒有y這個屬性
console.log(a.y) //undefined
console.log(b) //b = {x:1, y:{x:2}}
console.log(a === b.y) //true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
變數      值    參考位置
var a = {x:1} (0x01)
var b = a; (0x01)
a.x = {x:2} (0x02)

a.y = a = {y:1}
//同時執行這兩件事
//a = {y:1} (0x03) a被重新指派
//a.y (0x01) = {y:1} (0x03) === b.y(0x01) = {y:1}


console.log(a) // a(0x03) = {y:1}
console.log(b) //b(0x01) = {x: {x:2}, y:{y:1}}
console.log(b.y === a) //true


var a = {}; 0x01
var b = a; 0x01

var c = {number:1} 0x02
b = {number:1} 0x02
var c = b = {number:1}; //0x02
c.name = '小明'
console.log(a) //{}
console.log(b) //{number: 1, name: "小明"}
console.log(c) //{number: 1, name: "小明"}

Call by Sharing ?

Call by Sharing
以javascript來說,當有多個變數被賦予同個物件時,他們就共享一個參考位置,這就是call by sharing,此時若其中一個物件中的屬性被變動,參考位置上的原始物件內的屬性也會變動,連帶造成其他的物件也都產生相同變化。

淺層 / 深層複製

淺層複製 shallow copy
使用for in

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var family = {
name:'貓貓家',
members:{
father:'爸爸貓',
mother: '媽媽貓',
son:'喵喵貓'
}
}
var newFamily = {};
//列出family物件中的所有屬性
for(var key in family){
console.log(key); //name members
//讓newFamily擁有family中一樣的屬性
newFamily[key] = family[key];
}
newFamily.name = '狗狗家'
console.log(family, newFamily) //{name:貓貓家...} {name:狗狗家...}
console.log(family === newFamily) //false
//這兩個物件是完全不同的物件,不共享相同的參考位置

要注意的是,這種方法只能複製最外層的物件,也就是family物件,但是內層的members,即使在for迴圈中被複製過去,newFamily和family中的members還是一樣的(因為沒有為它開闢新的記憶體空間)

使用jquery

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

ES6

1
var newFamily2 = Object.assign({}, family)

深層複製

將原物件轉為字串後,讓它失去物件的特性,再parse回來。這樣一來新物件和舊物件會成為完全獨立的物件,裡面的屬性即使包含多少層物件,參考位置也已經完全不同。

1
2
3
4
5
6
7
8
9
10
11
12
console.log(JSON.stringify(family))
//""{"name":"貓貓家","members":{"father":"爸爸貓","mother":"媽媽貓","son":"喵喵貓"}}""

let newFamily = JSON.parse(JSON.stringify(family))
console.log(newFamily === family) //false
//此時newFamily跟family已經沒有任何關聯

newFamily.members.father = '爸爸狗'
console.log(newFamily)
/*name: "貓貓家"
members: {father: "爸爸狗", mother: "媽媽貓", son: "喵喵貓"}
__proto__: Object*/

陣列

陣列雖然在js中被視為物件,但是取值方式只有使用中括號一種,只能用push方法新增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//literal
let newArray = [1, 2, 'string', true, {
name:'小明'
}]

console.log(newArray)
/*
0: 1
1: 2
2: "string"
3: true
4: {name: "小明"}*/

//取值
newArray[0] //1

//新增
newArray.push('new value')

//增加屬性
newArray.name = '小花'
/*
0: 1
1: 2
2: "string"
3: true
4: {name: "小明"}
5: "new value"
name: "小花"*/
//注意: 此時小花是屬性,而不是陣列內的元素

//目前陣列長度為5, 若是直接新增一個為在第10位的值會如何?

newArray[10] = '我是第十位'
newArray.forEach((item, index) =>{
console.log(`第${index}位`, item)
})
console.log(newArray.length) //11

/*
第0位 1
第1位 2
第2位 string
第3位 true
第4位 {name: "小明"}
第5位 new value
第10位 我是第十位*/
//第6~9會是空值,屬於他們的記憶體位置有存在,但是沒有值
console.log(newArray[6]) //undefined

JSON

JSON跟物件的不同之處 - 它是獨立於程式語言之外的文字。
JSON是字串,且屬性名稱一定用雙引號撰寫

1
2
3
4
5
6
7
8
9
10
let example = {
property: 'value',
}
//將物件轉為json格式
let jsonExample = JSON.stringify(example)

//{"property":"value"}

//將JSON轉為JS
JSON.parse(jsonExample)
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×