克服JS 的奇怪部分 #2:認識 Object 和 function
- Published on
- understanding-the-weird-part
物件
- 可以用
new Object
或{}
來建立物件,但不建議使用前者(詳見第五章) - Object 可以有屬性(property)和方法(method)
- Object 參考的是位置,會取得位置對應的屬性/方法,用
.
或[]
來設定或取得都可以,前者比較簡潔(dot/bracket notation)
var person = {}
person['firstName'] = 'Tony'
person.lastName = 'Alice'
// 建立 "firstName" 屬性的記憶體,object 會參考到 "firstName" 的位址
greet(person)
// 將物件作為參數傳入 function greet
greet({ firstName: 'Cathy', lastName: 'Jenny' })
// 也可以在傳入時才建立物件,JS 實際上在執行時會先處理過
補充:JSON 和 object literal syntax 的關係
過去傳送 XML 耗費不必要的資源,所以有了 JavaScript object notation,形式上和 object literal syntax 相似,但還是有不同之處,比如屬性一定得包在 ""
引號
{
"data": {
"basic": {
"name": "John",
"email": "",
"create_time": 1612247227
}
}
}
如果需要轉換兩者,只需透過以下方式
JSON.stringify(object)
轉換成 JSON 字串JSON.parse(JSON)
轉換成物件
函式是物件的一種
在 JS 裡面,function 是 object 的一種,可以像其他型別一樣被設定成變數、被傳入其他地方,被快速創造出來使用,這樣的 function 被稱作是 First-class function:
- 是一種特殊的物件
- 可以將 function 存成變數
- 可以將 function 當成參數代入另一 function
- 可以在一個 function 中回傳另一個 function
- 跟物件一樣有屬性(property)
註:code property 和 name property 備註文字寫錯,要對調位置
當 function 被視為物件時,它還有兩個特別的屬性
- name property:function 的名稱,如果沒有的話就是匿名函式(anonymous function)
- code property:想像 functions 只是用來裝程式碼的容器,function 可被呼叫、新的執行環境會被建立,將執行裡面的程式碼
function greet() {
// 建立名為 greet 的函式
console.log('Hello') // code property
}
greet.language = 'english' // 因為是 object,可以添加屬性
greet() // name property 加上 () => 呼叫函式
console.log(greet) // 印出 code property 內容
參考資料:[筆記] JavaScript 中函式就是一種物件 ─ 談談 first class function
函式:表達式和聲明式
先來認識表達式和聲明式的差別
- 表達式(a unit of code that results in a values)
輸入的那串程式,執行後直接回傳一個值,通常會把表達式回傳的值存成變數,但並不是一定要存。
例如下面都是表達式,會回傳一個值,可以把它們指給一個變數
1 + 2 // 回傳 3
a === 3 // a === 3 會回傳 true
3 // 回傳 3
- 聲明式:會執行程式碼,但不是直接回傳值
const b = if (a === 3) {} // 不能用變數接 if 聲明 ❌
像 if
指令就是函式聲明,它並不會直接回傳一個值,也不能將它指定為一個變數
Function 是物件的一種,它可以透過 expression 或 statements 兩種方式來建立函式:
函式聲明式 Function Statements
不會直接回傳任何的值
函式聲明會被提升(hoisting),在創造階段就被放進記憶體儲存,所以我們可以在執行這段程式前,就先呼叫函式來使用而不會報錯
greet() // it works!
function greeting() {
console.log('hello')
}
函式表達式 Function Expressions
const anonymousGreet = function () {
// function object
console.log('hello')
}
- 同樣在創造階段被放進記憶體 => 匿名函數物件
- 在上面的例子中,
function() {...}
是函式表達式 => 被執行時會讓函數被創造出來,會回傳一個函數物件 - 而
anonymousGreet
變數知道它的位址,所以不需要再寫一個 name property 去參照它
因為一行一行執行,用 anonymousGreet 去指向函式位址,如果在還沒有被賦值前就呼叫的話,會報錯
anonymousGreet() // error!
const anonymousGreet = function () {
console.log('hello')
}
anonymousGreet() // 變數已經指向它的位置,可以呼叫
by value 和 by reference 基本觀念
1. by value
在建立 primitive type 的變數,純值會是用拷貝 value 的方式,拷貝 a 的值填到 b 位址。
var a = 3 // 0x001
var b
b = a // 0x002
;(在不同記憶體位置上) => 彼此不會互相影響
2. by reference
用 object literal 方式來創造物件,指定給 c,那麼就會是 by value、一樣會在記憶體中給它個位置。
var c = { greeting: 'hello' } // 0x001
但如果今天要把變數 c 的值等同於 d 時,因為 =
運算子知道 c 是指向一個物件,所以它不會創造新的記憶體位置給 d,而是讓 d 指向跟 c 一樣的地方
var d
d = c // 0x001
而 function 的參數,同樣會是用 by reference 的做法,如果將 d 作為參數傳進下方的函式,d 和 c 自身都會被改變
function changeGreeting(obj) {
obj.greeting = 'hola'
}
changeGreeting(d) // 同樣改變 d 和 c
參考資料:談談 JavaScript 中 by reference 和 by value 的重要觀念