在JavaScript,一個原語(primitive–原語值,原語數據類型)是一個數據,不是一個對象並且沒有方法。有七種原語數據類型:
- string
- number
- bigint
- boolean
- null
- undefined
- symbol
一般,原語表現爲語言實現的最底層。
所有的原語是不可改變的
(immutable
)。這裏面不要混淆一個原語本身和一個被分配類原語值的變量。一個變量可以重新分配一個值,但是這個存在的值不能使用objects
,arrays
和functions
改變。
Example
這個例子幫你理解原語值是不可改變的。
// 使用一個字符串方法不能改變一個字符串
var bar = "baz";
console.log(bar); // bar
bar.toUpperCase();
console.log(bar) // bar
// 使用數組方法改變數組
var foo = [];
console.log(foo); // []
foo.push('plugh');
console.log(foo) // ['plugh']
// 賦值給原語一個新(不可改變的)值
bar = bar.toUpperCase() // BAZ
一個原語能夠被替換,但不能直接改變。
另一個例子[遞進]
以下的例子將幫助你理解JavaScript如何處理原語。
// 原語
let foo = 5;
// 定義一個函數改變原語值
function addTwo(num) {
num += 2;
}
// 定義另一個函數做同樣的事情
function addTwo_V2(foo) {
foo += 2;
}
// 傳遞我們的原語作爲參數調用第一個函數
addTwo(foo);
// 得到當前的原始值
console.log(foo); // 5
// 調用第二個函數
addTwo_v2(foo);
console.log(foo); // 5
你認爲它會用7代替5嗎?如果是這樣,讀這段代碼如何運行:
- 對於
addTwo
和addTwo_v2
函數調用,JavaScript查找foo
的值。它能夠找到我們第一個實例化的變量。 - 找到之後,表達式被計算,
foo
被5替換並且JavaScript引擎傳遞值給函數作爲參數。 - 在函數體執行這個聲明之前,
JavaScript獲取原始參數的副本
並且創建一個本地副本。這些副本,僅僅存在函數的作用域之內,是可以通過我們函數定義的標識符訪問的(num
之於addTwo
,foo
之於addTwo_v2
) - 然後,函數的聲明被執行:
- 在第一個函數,一個本地的
num
變量已經被創建。我們將他的值加2,而不是改變原始的foo
的值! - 在第二個函數,一個本地的
foo
變量已經被創建。同樣我們把它加2,而不是原始的(外部的)foo
的值!另外,在這種情況下,外部的foo
值不能被直接訪問。這是因爲JavaScript的詞法作用域和由此產生的變量陰影。本地的foo
隱藏了外部的foo
。對於更多信息,看閉包。(注意:window.foo
仍然可以訪問外部的foo
變量)。
- 在第一個函數,一個本地的
- 總結,任何內部的改變都不會影響原始的
foo
,是由於我們使用了它的副本。
這就是爲什麼原語是不變的— 相反,我們使用了一個副本來代替它,原始值並不受影響。
JavaScript中的原語封裝對象
除了null
和undefined
,所有的原語值都有等價的封裝對象值:
String
對應string原語Number
對應number原語BigInt
對應bigint原語Boolean
對應boolean原語Symbol
對應symbol原語
封裝器的valueOf()
返回原語的值。
參考:Primitive