Alex Liang

在新創公司擔任軟體工程師,記錄我的學習歷程

JavaScript: Understaning the Weird Parts上課筆記

這篇記錄我在Udemy的JS課程上課筆記 (不斷更新)

call by value & call by reference

在js中,任何基本型別的變數皆為call by value,而object或是函式的參數則是call by reference

call by value: 當一個變數使用 = 指向另一個變數時,二者在記憶體存放的位址是分開的,也就是說變數b改變了並不會影響變數a

1
2
3
4
5
6
7
8
9
const a = 3;
let b = a;

console.log(a); // 3
console.log(b); // 3

b = 4;
console.log(a); // 3
console.log(b); // 4

call by reference: 二個物件皆放在同一個記憶體位址,當一個物件改變也會影響到另一個物件

1
2
3
4
5
6
7
8
9
const a = { greeting: 'Hi' };
const b = a;

console.log(a); // { greeting: 'Hi' }
console.log(b); // { greeting: 'Hi' }

b.greeting = 'Hello';
console.log(a); // { greeting: 'Hello' }
console.log(b); // { greeting: 'Hello' }

有了這個觀念之後,之後操作物件或傳參數至function時就要注意會不會動到原來的值。

object, function and this

javascript的this常讓人感到困惑,先看個例子

1
2
3
4
5
function a() {
console.log(this);
}

a();

當呼叫a時會印出window object

1
2
3
4
5
6
7
8
const b = {
name: 'Object',
log: function() {
console.log(this);
}
}

b.log();

上面這個例子,this印出來會是b object的內容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const b = {
name: 'Object',
log: function() {
console.log(this);

const setName = newName => {
this.name = newName;
}
setName('Updated object');
console.log(this);
}
}

b.log();

這次加入setName函式至log method中,並且該函式會改變this.name,此時結果會如何呢?

1
2
Object { name: 'Object', log: function }
Object { name: 'Object', log: function }

setName並沒有改變b object,而是改到window object

這也是js原始設計的小缺點,如果我們要讓setName改變b object,則需要加個變數:

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
const b = {
name: 'Object',
log: function() {
const self = this;
console.log(self);

const setName = newName => {
self.name = newName;
}
setName('Updated object');
console.log(self);
}
}
```
透過self指向b的記憶體位址,在setName函式裡即可正確的更動name。


### call(), apply() and bind()
11/24更新
在Javascript裡Function是一種特殊的object,它有可執行的程式碼(CODE property),NAME(可為空,即是匿名著函式)以及這裡要介紹的call, apply和bind

``` javascript
const person = {
firstName: 'Alex',
lastName: 'Liang',
getFullName: function() {
return `${this.firstName} ${this.lastName}`;
}
}

// 直接呼叫此函式,this會是undefined
const logName = (lang1, lang2) => {
console.info(`Logged: ${this.getFullName()}`);
};

const logPersonName = logName.bind(person);

logPersonName(); // Logged: Alex Liang

// 使用call達成一樣的效果
logName.call(person, 'en', 'cht');

// apply則是將參數放在array裡
logName.apply(person, ['en', 'cht']);

此段程式碼使用bind將logName函式和person物件結合並產生新的函式logPersonName
這也表示透過bind能改變函式內this的對象

而call不需要做出一個新的函式,直接將person傳入也能達成一樣的效果;apply和call不同之處在於傳入的參數放在array裡,其效果也是一樣。

這三個函式的應用可以參考下列程式碼

1
2
3
4
5
6
7
8
// 接續上一段
// function borrowing
const person2 = {
firstName: 'Ben',
lastName: 'Simmons'
};

person.getFullName.apply(person2); // Logged: Ben Simmons

透過apply,我們讓person2使用getFullName

1
2
3
4
5
6
7
8
// function currying
function multiply(a, b) {
return a*b;
}

const multipleByTwo = multiply.bind(this, 2);

console.info(multipleByTwo(4)); // 8

透過事先設定的參數,我們可以建立許多函式做組合,這也是functional programming的基礎。