JavaScript
Variable
本篇為 [JS201] 進階 JavaScript:那些你一直搞不懂的地方 這門課程的學習筆記。如有錯誤歡迎指正!
在第二週 [JS101] 用 JavaScript 一步步打造程式基礎 學習JavaScript 基礎時,我們就曾提及關於值的型態,以及該如何判斷資料型態。
而資料型態的不同,可能會造成一些操作結果與想像不符,這部分我們後面會進行討論。
關於值的型態,大致可分為原始型態和物件型態兩種:
'Hello World'
其中原始型態具有 Immutable(不可變動)的特性,相對於物件型態是 Mutable(可變的)。這裡指的不可變動不是「賦值」,而是不能改變原本的記憶體位置。
也就是說,若對其有任何變更(例如:新增、修改、刪除),就會回傳一個新值。以下列程式碼為範例:
typeof <value>
:用來判斷變數型態我們可使用 typeof
來判斷變數的資料型態,輸入結果會回傳一個字串,語法範例如下:
結果得到 true 的資料型態是 boolean。
接著我們再看看其他範例結果:
由結果可知,array 和 null 也屬於 object 型態,但前面不是說 null 的屬於原始型態嗎?這其實是 JavaScript 的歷史 bug,詳細內容可查閱下方參考資料:
null 使用 typeof 運算子,回傳的結果會是字串 "object",這指出 null 可被認為是象徵「無物件」(no object)的一種特殊物件值。(參考資料:犀牛書)
這其實是 JavaScript 最初發現的一個錯誤,然後被 ECMAScript 沿用至今。現在,null 被認為是物件的佔位符,從而解釋了這一矛盾。 (參考資料:你懂JavaScript 嗎?#4 型別(Types))
以下是在 MDN 網站 列出 typeof 的可能回傳值:
typeof
確認變數是否有使用到我們還可以利用 typeof
來確認某個變數是否有使用到(是否有被宣告),以下列程式碼為例:
若沒有先宣告變數 a,直接使用 typeof
檢查也會得到相同結果:
若應用在判斷句,在有宣告變數 a 的情況:
在不宣告變數 a 的情況下,直接利用 typeof
進行判斷:
若直接判斷變數 a 是否等於 undefined,就會出現未定義 a 的錯誤:
因此,若使用 typeof() 來判斷 a 是否為 undefined,就能夠避免出現錯誤,導致程式中斷。
Array.isArray()
:判斷變數是否為陣列若想檢查是否為陣列,可使用函式 Array.isArray()
,檢查傳入的值是否為一個 Array,範例如下:
但使用時須注意,一些較舊的瀏覽器並不支援 Array.isArray()
這個語法,因此更推薦的方法如下。
Object.prototype.toString
:用來判斷型態Object.prototype.toString
是另一種判斷型態的方式,結果也會比 typeof
還要準確,尤其物件型態會顯示更詳細的類別。
語法範例如下:
在課程第二週 JS101 的「從 Object 的等號真正的理解變數」中我們也曾提到相關概念。
若將變數視為一個箱子,在放入數字的情況下,兩者會相等:
但如果在變數 obj 裡放入物件,結果卻是不相等:
可想像成是「記憶體位置不同」導致的結果。儘管兩個箱子儲存的數值相同,但因記憶體位置不同,指向的元素不同,所以不會相等。
如下方示意圖:
=
等號賦值如果換成下列情形,也就是將 obj 賦值給 obj2 時:
兩者理所當然會相等,此時若以 obj2.a = 2
更改 obj2 物件中 a 的值,會連同 obj 的值也一起更動:
之所以 obj 的值也一起被更改,是因為 obj 和 obj2 指向了相同記憶體位置(0x01),也就是指向同一個物件:
但如果以 obj2 = {b:1}
將 obj2 賦值一個新的物件,此時就會指向一個新的記憶體位置。以下方程式碼為例:
會發現 obj2 和 obj 不相等,這是因為「往裡面放東西」與「改放全新的東西」是兩件完全不同的事情。後者會指向一個新的記憶體,可參考下圖理解:
若以陣列為例,會得到相同的結果,以下列程式碼為例:
賦值後的 arr2 會指向新的記憶體位置,因此兩者的值會不相同,可想像成 arr: 0x10
和 arr: 0x20
。
==
與 ===
的差別=
:代表賦值==
和 ===
:均用來判斷是否相等,差別在於是否判斷值的型態。原因是 ==
判斷過程會進行型態轉換結論:盡量使用三個等號進行判斷,如此最能夠避免因型態不同而發生錯誤。
以下列程式碼為例:
再以陣列作為範例:
之所以兩者不會相等,和前面提到的「記憶體位置不同」有關,可想像成:
也因此,不管裡面放相同參數或均為空陣列,兩者都不會相等,一定要加上 arr2 = arr
才會使等號成立。
同理,當我們比較空陣列或空物件時,結果也不會相等,因為比較的是兩者的記憶體位置:
在什麼樣的情況下會產生 NaN 呢?以「將字串轉換成 Number」為例,因無法轉換所以會得到 NaN:
這時如果再以等號進行判斷,結果會是:
什麼~~~~~?!同一個變數結果竟然會不相等?!(震驚ing)
為何會發生自己不等於自己的情況呢?這是因為 NaN === NaN
判斷結果不相等造成,屬於特殊案例。
至於要如何檢視變數是否為 NaN,可使用函式 isNaN()
:
接著談到宣告變數的方式,除了習慣使用的 var(variable 變數),在 ES6 還引入了 let 和 const(constant 常數) 兩種宣告方式。這三者之間最大差別,主要在於作用域不同,在之後的 Scope 章節會再詳細說明。
但我們可以去改變物件 obj 裡面的值,如以下範例:
如果直接賦予 obj 新的值,就會出現錯誤:
這和前面提到的記憶體位置觀念相同,const 說的不能改變,其實是不能改變「該記憶體位置」。obj 是存記憶體位置,number 則是存 value。也因此賦值給 obj 就代表改變記憶體位置。
參考資料: