[JavaScript] 型別系統 — Primitive & Object/Reference Type

Anin Huang
8 min readSep 13, 2019
Photo by Bonnie Kittle on Unsplash

在 JavaScript 中,有原始型別和物件型別二種。

Primitive Type 原始型別(或稱基本型別)

細分成以下六種:

string、number、boolean、null、undefined、symbol(於 ECMAScript 6 新定義)

  • string — 字串,為零或多個字元組成的有限序列
  • number — 整數、浮點數…etc
  • boolean — true 或 false
  • null — 空值
  • undefined — 已宣告的變數,但尚未賦值
  • symbol — 唯一值

除了上述外,Everything else is an object 🙌🏻

其它皆為物件,歸類於 Object/Reference Type 物件型別(或稱參考型別)

首先,要知道幾件事,

  • 原始型別只是一個值,無法設定屬性和方法,且不可變(immutable)

以下範例使用 String.prototype 下的內建方法,得到處理後新的回傳值,但實際上並不會修改到它原本的值:

var a = 'hello world';a.toUpperCase(); //'HELLO WORLD'a.slice(-5); //'world'console.log(a); //'hello world'

可能你又會想,

為什麼說原始型別無法設定屬性和方法,但像 string 內建不是有讀取字串長度的 length 屬性嗎?剛剛也有使用到 slice 方法擷取某一段字串阿@@

var a = 'hello';a.length //5

這是因為…

當我們試著讀取字串 a 的屬性時,JavaScript 會先透過 new String(a) 將字串值強制轉型(coercion)成物件。這邊的 new 關鍵字和 String() 建構子用來創建一個新物件,而 String() 同時也是一個 object wrapper,它繼承了所有字串的屬性和方法,在讀取屬性、呼叫方法完後,object wrapper 就會被丟棄。

也有另一種說法,把上述行為稱為 Boxing 📦(不過這個用詞沒有出現在官方文件中)。

“Boxing” is wrapping an object around a primitive value

概念就是,原始型別會被自動裝箱(boxing)成物件型別,用完再自動拆箱(unboxing),以節省記憶體空間。

其中,有三個原始型別 string、number、boolean 存在著它對應的物件型別(或可說成是 object wrapper),分別為 String、NumberBoolean

以下用 typeof 運算子檢驗其型別:

<!-- 原始型別 -->typeof 'Hello'; //'string'typeof 10; //'number'typeof true; //'boolean'____________________________________________________________________var a;typeof a; //'undefind'var b = null;typeof b; //'object'(不要懷疑!🤨此為 JavaScript 永久錯誤,怕會影響現有程式而不再修正)____________________________________________________________________typeof String('Hello'); //'string'typeof Number(10); //'number'typeof Boolean(true); //'boolean'____________________________________________________________________<!-- 物件型別 -->typeof new String('Hello'); //'object'typeof new Number(10); //'object'typeof new Boolean(true); //'object'

而物件型別可以再用 Object.prototype.valueOf() 取得原始型別:

typeof (new String('Hello')).valueOf(); //'string'typeof (new Number(10)).valueOf(); //'number'typeof (new Boolean(true)).valueOf(); //'boolean'
  • 原始型別皆屬於傳值(Passed by Value)

此例先把原始型別數字 10 賦值到變數 a 中,再把 a 的值賦予至新變數 b ,並改變 b 值為 1010,最後印出 ab 的結果,很直觀地會是 101010

原因是當 ab 在被宣告時,會在記憶體中各自產生一個位置,彼此獨立、互不影響。

var a = 10;
var b = a;
b = 1010;console.log(a); //10
console.log(b); //1010

而原始型別在比較時,是以真實的值進行比較:

var x = 1;
var y = true;
console.log(x == y); //true
console.log(x === y); //false

上述 == (標準相等)可參考 ECMAScript-262 5.1 版標準的第 7 點

If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).

如此可得知,== 會透過 ToNumber 內部運算自動進行隱性轉換,true 會被強行轉為 1,所以 1 == 1 的結果得到 true。

而 === (嚴格相等)可參考 ECMAScript-262 5.1 版標準的第 1 點

If Type(x) is different from Type(y), return false.

=== 一開始就會進行型別的比較,number 和 boolean 型別不同,結果因此為 false。

Object/Reference Type 物件型別(或稱參考型別)

含以下幾種:

Array、Object、Function

而像 JavaScript 內建物件 DateMathRegExpJSONWindowDocument… 都算在此類!

首先,先了解什麼是「物件」?

JavaScript 將物件定義為無序的屬性集合。 換句話說,物件是由「鍵/名稱」 (key/name) 與「值」 (value) 組成,其中值可以是原始值、物件或函數。

var dog = {
name: 'Eta',
type: 'Shiba Inu'
age: 5,
address: {
country: 'Canada',
city: 'Vancouver, BC'
},
bark: function() {
console.log(this.name + ' goes bow-wow!');
}
}
  • 物件型別可以設定屬性和方法,為可變(mutable)

承上範例,使用 . 存取 dog 物件的屬性 age 並修改其值,方式如下:

dog.age = 7;console.log(dog);

//由印出結果可知,原物件 dog 的屬性 age 已被更改為 7
//{
// name: 'Eta',
// type: 'Shiba Inu',
// age: 7,
// address: {
// country: 'Canada',
// city: 'Vancouver, BC'
// },
// bark: function() {
// console.log(this.name + ' goes bow-wow!');
// }
//}
  • 物件型別皆屬於傳址(Passed by Reference)

此例先把一個物件 {name: ‘Eta’} 賦值到變數 a 中,再將物件 a 賦予至新變數 b ,並改變物件 bname 屬性值為 Momo,最後印出原 aname 屬性,會發現它的值也一起被修改了,而比較 ab,結果為相等。

原因是當a 賦予至新變數 b 時,其實不會再創建一個新的記憶體位置,反之,兩個變數的值都是儲存在相同位置、指向同一個物件。

var a = {name: 'Eta'};
var b = a;
b.name = 'Momo';console.log(a.name); //'Momo'
console.log(a === b); //true

📖 參考資料:

JavaScript — Reference vs Primitive Values/ Types

Understanding JavaScript Objects

JavaScript Primitive vs. Reference Values

--

--

Anin Huang

Hi, I am a web developer with a particular focus on interactive and aesthetic design. Check out my GitHub page: https://github.com/AninHuang