## 11 值
> 原文: [http://exploringjs.com/impatient-js/ch_values.html](http://exploringjs.com/impatient-js/ch_values.html)
>
> 貢獻者:[lq920320](https://github.com/lq920320)
在本章中,我們將研究 JavaScript 具有哪些值。
 **支持工具:`===`**
在本章中,我們偶爾會使用嚴格相等運算符。如果`a`和`b`相等,`a === b`結果為`true`。具體含義在[12.4 嚴格相等](/docs/15.md#124平等與)詳細解釋。
### 11.1 什么是類型?
在本章中,我將類型視為值的集合。例如,類型 `boolean` 是集 {`false`,`true`} 。
### 11.2 JavaScript 的類型層次結構

圖 6: JavaScript 類型的部分層次結構。缺少的是錯誤類,與基元類型相關的類等等。此圖暗示了并非所有的對象都是 `Object` 的實例。
圖 [6](#fig:type_hierarchy) 顯示了 JavaScript 的類型層次結構。我們從該圖中學到了什么?
- JavaScript 區分兩種值:原始值和對象。我們很快就會看到有什么區別。
- 該圖區分了類 `Object` 的對象和實例。 `Object` 的每個實例也是一個對象,但反之亦然。但是,實際上你在實踐中遇到的所有對象都是 `Object` 的實例。例如,通過對象字面值創建的對象。關于該主題的更多細節在[26.4.3.4 對象并非 `Object` 的實例](/docs/32.md#26434-不是object實例的對象)中進行解釋。
### 11.3 語言規范的類型
ECMAScript 規范已知的總共 7 種類型。這些類型的名稱是(我使用 TypeScript 的名稱,而不是規范的名稱):
- `undefined`:唯一元素`undefined`。
- `null`:唯一元素`null`。
- `boolean`:包含`false`和`true`元素。
- `number`:所有數字的類型(例如`-123`,`3.141`)。
- `string`:所有字符串的類型(例如`'abc'`)。
- `symbol`:所有符號的類型(例如`Symbol('My Symbol')`)。
- `object`:所有對象的類型(與`Object`不同,類`Object`及其子類的所有實例的類型)。
### 11.4 原始值與對象
規范對值進行了重要區分:
- *原始值*是`undefined`,`null`,`boolean`,`number`,`string`,`symbol`類型的元素。
- 所有其他值都是*對象*。
與 Java 相比(啟發了 JavaScript 語言),原始值不是二等公民。它們和對象之間的區別更加微妙。簡而言之,它是:
- 原始值:是 JavaScript 中的原子數據塊。
- 它們是*值傳遞的*:當原始值分配給變量或傳遞給函數時,它們的內容被復制。
- 它們*按值*進行比較:比較兩個原始值時,比較它們的內容。
- 對象:是復合數據。
- 它們是*通過標識*(我的術語)傳遞:當對象被分配給變量或傳遞給函數時,它們的*標識*(想一下指針)被復制。
- 它們是*通過標識*(我的術語)進行比較的:當比較兩個對象時,他們的標識進行比較。
除此之外,原始值和對象非常相似:它們都具有*屬性*(鍵值條目),并且可以在相同的位置使用。
接下來,我們將更深入地研究原始值和對象。
#### 11.4.1 原始值(簡稱:基元)
##### 11.4.1.1 原始值是不可改變的
您無法更改,添加或刪除基元的屬性:
```js
let str = 'abc';
assert.equal(str.length, 3);
assert.throws(
() => { str.length = 1 },
/^TypeError: Cannot assign to read only property 'length'/
);
```
##### 11.4.1.2 原始值的*值傳遞*
基元是*值傳遞的*:變量(包括參數)存儲基元的內容。將原始值分配給變量或將其作為參數傳遞給函數時,會復制其內容。
```js
let x = 123;
let y = x;
assert.equal(y, 123);
```
##### 11.4.1.3 原始值*按值*進行比較
基元*按值*進行比較:當比較兩個原始值時,我們比較它們的內容。
```js
assert.equal(123 === 123, true);
assert.equal('abc' === 'abc', true);
```
要了解這種比較方式有什么特別之處,請繼續閱讀并找出對象的比較方式。
#### 11.4.2 對象
對象的內容將會在[第25章 “單個對象”](/docs/31.md#25單個對象)以及接下來的介紹更多細節。這里我們主要集中于其與原始值有哪些不同。
讓我們首先探討創建對象的兩種常見方法:
- 對象值:
```js
const obj = {
first: 'Jane',
last: 'Doe',
};
```
對象的值以花括號`{}`開頭和結尾。它創建了一個具有兩個屬性的對象。第一個屬性具有鍵`'first'`(字符串)和值`'Jane'`。第二個屬性具有鍵`'last'`和值`'Doe'`。有關對象字值的更多信息,請參閱[25.2.1 對象值:屬性](/docs/31.md#2521對象字面值屬性)的章節。
- 數組值:
```js
const arr = ['foo', 'bar'];
```
Array 值以方括號`[]`開頭和結尾。它創建了一個帶有兩個*元素*的數組:`'foo'`和`'bar'`。有關數組值的更多信息,請參閱[28.2.1 創建、讀取、寫入數組](/docs/35.md#2821數組創建閱讀寫作)。
##### 11.4.2.1 默認情況下,對象是可變的
默認情況下,您可以自由更改,添加和刪除對象的屬性:
```js
const obj = {};
obj.foo = 'abc'; // 添加一個屬性
assert.equal(obj.foo, 'abc');
obj.foo = 'def'; // 改變某個屬性的值
assert.equal(obj.foo, 'def');
```
##### 11.4.2.2 對象是*通過標識傳遞*
對象是*通過標識傳遞*(我的術語):變量(包括參數)存儲對象的*標識*。
對象的標識就像是*堆*(想一下 JavaScript 引擎的共享主內存)上的對象實際數據的指針(或透明引用)。
將對象分配給變量或將其作為參數傳遞給函數時,會復制其標識。每個對象字面值在堆上創建一個新對象并返回其標識。
```js
const a = {}; // fresh empty object
// Pass the identity in `a` to `b`:
const b = a;
// Now `a` and `b` point to the same object
// (they “share” that object):
assert.equal(a === b, true);
// Changing `a` also changes `b`:
a.foo = 123;
assert.equal(b.foo, 123);
```
JavaScript 使用*垃圾回收*自動管理內存:
```js
let obj = { prop: 'value' };
obj = {};
```
現在`obj`的舊值`{ prop: 'value' }`是*垃圾*(不再使用)。在某個時間點(如果有足夠的可用內存,可能永遠不會), JavaScript 會自動將它進行*垃圾回收*(從內存中刪除)。
>  **詳情:通過身份傳遞**
>
> “通過標識傳遞”意味著對象(透明引用)的標識按值傳遞。這種方法也稱為[“通過共享傳遞”](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing)。
##### 11.4.2.3 通過標識比較對象
*通過標識*(我的術語)比較對象:如果兩個變量包含相同的對象標識,則它們僅相等。如果它們引用具有相同內容的不同對象,則它們不相等。
```js
const obj = {}; // fresh empty object
assert.equal(obj === obj, true); // same identity
assert.equal({} === {}, false); // different identities, same content
```
### 11.5 運算符`typeof`和`instanceof`:值的類型是什么?
`typeof`和`instanceof`這兩個運算符可以確定給定值`x`的類型:
```js
if (typeof x === 'string') ···
if (x instanceof Array) ···
```
他們有什么不同?
- `typeof` 區分規范的 7 種類型(減去一個遺漏,加上一個補充)。
- `instanceof` 測試哪個類創建了給定值。
>  **經驗法則:`typeof`用于原始值,`instanceof`用于對象**
#### 11.5.1 `typeof`
表 2: `typeof` 操作符的結果集
| `x` | `typeof x` |
| --- | --- |
| `undefined` | `'undefined'` |
| `null` | `'object'` |
| 布爾值 | `'boolean'` |
| 數字 | `'number'` |
| 字符串 | `'string'` |
| 符號 | `'symbol'` |
| 函數 | `'function'` |
| 所有其他對象 | `'object'` |
表 [2](#tbl:typeof-results) 列出`typeof`的所有結果。它們大致對應于語言規范的 7 種類型。唉,有兩個不同之處,它們是語言怪癖:
- `typeof null` 返回 `'object'` 而不是`'null'`。那是一個錯誤。不幸的是,它無法修復。 TC39 嘗試這樣做,但它在網絡上打破了太多代碼。
- 函數的`typeof`應該是`'object'`(函數是對象)。為功能引入單獨的類別令人困惑。
>  **練習:`typeof`** 的兩個練習
>
> - `exercises/operators/typeof_exrc.js`
> - 額外:`exercises/operators/is_object_test.js`
#### 11.5.2 `instanceof`
該運算符回答了問題:是否有一個類`C`創建了值`x`?
```js
x instanceof C
```
例如:
```js
> (function() {}) instanceof Function
true
> ({}) instanceof Object
true
> [] instanceof Array
true
```
原始值不是任何實例:
```js
> 123 instanceof Number
false
> '' instanceof String
false
> '' instanceof Object
false
```
>  **練習:`instanceof`**
>
> `exercises/operators/instanceof_exrc.js`
### 11.6 類和構造函數
JavaScript 的對象原始工廠是*構造函數*:如果通過 `new` 操作符調用它們,則返回自身的“實例”的普通函數。
ES6 引入了構造函數最好的語法,*類*。
在本書中,我可以互換地使用術語*構造函數*和*類*。
類可以看作是將規范的單一類型 `object` 劃分為子類型——它們給出了比規范中有限的 7 種類型更多的類型。每個類都是由它創建的對象的類型。
#### 11.6.1 與基本類型關聯的構造函數
每個基本類型(`undefined`和`null`的規范內部類型除外)都有一個關聯的*構造函數*(想一下類):
- 構造函數`Boolean`與布爾值相關聯。
- 構造函數`Number`與數字相關聯。
- 構造函數`String`與字符串相關聯。
- 構造函數`Symbol`與符號相關聯。
每個函數都扮演著幾個角色。例如,`Number`:
- 可以將其用作函數并將值轉換為數字:
```js
assert.equal(Number('123'), 123);
```
- `Number.prototype`提供數字的屬性。例如,方法`.toString()`:
```js
assert.equal((123).toString, Number.prototype.toString);
```
- `Number`是數字工具函數的命名空間/容器對象。例如:
```js
assert.equal(Number.isInteger(123), true);
```
- 最后,你還可以將`Number`用作類并創建數字對象。這些對象與實數不同,應該避免。
```js
assert.notEqual(new Number(123), 123);
assert.equal(new Number(123).valueOf(), 123);
```
##### 11.6.1.1 包裝原始值
與基本類型相關的構造函數也稱為*包裝類型*,因為它們提供了將原始值轉換為對象的規范方法。在此過程中,原始值被“包裝”在對象中。
```js
const prim = true;
assert.equal(typeof prim, 'boolean');
assert.equal(prim instanceof Boolean, false);
const wrapped = Object(prim);
assert.equal(typeof wrapped, 'object');
assert.equal(wrapped instanceof Boolean, true);
assert.equal(wrapped.valueOf(), prim); // unwrap
```
包裝在實踐中很少有用,但它在語言規范內部使用,以提供原語屬性。
### 11.7 在類型之間轉換
在 JavaScript 中,有兩種方法可以將值轉換為其他類型:
- 顯式轉換:通過`String()`等功能。
- *強制*(自動轉換):當操作接收到無法使用的操作數/參數時發生。
#### 11.7.1 類型之間的顯式轉換
與基本類型關聯的(構造)函數顯式地將值轉換為該類型:
```js
> Boolean(0)
false
> Number('123')
123
> String(123)
'123'
```
你還可以使用`Object()`將值轉換為對象:
```js
> typeof Object(123)
'object'
```
#### 11.7.2 強制(類型之間的自動轉換)
對于許多操作,如果操作數/參數的類型不匹配,JavaScript 會自動轉換它們。這種自動轉換稱為*強制*。
例如,乘法運算符將其操作數強制轉換為數字:
```js
> '7' * '3'
21
```
許多內置函數也強制執行。例如,`parseInt()`將其參數強制轉換為字符串(解析在第一個不是數字的字符處停止):
```js
> parseInt(123.45)
123
```
>  **練習:將值轉換為基元**
>
> `exercises/values/conversion_exrc.js`
> **測驗**
>
> 參見[測驗應用程序](/docs/11.md#91測驗)。
- I.背景
- 1.關于本書(ES2019 版)
- 2.常見問題:本書
- 3. JavaScript 的歷史和演變
- 4.常見問題:JavaScript
- II.第一步
- 5.概覽
- 6.語法
- 7.在控制臺上打印信息(console.*)
- 8.斷言 API
- 9.測驗和練習入門
- III.變量和值
- 10.變量和賦值
- 11.值
- 12.運算符
- IV.原始值
- 13.非值undefined和null
- 14.布爾值
- 15.數字
- 16. Math
- 17. Unicode - 簡要介紹(高級)
- 18.字符串
- 19.使用模板字面值和標記模板
- 20.符號
- V.控制流和數據流
- 21.控制流語句
- 22.異常處理
- 23.可調用值
- VI.模塊化
- 24.模塊
- 25.單個對象
- 26.原型鏈和類
- 七.集合
- 27.同步迭代
- 28.數組(Array)
- 29.類型化數組:處理二進制數據(高級)
- 30.映射(Map)
- 31. WeakMaps(WeakMap)
- 32.集(Set)
- 33. WeakSets(WeakSet)
- 34.解構
- 35.同步生成器(高級)
- 八.異步
- 36. JavaScript 中的異步編程
- 37.異步編程的 Promise
- 38.異步函數
- IX.更多標準庫
- 39.正則表達式(RegExp)
- 40.日期(Date)
- 41.創建和解析 JSON(JSON)
- 42.其余章節在哪里?