JavaScript 擁有大多數我們從程式語言中所期待的值:布林值、數字、字串、陣列等。所有 JavaScript 中的正常值都具備 屬性。[9] 每個屬性都有一個 鍵(或 名稱)和一個 值。您可以將屬性視為記錄的欄位。您可以使用 點 (.
) 算子存取屬性:
> var obj = {}; // create an empty object > obj.foo = 123; // write property 123 > obj.foo // read property 123 > 'abc'.toUpperCase() // call method 'ABC'
根據 ECMAScript 語言規範第 8 章,JavaScript 只有六種類型
ECMAScript 語言類型對應到 ECMAScript 程式設計師使用 ECMAScript 語言直接操作的值。ECMAScript 語言類型為:
- 未定義、Null
- 布林值、字串、數字,以及
- 物件
因此,即使建構函式據說有實例,但技術上來說並未引入新的類型。
在靜態 類型語言中,變數、參數和物件的成員(JavaScript 稱之為屬性)在編譯時就具有編譯器已知的類型。編譯器可以使用該資訊執行類型檢查並最佳化已編譯的程式碼。
即使在靜態類型語言中,變數也有一個動態類型,也就是在執行時特定時間點變數值的類型。動態類型可能與靜態類型不同。例如(Java):
Object
foo
=
"abc"
;
foo
的靜態類型為 Object
;其動態類型為 String
。
JavaScript 是動態類型語言;變數的類型通常在編譯時並不知道。
JavaScript 執行非常有限的動態類型檢查
> var foo = null; > foo.prop TypeError: Cannot read property 'prop' of null
不過,大多數情況下,事情會靜默失敗或運作。例如,如果您存取不存在的屬性,您會取得 undefined
值
> var bar = {}; > bar.prop undefined
在 JavaScript 中,處理與類型不符的值的主要方法是將其強制轉換為正確的類型。強制轉換表示隱式類型轉換。大部分運算元都會強制轉換:
> '3' * '4' 12
JavaScript 的內建轉換機制僅支援 Boolean
、Number
、String
和 Object
類型。沒有標準的方法可以將一個建構函式的實例轉換為另一個建構函式的實例。
術語強類型和弱類型沒有普遍有意義的定義。這些術語會被使用,但通常不正確。最好改用靜態類型、靜態類型檢查等術語。
JavaScript對值做了一些武斷的區分:
null
和 undefined
。
兩者之間的主要差異在於它們的比較方式;每個物件都有唯一的識別碼,並且僅(嚴格地)等於它自己
> var obj1 = {}; // an empty object > var obj2 = {}; // another empty object > obj1 === obj2 false > var obj3 = obj1; > obj3 === obj1 true
相反地,編碼相同值的原始值都被視為相同
> var prim1 = 123; > var prim2 = 123; > prim1 === prim2 true
以下兩個區段會更詳細地說明原始值和物件。
true
、false
(請參閱第 10 章)
1736
、1.351
(請參閱第 11 章)
'abc'
、"abc"
(請參閱第 12 章)
undefined
、null
(請參閱undefined 和 null)
原始值具有以下特性:
比較「內容」
> 3 === 3 true > 'abc' === 'abc' true
> var str = 'abc'; > str.length = 1; // try to change property `length` > str.length // ⇒ no effect 3 > str.foo = 3; // try to create property `foo` > str.foo // ⇒ no effect, unknown property undefined
(讀取未知屬性時,總是會傳回 undefined
。)
所有非原始值都是 物件。最常見的物件類型為:
物件具有下列特性
比較身分:每個物件都有自己的身分:
> ({} === {}) // two different empty objects false > var obj1 = {}; > var obj2 = obj1; > obj1 === obj2 true
您通常可以自由變更、新增和移除屬性(請參閱 點運算子 (.): 透過固定鍵存取屬性)
> var obj = {}; > obj.foo = 123; // add property `foo` > obj.foo 123
JavaScript 有兩個「非值」表示遺失的資訊,undefined
和 null
:
undefined
表示「沒有值」(既非原始值也非物件)。未初始化變數、遺失的參數和遺失的屬性具有該非值。此外,如果沒有明確傳回任何內容,函式會隱含傳回該值。 null
表示「沒有物件」。它用於 預期有物件的非值(例如,作為參數、物件鏈中的成員等)。 undefined
和 null
是唯一會導致任何類型的屬性存取產生例外狀況的值
> function returnFoo(x) { return x.foo } > returnFoo(true) undefined > returnFoo(0) undefined > returnFoo(null) TypeError: Cannot read property 'foo' of null > returnFoo(undefined) TypeError: Cannot read property 'foo' of undefined
undefined
有時也會用作表示不存在的元值。相反地,null
表示空值。例如,JSON 節點訪客 (請參閱 透過節點訪客轉換資料) 會傳回
undefined
以移除物件屬性或陣列元素
null
以將屬性或元素設定為 null
以下我們會檢閱 undefined
和 null
發生的各種情境。
未初始化的 變數為 undefined
:
> var foo; > foo undefined
遺漏的 參數為 undefined
:
> function f(x) { return x } > f() undefined
如果您讀取不存在的屬性,您會得到 undefined
:
> var obj = {}; // empty object > obj.foo undefined
而且如果沒有明確傳回任何內容,函式 會隱含傳回 undefined
:
> function f() {} > f() undefined > function g() { return; } > g() undefined
null
是 原型鏈中的最後一個元素 (物件鏈;請參閱 第 2 層:物件之間的原型關係)
> Object.getPrototypeOf(Object.prototype) null
如果字串中沒有符合正規表達式的項目,RegExp.prototype.exec()
會傳回 null
> /x/.exec('aaa') null
在以下各節中,我們會檢閱如何分別檢查 undefined
和 null
,或檢查是否存在任一者。
嚴格相等(===
)是檢查 undefined
的正規方式:
if
(
x
===
undefined
)
...
您也可以透過 typeof
算子(typeof:分類基本型別)檢查 undefined,但您通常應該使用上述方法。
大多數函式允許您透過 undefined
或 null
指示遺失值。檢查兩者的其中一種方法是透過明確比較:
// Does x have a value?
if
(
x
!==
undefined
&&
x
!==
null
)
{
...
}
// Is x a non-value?
if
(
x
===
undefined
||
x
===
null
)
{
...
}
另一種方法是利用 undefined
和 null
都被視為 false
的事實(請參閱真值和假值)
// Does x have a value (is it truthy)?
if
(
x
)
{
...
}
// Is x falsy?
if
(
!
x
)
{
...
}
false
、0
、NaN
和 ''
也被視為 false
。
單一的非值可以同時扮演 undefined
和 null
的角色。為什麼 JavaScript 有這兩個值?原因是歷史性的。
JavaScript 採用 Java 將值分割成基本型別和物件的方法。它也使用 Java 的「不是物件」值 null
。遵循 C(但不是 Java)設定的先例,如果將 null
轉換為數字,null
會變成 0:
> Number(null) 0 > 5 + null 5
請記住,第一個版本的 JavaScript 沒有例外處理。因此,未初始化的變數和遺失的屬性等例外情況必須透過值來指示。null
會是很好的選擇,但當時 Brendan Eich 想避免兩件事
因此,Eich 將 undefined
作為額外的非值新增到語言中。它會轉換為 NaN
> Number(undefined) NaN > 5 + undefined NaN
undefined
是 全域物件的屬性(因此是全域變數;請參閱 全域物件)。在 ECMAScript 3 中,您在讀取 undefined
時必須採取預防措施,因為很容易意外地變更其值。在 ECMAScript 5 中,這是不必要的,因為 undefined
是唯讀的。
為了防止變更 undefined
,有兩種流行的技術(它們仍然與較舊的 JavaScript 引擎相關)
隱藏全域 undefined
(它可能有錯誤的值)
(
function
(
undefined
)
{
if
(
x
===
undefined
)
...
// safe now
}());
// don’t hand in a parameter
在先前的程式碼中,undefined
保證具有正確的值,因為它是一個參數,其值並未由函式呼叫提供。
與 void 0
進行比較,它總是(正確的)undefined
(請參閱 void 運算子)
if
(
x
===
void
0
)
// always safe
三種 基本資料型別布林、數字和字串具有對應的 建構函式:Boolean
、Number
、String
。它們的執行個體(所謂的 包裝物件)包含(包裝)基本資料型別值。建構函式可以用兩種方式使用:
作為建構函式,它們會建立與其包裝的基本資料型別值在很大程度上不相容的物件
> typeof new String('abc') 'object' > new String('abc') === 'abc' false
作為函式,它們會將值轉換為對應的基本資料型別(請參閱 用於轉換為布林、數字、字串和物件的函式)。這是建議的轉換方法
> String(123) '123'
避免使用包裝物件被視為最佳實務。您通常不需要它們,因為物件沒有基本資料型別無法執行的任何功能(除了變異之外)。(這與 JavaScript 從中繼承基本資料型別和物件之間差異的 Java 不同!)
基本資料型別 值(例如 'abc'
)與包裝執行個體(例如 new String('abc')
)有根本上的不同:
> typeof 'abc' // a primitive value 'string' > typeof new String('abc') // an object 'object' > 'abc' instanceof String // never true for primitives false > 'abc' === new String('abc') false
包裝實例是物件,而 JavaScript 中沒有比較物件的方法,甚至透過寬鬆相等 ==
也無法比較(請參閱 相等運算子:=== 與 ==)
> var a = new String('abc'); > var b = new String('abc'); > a == b false
包裝物件有一個使用案例:您想將屬性新增到基本型別值。然後,您包裝基本型別並將屬性新增到包裝物件。您需要解開值,才能使用它。
透過呼叫包裝建構函式來包裝基本型別
new
Boolean
(
true
)
new
Number
(
123
)
new
String
(
'abc'
)
透過呼叫方法 valueOf()
來解開基本型別。所有物件都有這個方法(如 轉換為基本型別 中所述)
> new Boolean(true).valueOf() true > new Number(123).valueOf() 123 > new String('abc').valueOf() 'abc'
正確地將包裝物件轉換為基本型別 會萃取出數字和字串,但不會萃取出布林值:
> Boolean(new Boolean(false)) // does not unwrap true > Number(new Number(123)) // unwraps 123 > String(new String('abc')) // unwraps 'abc'
原因在 轉換為布林值 中說明。
基本型別沒有自己的方法,而是從包裝器借用它們
>
'abc'
.
charAt
===
String
.
prototype
.
charAt
true
草率模式和嚴格模式以不同的方式處理這種借用。在草率模式中,基本型別會在執行時轉換為包裝器
String
.
prototype
.
sloppyMethod
=
function
()
{
console
.
log
(
typeof
this
);
// object
console
.
log
(
this
instanceof
String
);
// true
};
''
.
sloppyMethod
();
// call the above method
在嚴格模式中,包裝器原型中的方法會以透明的方式使用
String
.
prototype
.
strictMethod
=
function
()
{
'use strict'
;
console
.
log
(
typeof
this
);
// string
console
.
log
(
this
instanceof
String
);
// false
};
''
.
strictMethod
();
// call the above method
型別強制轉換 表示將一種型別的值隱式轉換為另一種型別的值。JavaScript 的大多數運算子、函式和方法會強制轉換運算元和參數為它們需要的型別。例如,乘法運算子 (*
) 的運算元會強制轉換為數字:
> '3' * '4' 12
另一個範例,如果其中一個運算元是字串,加號運算子 (+
) 會將另一個運算元轉換為字串
> 3 + ' times' '3 times'
因此,JavaScript 很少會抱怨值具有錯誤的型別。例如,程式通常會接收使用者輸入(來自線上表單或 GUI 小工具)作為字串,即使使用者輸入的是數字。如果您將數字字串視為數字,您不會收到警告,只會得到意外的結果。例如
var
formData
=
{
width
:
'100'
};
// You think formData.width is a number
// and get unexpected results
var
w
=
formData
.
width
;
var
outer
=
w
+
20
;
// You expect outer to be 120, but it’s not
console
.
log
(
outer
===
120
);
// false
console
.
log
(
outer
===
'10020'
);
// true
在類似上述的情況中,您應該在早期階段轉換為適當的型別
var
w
=
Number
(
formData
.
width
);
下列函式是 將值轉換為布林值、數字、字串或物件 的首選方式:
Boolean()
(請參閱 轉換為布林值)
將值 轉換為布林值。下列值會轉換為 false
;它們稱為「假值」:
undefined
、null
false
0
、NaN
''
所有其他值都視為「真值」,並轉換為 true
(包括所有物件!)。
Number()
(請參閱 轉換為數字)
將 值轉換為數字:
undefined
會變成 NaN
。
null
會變成 0
。
false
會變成 0
,true
會變成 1
。
String()
(請參閱 轉換為字串)
將值轉換為字串。它 對所有基本型別都有明顯的結果。例如:
> String(null) 'null' > String(123.45) '123.45' > String(false) 'false'
Object()
(請參閱 將任何值轉換為物件)
將物件轉換為它們自己,undefined
和 null
轉換為空物件,以及將基本型別轉換為包裝的基本型別。例如:
> var obj = { foo: 123 }; > Object(obj) === obj true > Object(undefined) {} > Object('abc') instanceof String true
請注意,Boolean()
、Number()
、String()
和 Object()
是作為函式呼叫的。您通常不會將它們用作建構函式。然後它們會建立它們自己的執行個體(請參閱 基本型別的包裝物件)。
若要 將值轉換為數字或字串,它會先轉換為任意基本型別值,然後再轉換為最終型別(如 轉換為布林值、數字、字串和物件的函式 中所討論)。
ECMAScript 規範有一個內部函式 ToPrimitive()
(無法從 JavaScript 存取),它會執行此轉換。了解 ToPrimitive()
可讓您設定如何將物件轉換為數字和字串。它的簽章如下
ToPrimitive
(
input
,
PreferredType
?
)
選用參數 PreferredType
指出轉換的最終類型:根據 ToPrimitive()
的結果會轉換為數字或字串,因此可能是 Number
或 String
。
如果 PreferredType
是 Number
,則執行下列步驟
input
是基本型別,則傳回它(沒有其他動作)。
input
是物件。呼叫 input.valueOf()
。如果結果是基本型別,則傳回它。
input.toString()
。如果結果是基本型別,則傳回它。
TypeError
(指出無法將 input
轉換為基本型別)。
如果 PreferredType
是 String
,則步驟 2 和 3 會交換。也可以省略 PreferredType
;然後它會被視為日期的 String
和所有其他值的 Number
。這是運算子 +
和 ==
呼叫 ToPrimitive()
的方式。
預設 實作 valueOf()
傳回 this
,而預設實作 toString()
傳回類型資訊:
> var empty = {}; > empty.valueOf() === empty true > empty.toString() '[object Object]'
因此,Number()
會略過 valueOf()
並將 toString()
的結果轉換為數字;也就是說,它會將 '[object Object]'
轉換為 NaN
> Number({}) NaN
下列物件自訂 valueOf()
,這會影響 Number()
,但不會對 String()
造成任何變更
> var n = { valueOf: function () { return 123 } }; > Number(n) 123 > String(n) '[object Object]'
下列物件自訂 toString()
。因為結果可以轉換為數字,所以 Number()
可以傳回數字
> var s = { toString: function () { return '7'; } }; > String(s) '7' > Number(s) 7