《JavaScript語言入門教程》記錄整理:運算子、語法和標準庫
阿新 • • 發佈:2020-08-04
[toc]
本系列基於阮一峰老師的[《JavaScrip語言入門教程》](https://wangdoc.com/javascript/index.html)或《JavaScript教程》記錄整理,教程採用[知識共享 署名-相同方式共享 3.0協議](https://creativecommons.org/licenses/by-sa/3.0/deed.zh)。這幾乎是學習js最好的教程之一(去掉之一都不過分)
最好的教程而阮一峰老師又採用開源方式共享出來,之所以重新記錄一遍,一是強迫自己重新認真讀一遍學一遍;二是對其中知識點有個自己的記錄,加深自己的理解;三是感謝這麼好的教程,希望更多人閱讀了解
# 運算子
## 算數運算子
1. js提供了10種運算子
- 加法運算子:`x + y`
- 減法運算子:`x - y`
- 乘法運算子:`x * y`
- 除法運算子:`x / y`
- 指數運算子:`x ** y`
- 餘數運算子:`x % y`
- 自增運算子:`++x` 或者 `x++`
- 自減運算子:`--x` 或者 `x--`
- 數值運算子: `+x`
- 負數值運算子:`-x`
2. js中非數值可以相加,比如布林值與數值相加,字串相加用於連線兩個字串
```js
true + true // 2
1 + true // 2
1 + 'a' // "1a"
false + 'a' // "falsea"
```
加法運算子是在執行時決定,到底是執行相加,還是執行連線。**運運算元的不同,導致了不同的語法行為,這種現象稱為“過載”(overload)**。
```js
'3' + 4 + 5 // "345"
3 + 4 + '5' // "75"
```
加法運算子存在過載。**減法、除法和乘法等運算子不會過載:所有運運算元一律轉為數值,再進行相應的數學運算**。
2. 物件的相加:運運算元是物件時,會先轉成原始型別的值,然後再相加。
物件預設轉成原始型別的值是`[object Object]`
```js
var obj = { p: 1 };
obj+5 // "[object Object]5"
```
物件轉成原始型別的值,規則:
- 自動呼叫物件的`valueOf`方法。物件的`valueOf`方法預設返回物件自身
- 再呼叫`toString`方法轉為字串。物件的`toString`方法預設返回`[object Object]`
自定義`valueOf`方法或`toString`方法(同時改寫兩個方法時要小心),改變物件相加的結果
```js
obj.valueOf() // {p: 1}
obj.valueOf().toString() // "[object Object]"
obj.valueOf=function () {
return 1;
}
obj+5 // 6
```
唯一的特例是,當運運算元是`Date`物件時,會優先執行`toString`方法
```js
var obj = new Date();
obj.valueOf = function () { return 1 };
obj.toString = function () { return 'hello' };
obj + 5 // "hello5"
```
3. 餘數運算子(`%`)返回前一個運運算元被後一個運運算元除所得的餘數。結果的正負號由第一個運運算元決定
```js
-1 % 2 // -1
1 % -2 // 1
```
可以使用絕對值,獲得負數的正確餘數值
```js
// 正確的寫法
function isOdd(n) {
return Math.abs(n % 2) === 1;
}
isOdd(-5) // true
isOdd(-4) // false
```
4. 自增和自減運算子是一元運算子,只有一個運運算元。
> 運算之後,變數的值發生變化,這種效應叫做運算的副作用(`side effect`)。自增和自減運算子是僅有的兩個具有副作用的運算子,其他運算子都不會改變變數的值。
自增/自減放在變數後面,會先返回變數操作前的值,再進行自增/自減操作
自增/自減放在變數之前,會先進行自增/自減操作,再返回變數操作後的值
5. 數值運算子(`+`)的作用可以將任何值轉為數值(與Number函式作用相同)
負數值運算子(`-`),將一個值轉為數值的負值
**不會改變原始變數的值,而是返回新值**
6. 指數運算子(`**`)完成指數運算
**指數運算子是右結合**,而不是左結合。即多個指數運算子連用時,先進行最右邊的計算。
```js
// 相當於 2 ** (3 ** 2)
2 ** 3 ** 2 // 512
```
7. 賦值運算子(`Assignment Operators`)用於給變數賦值。還有複合的賦值運算子,如`x += y`、`x -= y`
## 比較運算子
1. 比較運算子比較兩個值的大小,並返回一個布林值。js提供了8個比較運算子
- `>` 大於運算子
- `<` 小於運算子
- `<=` 小於或等於運算子
- `>=` 大於或等於運算子
- `==` 相等運算子
- `===` 嚴格相等運算子
- `!=` 不相等運算子
- `!==` 嚴格不相等運算子
2. 相等比較和非相等比較。
> 對於非相等的比較,演算法是先看兩個運運算元是否都是字串,如果是的,就按照字典順序比較(實際上是比較 Unicode 碼點);否則,將兩個運運算元都轉成數值,再比較數值的大小。
3. 相等運算子(`==`)比較兩個值是否相等,嚴格相等運算子(`===`)比較兩個值是否為“同一個值”。
> 如果兩個值不是同一型別,嚴格相等運算子`===`直接返回false,而相等運算子`==`會將它們轉換成同一個型別,再進行比較
4. 嚴格相等運算子:型別不同返回false;同一型別的原始型別值,會比較兩者的值是否相等;複合型別的值(物件、陣列、函式)比較的是是否指向同一個地址;undefined和null與自身嚴格相等
**兩個物件的比較,嚴格相等運算子比較的是地址,而大於或小於運算子比較的是值**
```js
var obj1 = {};
var obj2 = {};
obj1 > obj2 // 比較的是值 false
obj1 < obj2 // 比較的是值 false
obj1 === obj2 // 比較的是地址 false
```
相等運算子比較是隱含了型別轉換,建議最好只使用嚴格相等運算子(`===`)。
## 布林運算子
1. 布林運算子用於將表示式轉為布林值。一共有4個
- 取反運算子:`!`
- 且運算子:`&&`
- 或運算子:`||`
- 三元運算子:`?:`
2. 取反運算子將布林值變為相反值。兩次取反就是將一個值轉為布林值的簡便寫法
3. 且運算子`&&`常用於多個表示式的**求值**
> 且運算子`&&`運算規則是:如果第一個運運算元的布林值為true,則返回第二個運運算元的值(注意是值,不是布林值);如果第一個運運算元的布林值為false,則直接返回第一個運運算元的值,且不再對第二個運運算元求值。
`&&`且運算可以用來取代`if`語句
```js
if (i) {
doSomething();
}
// 等價於
i && doSomething();
```
4. 或運算子(`||`)也用於多個表示式的求值。
> 或運算子`||`的運算規則是:如果第一個運運算元的布林值為`true`,則返回第一個運運算元的值,且不再對第二個運運算元求值;如果第一個運運算元的布林值為`false`,則返回第二個運運算元的值。
或運算子常用於為一個變數設定預設值。
```js
function saveText(text) {
text = text || '';
// ...
}
// 或者寫成
saveText(this.text || '')
```
5. 且運算子和或運算子,這種通過第一個表示式(運運算元)的值,控制是否執行第二個表示式(運運算元)的機制,就稱為“短路”(`short-cut`)
6. 三元條件運算子(`?:`)是js中唯一一個需要三個運運算元的運算子
## 二進位制位運算子
1. 二進位制位運算子用於直接對二進位制位進行計算,一共有7個:
- **二進位制或運算子**(or):符號為`|`,表示若兩個二進位制位都為0,則結果為0,否則為1。
- **二進位制與運算子**(and):符號為`&`,表示若兩個二進位制位都為1,則結果為1,否則為0。
- **二進位制否運算子**(not):符號為`~`,表示對一個二進位制位取反。
- **異或運算子**(xor):符號為`^`,表示若兩個二進位制位不相同,則結果為1,否則為0。
- **左移運算子**(left shift):符號為`<<`,
- **右移運算子**(right shift):符號為`>>`,
- **頭部補零的右移運算子**(zero filled right shift):符號為`>>>`,
2. 位運算子只對整數起作用,如果一個運運算元不是整數,會自動轉為整數後再執行。雖然在JavaScript內部,數值都是以64位浮點數的形式儲存,但是做位運算的時候,是以32位帶符號的整數進行運算的,並且返回值也是一個32位帶符號的整數
利用這個特性,可以寫出一個函式,將任意數值轉為32位整數。
```js
function toInt32(x) {
return x | 0;
}
```
3. 位運算子可以用作設定物件屬性的開關。(開關作用有些抽象,但很精巧)
假定某個物件有四個開關,每個開關都是一個變數。那麼,可以設定一個四位的二進位制數,它的每個位對應一個開關。A、B、C、D四個開關,每個開關佔有一個二進位制位
```js
var FLAG_A = 1; // 0001
var FLAG_B = 2; // 0010
var FLAG_C = 4; // 0100
var FLAG_D = 8; // 1000
```
- 用二進位制與運算,檢查當前設定是否打開了指定開關
```js
var flags = 5; // 二進位制的0101
// 檢驗是否打開了開關C
if (flags & FLAG_C) { // 0101 & 0100 => 0100 => true
// ...
}
```
- 假設需要開啟`A`、`B`、`D`三個開關,可以先構造一個掩碼變數,然後通過二進位制或運算掩碼變數,可以確保開啟這三個開關
```js
var mask = FLAG_A | FLAG_B | FLAG_D;
// 0001 | 0010 | 1000 => 1011
flags = flags | mask; // 代表三個開關的二進位制位都開啟的變數
```
- 二進位制與運算可以將當前設定中凡是與開關設定不一樣的項,全部關閉
```js
flags = flags & mask;
```
- 異或運算可以切換(`toggle`)當前設定,即第一次執行可以得到當前設定的相反值,再執行一次又得到原來的值。
```js
flags = flags ^ mask;
```
- 二進位制否運算可以翻轉當前設定
```js
flags = ~flags;
```
## void和逗號運算子
1. void運算子,執行一個表示式,然後不返回任何值,或者返回`undefined`
```js
void 0 // undefined
void(0) // undefined 推薦寫法
```
void運算子的優先順序很高,使用括號避免錯誤
```js
var x = 3;
void (x = 5) //undefined
x // 5
```
2. void運算子的主要用途是瀏覽器的書籤工具(Bookmarklet),以及在超連結中插入程式碼防止網頁跳轉。
如下程式碼,點選連結後先執行`onclick`,然後返回`false`,所以瀏覽器不會跳轉
```html
點選
```
`void`運算子可以取代上面的寫法:
```html
文字
```
或者,實現點選連結提交表單,但不產生頁面跳轉
```html
提交
```
3. 逗號運算子用於對兩個表示式求值,並返回後一個表示式的值。
```js
'a', 'b' // "b"
var x = 0;
var y = (x++, 10);
x // 1
y // 10
```
用途是:**在返回一個值之前,進行一些輔助操作。**
```js
var value = (console.log('Hi!'), true);
// Hi!
value // true
```
## 運算順序
1. 運算子優先級別(`Operator Precedence`)高的先執行
2. 圓括號`()`用來提高運算的優先順序(它的優先順序最高),即圓括號中的表示式會第一個運算
> 圓括號不是運算子,而是一種語法結構。它一共有兩種用法:一種是把表示式放在圓括號之中,提升運算的優先順序;另一種是跟在函式的後面,作用是呼叫函式。
函式放在圓括號中,會返回函式本身。圓括號緊跟在函式的後面,表示呼叫函式。
圓括號之中,只能放置表示式
3. "左結合"(`left-to-right associativity`)運算子會先從左向右運算
"右結合"(`right-to-left associativity`)運算子會先從右向左運算
js中賦值運算子(`=`)、三元條件運算子(`?:`)、指數運算子(`**`)是"右結合"的
# 語法
## 資料型別的轉換
1. JavaScript 是一種動態型別語言,變數的型別無法在編譯階段確定,必須在執行時才能知道。而同時js的變數型別又可以隨意改變,因此又屬於弱型別語言
2. JS中的運算子對資料型別有要求。因此常常發生型別自動轉換
3. 強制型別轉換主要指使用`Number()`、`String()`和`Boolean()`手動將任意型別的值,分別轉換成數字、字串或者布林值。
4. `Number()`轉換為數值。比parseInt函式嚴格
- 轉換原始型別的值
```js
// 數值:轉換後還是原來的值
Number(324) // 324
// 字串:如果可以被解析為數值,則轉換為相應的數值
Number('324') // 324
// 字串:如果不可以被解析為數值,返回 NaN
Number('324abc') // NaN
// 空字串轉為0
Number('') // 0
// 布林值:true 轉成 1,false 轉成 0
Number(true) // 1
Number(false) // 0
// undefined:轉成 NaN
Number(undefined) // NaN
// null:轉成0
Number(null) // 0
// 忽略前後空格
Number('\t\v\r12.34\n') // 12.34
```
- 轉換物件時,規則如下:
第一步,呼叫物件自身的`valueOf`方法。如果返回原始型別的值,則直接對該值使用Number函式,不再進行後續步驟。
第二步,如果valueOf方法返回的還是物件,則改為呼叫物件自身的toString方法。如果toString方法返回原始型別的值,則對該值使用Number函式,不再進行後續步驟。
第三步,如果toString方法返回的是物件,就報錯。
自定義`valueOf`或`toString`
```js
Number({
valueOf: function () {
return 2;
}
})
// 2
Number({
toString: function () {
return 3;
}
})
// 3
Number({
valueOf: function () {
return 2;
},
toString: function () {
return 3;
}
})
// 2
```
5. `String()`轉換字串的規則如下:
- 原始型別的值
數值:相應的字串。
字串:原來的值。
布林值:true-"true",false-"false"。
undefined:"undefined"。
null:"null"。
- 物件
`String`引數如果是物件,返回一個型別字串;如果是陣列,返回該陣列的字串形式。
```js
String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"
```
轉換規則如下:
第一步,先呼叫物件自身的toString方法。如果返回原始型別的值,則對該值使用String函式,不再進行以下步驟。
第二步,如果toString方法返回的是物件,再呼叫原物件的valueOf方法。如果valueOf方法返回原始型別的值,則對該值使用String函式,不再進行以下步驟。
第三步,如果valueOf方法返回的是物件,就報錯。
```js
String({
toString: function () {
return 3;
}
})
// "3"
String({
valueOf: function () {
return 2;
}
})
// "[object Object]"
String({
valueOf: function () {
return 2;
},
toString: function () {
return 3;
}
})
// "3"
```
6. `Boolean()`轉換為布林值,規則簡單,除了下面6個值結果為false,其餘全部為true
`undefined`、`null`、`0`(包含`-0`和`+0`)、`NaN`、`''`(空字串)和`false`
*所有物件(包括空物件)的轉換結果都是`true`,包括`false`對應的布林物件`new Boolean(false)`也是true*
7. js中資料型別自動轉換髮生的情況:一、不同型別的資料相互運算時會自動轉換。二、對非布林值型別的資料求布林值時。三、對非數值型別的值使用一元運算子(即`+`和`-`)。轉換時的規則是:**預期什麼型別的值,就呼叫該型別的轉換函式**。如果該位置既可以是字串,又可以是數值,則預設轉為數值
8. JavaScript在預期為布林值的地方(比如if語句的條件部分),會將非布林值的引數自動轉換為布林值。系統內部會自動呼叫`Boolean`函式。
如下兩個方法將一個表示式轉為布林值
```js
// 寫法一
expression ? true : false
// 寫法二
!! expression
```
9. 除了加法運算子(`+`)有可能把運運算元轉為字串,其他運算子都會把運運算元自動轉成數值。
`null`數值`0`,`undefined`數值`NaN`
## 錯誤處理機制
1. JavaScript原生提供`Error`建構函式,所有丟擲的錯誤都是這個建構函式的例項。當發生錯誤時,js引擎丟擲`Error`例項物件以後,整個程式就中斷在發生錯誤的地方,不再往下執行。
```js
var err = new Error('出錯了');
err.message // "出錯了"
```
2. `Error`例項的屬性:
- `message`:錯誤提示資訊
- `name`:錯誤名稱(非標準屬性)
- `stack`:錯誤的堆疊(非標準屬性)
```js
function throwit() {
throw new Error('');
}
function catchit() {
try {
throwit();
} catch(e) {
console.log(e.stack); // print stack trace
}
}
catchit()
// Error
// at throwit (:2:9)
// at catchit (:7:5)
// at :1:1
```
3. `Error`例項是最一般的錯誤型別,js還提供`Error`的6個派生物件
- `SyntaxError`物件:解析程式碼時發生的語法錯誤
- `ReferenceError`物件:引用一個不存在的變數時發生的錯誤。
- `RangeError`物件:一個值超出有效範圍時發生的錯誤。
- `TypeError`物件:變數或引數不是預期型別時發生的錯誤。
- `URIError`物件:`URI`相關函式的引數不正確時丟擲的錯誤。主要`encodeURI()`、`decodeURI()`、`encodeURIComponent()`、`decodeURIComponent()`、`escape()`和`unescape()`。
- `EvalError`物件:已不再使用
4. 自定義錯誤
```js
function UserError(message) {
this.message = message || '預設資訊';
this.name = 'UserError';
}
UserError.prototype = new Error();
UserError.prototype.constructor = UserError;
```
5. `throw`語句:手動中斷程式執行,丟擲一個錯誤。
```js
if (true) {
throw new Error('x 必須為正數');
}
// Uncaught Error: x 必須為正數
// at :2:9
```
*`throw`可以丟擲任何型別的值*
6. `try...catch`結構用於對錯誤進行處理,選擇是否往下執行。`catch`程式碼塊捕獲錯誤後,程式不會中斷。
```js
try {
throw new Error('出錯了!');
} catch (e) {
console.log(e.name + ": " + e.message);
console.log(e.stack);
}
// Error: 出錯了!
// at :3:9
// ...
```
`catch`程式碼塊中加入判斷語句,捕獲不同型別的錯誤
```js
try {
foo.bar();
} catch (e) {
if (e instanceof SyntaxError) {
console.log(e.name + ": " + e.message);
} else if (e instanceof RangeError) {
console.log(e.name + ": " + e.message);
}
// ...
}
```
7. `try...catch...finally`結構中的`finally`程式碼塊,不管是否出現錯誤,都會在最後執行。
```js
function cleansUp() {
try {
throw new Error('出錯了……');
console.log('此行不會執行');
} finally {
console.log('完成清理工作');
}
}
```
`finally`程式碼塊前面即使有`return`返回語句,依舊會執行完再返回。
```js
function idle(x) {
try {
console.log(x);
return 'result';
} finally {
console.log('FINALLY');
}
}
idle('hello')
// hello
// FINALLY
```
如下說明:`return`語句的執行在`finally`程式碼之前,只是等到`finally`執行完最終才返回
```js
var count = 0;
function countUp() {
try {
return count;
} finally {
count++;
}
}
countUp()
// 0
count
// 1
```
`finally`程式碼塊的典型場景
```js
openFile();
try {
writeFile(Data);
} catch(e) {
handleError(e);
} finally {
closeFile();
}
```
## 程式設計風格
1. "程式設計風格"(`programming style`)指的是編寫程式碼的樣式規則。
*你選擇的,不是你喜歡的風格,而是一種能夠清晰表達你的意圖的風格*
2. 程式設計風格主要考慮的幾點:縮排(`indent`)、區塊(`block`)、圓括號(`parentheses`)、行尾的分號、變數宣告、嚴格相等、語句的合併書寫等
3. 使用`{}`程式碼塊時,js中要使用左大括號`{`緊挨著語句在同一行中,不要換行寫。這是因為**JavaScript會自動新增句末的分號,**從而產生一些難以察覺的錯誤。
```js
block {
// ...
}
```
如下`return`語句其實會變成兩句,從而導致出問題
```js
return
{
key: value
};
// 相當於
return;
{
key: value
};
// 正確寫法
return {
key : value
};
```
4. 行尾的分號:分號表示一條語句的結束。js允許省略。
有三種情況,語法規定不需要在結尾新增分號。如果新增,js引擎將分號解釋為空語句
- 1) `for` 和 `while` 迴圈
```js
for ( ; ; ) {
} // 沒有分號
while (true) {
} // 沒有分號
```
但是`do...while`要有分號
- 2) 分支語句:`if`,`switch`,`try`
```js
if (true) {
} // 沒有分號
switch () {
} // 沒有分號
try {
} catch {
} // 沒有分號
```
- 函式的宣告語句
```js
function f() {
} // 沒有分號
```
函式表示式仍要使用分號
```js
var f = function f() {
};
```
除了這三種情況,所有語句都應該使用分號。
在沒有分號時JavaScript會自動新增,這種語法特性叫"分號的自動新增"(`Automatic Semicolon Insertion`,簡稱`ASI`)
但是,**如果下一行的開始可以與本行的結尾連在一起解釋,JavaScript就不會自動新增分號。**
而是否自動新增分號無法預測,很有可能導致額外的錯誤。
*一行的起首"自增"(++)或"自減"(--),則前面會自動新增分號*
> 不應該省略結尾的分號,還有一個原因。有些JavaScript程式碼壓縮器(`uglifier`)不會自動新增分號,因此遇到沒有分號的結尾,就會讓程式碼保持原狀,而不是壓縮成一行,使得壓縮無法得到最優的結果。
>
> 另外,不寫結尾的分號,可能會導致指令碼合併出錯。所以,有的程式碼庫在第一行語句開始前,會加上一個分號。可以避免與其他指令碼合併時,前面的指令碼最後一行語句沒有分號,導致執行出錯的問題。
> ```js
> ;var a = 1;
> // ...
> ```
5. 避免全域性變數的使用,如果必須使用,考慮大寫字母表示
6. 變數宣告,由於存在變數提升,許多語句會導致產生全域性變數(比如`for`迴圈中)。
所有函式都應該在使用之前定義。函式內部的變數宣告,都應該放在函式的頭部。
7. 建議只使用嚴格相等運算子(`===`)
8. `switch...case`結構可以用物件結構代替
*`switch...case`結構類似於`goto`語句,容易造成程式流程的混亂,使得程式碼結構混亂不堪,不符合面向物件程式設計的原則。*
## console物件和控制檯
1. `console`物件是JavaScript的原生物件,可以輸出各種資訊到控制檯
2. `console`的常見用途:除錯程式,顯示網頁程式碼執行時的錯誤資訊;提供了一個命令列介面,用來與網頁程式碼互動。
3. 開發者工具的幾個面板。
- `Elements`:檢視網頁的 HTML 原始碼和 CSS 程式碼。
- `Resources`:檢視網頁載入的各種資原始檔(比如程式碼檔案、字型檔案 CSS 檔案等),以及在硬碟上建立的各種內容(比如本地快取、Cookie、Local Storage等)。
- `Network`:檢視網頁的 HTTP 通訊情況。
- `Sources`:檢視網頁載入的指令碼原始碼,可進行斷點debug。
- `Timeline`:檢視各種網頁行為隨時間變化的情況。
- `Performance`:檢視網頁的效能情況,比如 CPU 和記憶體消耗。
- `Console`:即控制檯,用來執行js命令,和頁面中js程式碼console方法的輸出。
4. [console 物件的靜態方法](https://wangdoc.com/javascript/features/console.html#console-%E5%AF%B9%E8%B1%A1%E7%9A%84%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95)
- `console.log()`,`console.info()`,`console.debug()`
- `console.warn(),console.error()`
- `console.table()`
- `console.count()`
5. `debugger`語句主要用於除錯,作用是設定斷點。
# 標準庫
下面基本都是js原生物件的介紹,裡面許多屬性和方法僅瞭解一下即可,有需要時再查詢使用
## Object物件
1. JavaScript原生提供`Object`物件
2. JavaScript的所有其他物件都繼承自`Object`物件,都是`Object`的例項。
3. `Object`物件的原生方法分成兩類:`Object`本身的方法("靜態方法")與`Object`的例項方法。
- `Object`物件本身的方法:直接定義在`Object`物件上的方法
- `Object`的例項方法:定義在`Object`原型物件`Object.prototype`上的方法。它可以被`Object`例項直接使用。
```js
// 本身的方法
Object.selfPrint = function (o) { console.log(o) };
// 例項方法
Object.prototype.print = function () {
console.log(this);
};
var obj = new Object();
obj.print() // Object
```
4. `Object`本身是一個函式,可以當作工具方法使用,將任意值轉為物件。保證某個值一定是物件。
5. `Object`方法無引數或為`undefined`、`null`,返回一個空物件
```js
var obj = Object();
// 等同於
var obj = Object(undefined);
var obj = Object(null);
obj instanceof Object // true
```
引數是原始型別,將原始型別的值轉換為對應的包裝物件的例項
引數是一個物件,則返回該物件(不進行轉換)
```js
var arr = [];
var obj = Object(arr); // 返回原陣列
obj === arr // true
var value = {};
var obj = Object(value) // 返回原物件
obj === value // true
var fn = function () {};
var obj = Object(fn); // 返回原函式
obj === fn // true
```
- 判斷變數是否為物件
```js
function isObject(value) {
return value === Object(value);
}
isObject([]) // true
isObject(true) // false
```
6. `instanceof`運算子驗證**一個物件是否為指定的建構函式的例項**
7. `Object`建構函式用來生成新物件
```js
var obj = new Object();
// 等價於
var obj = {};
```
8. `Object`建構函式與工具方法類似。如果引數是一個物件,則直接返回該物件;如果是一個原始型別的值,則返回該值對應的包裝物件
9. Object 的靜態方法
- `Object.keys()`,`Object.getOwnPropertyNames()`遍歷物件的屬性。兩者都返回物件自身的(而不是繼承的)所有屬性名組成的陣列。`Object.keys`方法只返回可列舉的屬性;`Object.getOwnPropertyNames`還返回不可列舉的屬性名。
通常使用`Object.keys`遍歷物件屬性
計算物件屬性的個數
```js
var obj = {
p1: 123,
p2: 456
};
Object.keys(obj).length // 2
Object.getOwnPropertyNames(obj).length // 2
```
10. `Object`例項物件的方法:
- `Object.prototype.valueOf()`:返回當前物件對應的值,預設情況下返回物件本身。
- `Object.prototype.toString()`:返回當前物件對應的字串形式,預設返回型別字串。
- `Object.prototype.toLocaleString()`:返回當前物件對應的本地字串形式。
- `Object.prototype.hasOwnProperty()`:判斷某個屬性是否為當前物件自身的屬性,還是繼承自原型物件的屬性。
- `Object.prototype.isPrototypeOf()`:判斷當前物件是否為另一個物件的原型。
- `Object.prototype.propertyIsEnumerable()`:判斷某個屬性是否可列舉。
11. 陣列、字串、函式、Date物件都自定義了`toString`方法,覆蓋了`Object.prototype.toString`方法。
```js
[1, 2, 3].toString() // "1,2,3"
'123'.toString() // "123"
(function () {
return 123;
}).toString()
// "function () {
// return 123;
// }"
(new Date()).toString()
// "Fri Jul 31 2020 21:24:16 GMT+0800 (中國標準時間)"
```
12. **判斷資料型別**
關於如何正確的判斷資料型別,由於`typeof`僅能準確返回數值、字串、布林值、undefined的型別,其他返回object。所以無法藉助它準確判斷型別;而`instanceof`對於繼承的物件,除了判斷當前物件例項時返回`true`,判斷繼承的上級物件例項時也會返回`true`,並且只能判斷是否是某個物件的例項,無法判斷基本型別。
因此最準確的辦法是利用 **`Object.prototype.toString`方法返回物件的型別字串** 這一特點,判斷一個值的型別
如下,空物件的`toString`方法,返回字串`object Object`,第二個`Object`表示當前值的建構函式。
```js
var obj = {};
obj.toString() // "[object Object]"
```
> ```js
> Object.prototype.toString.call(value) // 對value這個值呼叫Object.prototype.toString方法
> ```
`Object.prototype.toString`可以確認一個值是什麼型別。如下,實現比`typeof`運算子更準確的型別判斷函式
```js
var type = function (o){
var s = Object.prototype.toString.call(o);
return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};
type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"
```
實現判斷某種型別的方法:
```js
var type = function (o){
var s = Object.prototype.toString.call(o);
return s.match(/\[object (.*?)\]/)[1].toLowerCase();
};
['Null',
'Undefined',
'Object',
'Array',
'String',
'Number',
'Boolean',
'Function',
'RegExp'
].forEach(function (t) {
type['is' + t] = function (o) {
return type(o) === t.toLowerCase();
};
});
type.isObject({}) // true
type.isNumber(NaN) // true
type.isRegExp(/abc/) // true
```
13. `toLocaleString()`用來實現自定義的本地字串。如`Array.prototype.toLocaleString()`、
`Number.prototype.toLocaleString()`、`Date.prototype.toLocaleString()`等物件自定義這個方法
## 屬性描述物件
1. JS提供了叫做"屬性描述物件"(`attributes object`)的內部資料結構,用來描述物件的屬性,控制它的行為,比如該屬性是否可寫、可遍歷等。
2. 每個屬性都有自己對應的屬性描述物件,儲存該屬性的一些元資訊。
3. 如下為屬性描述物件的例子:
```js
{
value: 123, // 屬性的屬性值 預設undefined
writable: false, // 屬性值(value)是否可改變(可寫) 預設true
enumerable: true, // 屬性是否可遍歷,預設true
configurable: false,// 屬性的可配置性,預設true 控制屬性描述物件的可寫性
get: undefined, // get該屬性的取值函式(getter),預設undefined
set: undefined // set該屬性的存值函式(setter),預設undefined。
}
```
> 定義了取值函式`get`(或存值函式`set`),就不能將`writable`屬性設為true,或者同時定義value屬性,否則會報錯。
4. `Object.getOwnPropertyDescriptor()`獲取屬性描述物件
```js
var obj = { p: 'a' };
Object.getOwnPropertyDescriptor(obj, 'p')
// Object { value: "a",
// writable: true,
// enumerable: true,
// configurable: true
// }
```
5. `Object.defineProperty()`通過屬性描述物件,定義或修改屬性,並返回修改後的物件。
```js
Object.defineProperty(object, propertyName, attributesObject)
```
引數:
- object:屬性所在的物件
- propertyName:字串,屬性名
- attributesObject:屬性描述物件
`Object.defineProperties()`可以一次定義多個屬性
6. `JSON.stringify`方法會排除`enumerable`為false的屬性,有時可以利用這一點。如果物件的 `JSON`格式輸出要排除某些屬性,可以把這些屬性的`enumerable`設為false。
7. 存取器(`accessor`,set-setter,get-getter)是另外定義屬性的方式,定義存取器後,將會執行對應的函式。
除了`defineProperty`方法中通過屬性描述物件定義存取器,還提供如下的寫法(且這種寫法`configurable`和`enumerable`都為true,是可遍歷的屬性。更常用)
```js
var obj = {
get p() {
return 'getter';
},
set p(value) {
console.log('setter: ' + value);
}
};
```
存取器常用於:屬性的值依賴物件內部資料的場合。
```js
var obj ={
$n : 5,
get next() { return this.$n++ },
set next(n) {
if (n >= this.$n) this.$n = n;
else throw new Error('新的值必須大於等於當前值');
}
};
obj.next // 5
obj.next = 10;
obj.next // 10
obj.next = 5;
// Uncaught Error: 新的值必須大於當前值
```
8. 物件的拷貝:
由於物件是引用型別,資料存放在堆中,棧中值存放物件的地址。預設值型別的賦值是複製給另一個變數;但引用型別的賦值是直接將引用地址複製給另一個變數,賦值引用就是常說的淺拷貝(淺拷貝的物件共用一個記憶體地址)。而深拷貝指的是將引用型別的資料也完全複製一份給新的變數。
物件深拷貝的基本原理就是:通過遍歷物件的屬性,然後將屬性和遞迴至不是物件的屬性值重新賦值為另一個物件,如果屬性值是物件,則遞迴執行當前函式。
- **方法一**。 如下,缺點不能深拷貝`function`,物件存取器屬性拷貝出來的是一個值
```js
var DeepCopy = function dc(obj) {
if (obj===null) {
return obj;
}
else if (typeof obj === 'object') {
if (obj instanceof Array) {
var newArr = [], i, len = obj.length;
for (i = 0; i < len; i++) {
newArr[i] = dc(obj[i]);
}
return newArr;
} else {
var newObj = {};
for (var name in obj) {
newObj[name] = dc(obj[name]);
}
return newObj;
}
}
// 'number' 'string' 'boolean' undefined null
return obj;
}
var objFunction=function(){
//
}
var obj0={
p1:1,
get p2(){
return this.p1;
},
p3:objFunction
}
var obj1=DeepCopy(obj0);
// {p1: 1, p2: 1, p3: ƒ}
// p1: 1
// p2: 1
// p3: ƒ ()
obj1.p3===obj0.p3 // true
```
- **方法二**。使用`defineProperty`設定屬性描述器,完成拷貝屬性,可**實現拷貝物件存取器屬性**。但是此時複製的存取器屬性函式屬於淺拷貝
```js
var DeepCopy = function dc(obj) {
if (obj===null) {
return obj;
}
else if(typeof obj === 'object'){
if (obj instanceof Array) {
var newArr = [], i, len = obj.length;
for (i = 0; i < len; i++) {
newArr[i] = dc(obj[i]);
}
return newArr;
} else {
var newObj = {};
for (var name in obj) {
if (obj.hasOwnProperty(name)) {
Object.defineProperty(
newObj,
name,
Object.getOwnPropertyDescriptor(obj, name)
);
}
}
return newObj;
}
}
return obj;
}
```
- **方法三**。如下,利用`new Function`建構函式實現函式function的深拷貝。這也是處理js深拷貝最全的方法了,
```js
var DeepCopy = function dc(obj) {
if (obj===null) {
return obj;
}
else if (typeof obj === 'object') {
if (obj instanceof Array) {
var newArr = [], i, len = obj.length;
for (i = 0; i < len; i++) {
newArr[i] = dc(obj[i]);
}
return newArr;
} else {
var newObj = {};
for (var name in obj) {
if (obj.hasOwnProperty(name)) {
//newObj[name] = dc(obj[name]);
if(typeof obj[name] === 'function'){
newObj[name] = dc(obj[name]);
}
else{
Object.defineProperty(
newObj,
name,
Object.getOwnPropertyDescriptor(obj, name)
);
}
}
}
return newObj;
}
}
else if(typeof obj === 'function'){
// var funStr="var f="+obj.toString()+";"
// return new Function(funStr+"return f;");
return new Function("return "+obj+";");
}
return obj;
}
obj1=DeepCopy(obj0);
// {p1: 1, p3: ƒ}p1: 1p2: (...)p3: ƒ anonymous( )get p2: ƒ p2()__proto__: Object
obj1.p3===obj0.p3 // false
obj1.p2===obj0.p2 // true
```
- **方法四**。對於存取器屬性函式的深拷貝,可以通過`getOwnPropertyDescriptor`獲取的屬性描述器物件,判斷其get和set屬性,完成其函式的深拷貝
- **方法五**。還有一個簡便的方法,使用`JSON.stringfy()`或`JSON.parse()`序列化為json字串然後解析為js物件,實現一個物件的深拷貝。但是它存在一個致命的問題,就是自定義的函式無法拷貝(`JSON.stringfy()`方法無法將函式值轉為json字串。json無法表示函式型別)
```js
var objFunction=function(){
//
}
var obj0={
p1:1,
get p2(){
return this.p1;
},
p3:objFunction,
p4:{
p5:5
}
}
var newObj = JSON.parse(JSON.stringify(obj0));
newObj
// {p1: 1, p2: 1, p4: {…}}
// p1: 1
// p2: 1
// p4:
// p5: 5
```
*以上物件拷貝的都是可遍歷屬性,且可能改變不可寫的屬性為可寫。最最重要的是,新物件和舊物件的原型物件`obj.prototype`各自獨立*
ES6中實現物件複製的方式:比如`Object.assign`(淺拷貝)、展開操作符`…`(淺拷貝)
另:Array的`slice`和`concat`等方法不改變原陣列,但是返回的也是淺拷貝了的新陣列
另:`$.extend`方法的第一個引數給bool值表示是否深拷貝:`jQuery.extend( [deep ], target, object1 [, objectN ] )`
9. 控制物件狀態
- `Object.preventExtensions`方法:使一個物件無法再新增新的屬性
- `Object.isExtensible`方法檢查一個物件是否使用了`Object.preventExtensions`方法。檢查是否可以為一個物件新增屬性。
- `Object.seal`方法使得一個物件既無法新增新屬性,也無法刪除舊屬性。`Object.isSealed()`
- `Object.freeze`方法使一個物件變成常量。無法新增新屬性、無法刪除舊屬性、也無法改變屬性的值。`Object.isFrozen()`
上面三個方法鎖定物件的可寫性有一個漏洞:可以通過改變原型物件,來為物件增加屬性。解決方案是原型也凍結住。另外一個侷限是,如果屬性值是物件,這些方法只能凍結屬性指向的物件,而不能凍結物件本身的內容。
## Array 物件
1. `Array`是JavaScript的原生物件,也是一個建構函式,用來生成新陣列。
```js
var arr = new Array(2); // 等同於 var arr = Array(2);
arr.length // 2
arr // [ empty x 2 ]
```
2. `Array()`建構函式有很大的缺陷,不同的引數生成的結果會不一樣。因此建議使用陣列字面量的方式
```js
// 不建議的方式
var arr = new Array(1, 2);
// 推薦
var arr = [1, 2];
```
3. `Array.isArray()`靜態方法,判斷是否是陣列
```js
var arr = [1, 2, 3];
typeof arr // "object"
Array.isArray(arr) // true
```
4. 陣列物件的例項方法:
- `valueOf()`返回陣列本身
- `toString()`返回陣列的字串形式
- `push()`在陣列的末端新增一個或多個元素,返回新增後的陣列長度——(在陣列末尾壓入元素)。該方法改變原陣列。
- `pop()`刪除陣列的最後一個元素,並返回該元素——(彈出最後一個元素)。該方法改變原陣列。
`push`和`pop`結合使用,可構成"後進先出"的棧結構(`stack`)。
```js
var arr = [];
arr.push(1, 2);
arr.push(3);
arr.pop();
arr // [1, 2]
```
- `shift()`刪除陣列的第一個元素,並返回該元素——(彈出第一個元素)。該方法改變原陣列。
`shift()`方法可以遍歷並清空一個數組。
```js
var list = [1, 2, 3, 4];
while (list.length) {
console.log(list.shift());
}
list // []
```
`push()`和`shift()`結合使用,就構成了"先進先出"的佇列結構(`queue`)。
- `unshift()`在陣列的第一個位置新增元素,並返回新增後的陣列長度——(陣列頭部壓入一個元素)。該方法會改變原陣列。
```js
var arr = [ 'c', 'd' ];
arr.unshift('a', 'b') // 4
arr // [ 'a', 'b', 'c', 'd' ]
```
- `join()`以指定引數作為分隔符,將所有陣列成員連線為一個字串返回。預設用逗號分隔。
```js
var a = [1, 2, 3, 4,undefined, null];
a.join(' ') // '1 2 3 4 '
a.join(' | ') // "1 | 2 | 3 | 4 | | "
a.join() // "1,2,3,4,,"
```
*undefined或null或空位被轉為空字串*
*通過`call`方法,join也可以用於字串或類似陣列的物件*
- `concat()`用於多個數組的合併。將新陣列的成員,新增到原陣列成員的後部,並返回一個新陣列,原陣列不變。
```js
['hello'].concat(['world'])
// ["hello", "world"]
['hello'].concat(['world'], ['!'])
// ["hello", "world", "!"]
[].concat({a: 1}, {b: 2})
// [{ a: 1 }, { b: 2 }]
```
*concat連線的陣列中有物件時,返回的淺拷貝*
- `reverse()`翻轉陣列,用於顛倒排列陣列元素,返回改變後的陣列。該方法將改變原陣列。
- `slice()`用於提取陣列的一部分,返回一個新陣列。原陣列不變。
左閉右開,返回結果不包含end位置的元素。
```js
arr.slice(start, end);
```
省略第二個引數,會一直返回陣列最後的成員;或省略全部引數,返回元素組;第一個引數大於等於陣列長度,或者第二個引數小於第一個引數,則返回空陣列。
**`slice()`一個重要應用,是將類似陣列的物件轉為真正的陣列**。
- `splice()`用於刪除原陣列的一部分成員,並可以在刪除的位置新增新的陣列成員,返回值是被刪除的元素。該方法會改變原陣列。
引數為起始位置、刪除的元素個數,新增到刪除位置的新元素
```js
arr.splice(start, count, addElement1, addElement2, ...);
```
第二個引數設為0,可實現插入元素
```js
var a = [1, 1, 1];
a.splice(1, 0, 2) // []
a // [1, 2, 1, 1]
```
只提供第一個引數,將"剪下"到陣列末尾
- `sort()`對陣列成員進行排序,預設按照字典順序排序。原陣列將被改變。
```js
['d', 'c', 'b', 'a'].sort()
// ['a', 'b', 'c', 'd']
[4, 3, 2, 1].sort()
// [1, 2, 3, 4]
[11, 101].sort()
// [101, 11]
[10111, 1101, 111].sort()
// [10111, 1101, 111]
```
通過傳入一個函式,可以讓sort方法按照自定義方式排序
```js
[10111, 1101, 111].sort(function (a, b) {
return a - b;
})
// [111, 1101, 10111]
[
{ name: "張三", age: 30 },
{ name: "李四", age: 24 },
{ name: "王五", age: 28 }
].sort(function (o1, o2) {
return o1.age - o2.age;
})
// [
// { name: "李四", age: 24 },
// { name: "王五", age: 28 },
// { name: "張三", age: 30 }
// ]
```
**`sort`引數函式接受兩個引數,表示進行比較的兩個陣列成員。如果函式的返回值大於0,表示第一個成員排在第二個成員後面;如果函式的返回值小於等於0,則第一個元素排在第二個元素前面。**
*自定義的排序函式應該返回數值*
- `map()`將陣列的所有成員依次傳入引數函式,然後把每一次的執行結果組成一個新陣列返回。元素組不變
```js
var numbers = [1, 2, 3];
numbers.map(function (n) {
return n + 1;
});
// [2, 3, 4]
numbers // [1, 2, 3]
```
`map`引數函式的三個引數:當前成員、當前位置和陣列本身。
```js
[1, 2, 3].map(function(elem, index, arr) {
return elem * index;
});
// [0, 2, 6]
```
`map`的第二個引數,用來繫結回撥函式內部的`this`變數
- `forEach`與`map`相似,對陣列的所有成員依次執行引數函式,但不返回值。
*如果陣列遍歷是為了得到返回值,可以使用map方法,否則使用forEach方法。*
**forEach方法無法中斷執行**。如果想要中斷,可使用for迴圈、或`some`、`every`方法。
- `some()`,`every()`方法類似"斷言"(`assert`),返回布林值,表示陣列成員是否符合某種條件
some方法是隻要一個成員的返回值是true,則整個some方法的返回值就是true,否則返回false。
every方法是所有成員的返回值都是true,整個every方法才返回true,否則返回false。
藉助這一點,可以迴圈執行陣列每個元素時,some方法的引數函式中判斷某個條件然後返回true,every方法的引數函式中判斷某個條件然後返回false,即可起到類似for迴圈中break中斷的作用;
```js
var arr = [1, 2, 3, 4, 5];
arr.some(function (elem, index, arr) {
console.log(elem); //執行操作
return elem > = 3;
});
// 1
// 2
// 3
// true
arr.every(function (elem, index, arr) {
if(elem<=3){
console.log(elem);
return true;
}
else{
return false;
}
});
// 1
// 2
// 3
// false
```
*對於空陣列,some方法返回false,every方法返回true,回撥函式都不會執行*
- `filter()`過濾陣列成員,滿足條件的成員組成一個新陣列返回——即filter的引數函式返回true的成員保留下來組成新陣列。不會改變原陣列。
引數函式的三個引數:當前成員,當前位置和整個陣列。
```js
[1, 2, 3, 4, 5].filter(function (elem) {
return (elem > 3);
})
[1, 2, 3, 4, 5].filter(function (elem, index, arr) {
return index % 2 === 0;
});
```
- `reduce()`、`reduceRight()`依次處理陣列的每個成員,最終累計為一個值。處理的是上一次累計值和當前元素執行結果的累計值。區別是,`reduce`是從左到右處理(從第一個成員到最後一個成員),`reduceRight`則是從右到左(從最後一個成員到第一個成員)。
```js
[1, 2, 3, 4, 5].reduce(function (a, b) {
console.log("上一次的累計值:"+a, "當前值:"+b);
return a + b;
})
// 上一次的累計值:1 當前值:2
// 上一次的累計值:3 當前值:3
// 上一次的累計值:6 當前值:4
// 上一次的累計值:10 當前值:5
// 15
```
第一次執行時,累計值a是陣列的第一個元素,之後就是累計值和元素值
其引數函式可接受四個變數:累積變數,預設為陣列的第一個成員;當前變數,預設為陣列的第二個成員;當前位置(從0開始);原陣列。前兩個必須
`reduce`和`reduceRight`的第二個引數可指定執行時的初始值
```js
[1, 2, 3, 4, 5].reduce(function (a, b) {
console.log("上一次的累計值:"+a, "當前值:"+b);
return a + b;
},10)
// 上一次的累計值:10 當前值:1
// 上一次的累計值:11 當前值:2
// 上一次的累計值:13 當前值:3
// 上一次的累計值:16 當前值:4
// 上一次的累計值:20 當前值:5
// 25
```
*空陣列執行`reduce`或`reduceRight`時會報錯,可指定第二個引數初始值解決*
藉助`reduce`(或`reduceRight`)可以實現一些遍歷操作,比如找出字元長度最大的陣列元素
```js
function findLongest(entries) {
return entries.reduce(function (longest, entry) {
return entry.length > longest.length ? entry : longest;
}, '');
}
findLongest(['aaa', 'bb', 'c']) // "aaa"
```
- `indexOf()`返回給定元素在陣列中第一次出現的位置,沒有則返回`-1`。第二個引數表示搜尋開始的位置
- `lastIndexOf()`返回給定元素在陣列中最後一次出現的位置,沒有則返回`-1`。
5. 鏈式呼叫,如果陣列方法返回的還是陣列,就可以接著呼叫陣列方法,實現鏈式呼叫
```js
var users = [
{name: 'tom', email: '[email protected]'},
{name: 'peter', email: '[email protected]'}
];
users.map(function (user) {
return user.email;
})
.filter(function (email) {
return /^t/.test(email);
})
.forEach(function (email) {
console.log(email);
});
// "[email protected]"
```
## 包裝物件
1. js的三種原始型別的值——數值、字串、布林值——在一定條件下會自動轉為物件,這就是原始型別的"包裝物件"(`wrapper`)
2. "包裝物件"指的是與數值、字串、布林值分別相對應的`Number`、`String`、`Boolean`三個原生物件。這三個原生物件可以把原始型別的值變成(包裝成)物件。
```js
var v1 = new Number(123);
var v2 = new String('abc');
var v3 = new Boolean(true);
typeof v1 // "object"
typeof v2 // "object"
typeof v3 // "object"
v1 === 123 // false
v2 === 'abc' // false
v3 === true // false
```
3. 包裝物件的設計目的:首先,使得"物件"這種型別可以覆蓋JavaScript所有的值,整門語言有一個通用的資料模型。其次,使得**原始型別的值也有辦法呼叫自己的方法**。
4. `Number`、`String`和`Boolean`作為普通函式呼叫時用以型別轉換,將任意型別的值轉為數值、字串和布林值等原始型別的值;作為建構函式使用(帶有`new`)時,將原始型別的值轉為物件
5. 包裝物件繼承了`Object`物件的`valueOf()`——返回包裝物件例項對應的原始型別的值、`toString()`——返回對應的字串形式方法
6. 原始型別與例項物件的自動轉換:有時,原始型別的值會自動當作包裝物件呼叫,即呼叫包裝物件的屬性和方法。*JavaScript 引擎會自動將原始型別的值轉為包裝物件例項,並在使用後立刻銷燬例項*。
比如字串呼叫`length`屬性:
```js
'abc'.length // 3
```
`abc`是一個字串,本身不是物件,不能呼叫`length`屬性。JavaScript引擎自動將其轉為包裝物件,在這個物件上呼叫`length`屬性。呼叫結束後,這個臨時物件就會被銷燬。這就叫**原始型別與例項物件的自動轉換**。
**自動轉換生成的包裝物件是隻讀的,無法修改**。所以,字串無法新增新屬性。同時呼叫結束後,包裝例項會自動銷燬,所以每次呼叫其實都是一個新的包裝物件。
```js
var s = 'Hello World';
s.x = 123;
s.x // undefined
```
*如果要為字串新增屬性,只有在它的原型物件`String.prototype`上定義*
7. 可以在包裝物件的原型物件`prototype`上新增自定義方法或屬性
## Boolean物件
1. 通過`valueOf()`獲取包裝物件對應的原始型別值
```js
new Boolean(false).valueOf()
```
## Number物件
1. Number物件的靜態屬性:
- `Number.POSITIVE_INFINITY`:正的無限,指向`Infinity`。
- `Number.NEGATIVE_INFINITY`:負的無限,指向`-Infinity`。
- `Number.NaN`:表示非數值,指向`NaN`
- `Number.MIN_VALUE`:表示最小正數(即最接近0的正數,在64位浮點數體系中為5e-324),相應的,最接近0的負數為`-Number.MIN_VALUE`。
- `Number.MAX_VALUE`:表示最大正數
- `Number.MAX_SAFE_INTEGER`:表示能夠精確表示的最大整數,即`9007199254740991`。
- `Number.MIN_SAFE_INTEGER`:表示能夠精確表示的最小整數,即`-9007199254740991`。
2. 例項方法
- `Number.prototype.toString()`,用於將一個數值轉為字串形式。該方法可以接受一個引數,表示輸出的進位制
```js
(10).toString() // "10"
(10).toString(2) // "1010"
(10).toString(8) // "12"
(10).toString(16) // "a"
```
呼叫時,數值必須用括號括起來,否則js引擎會把`.`解讀為小數點,從而混淆。任何不至於誤讀的寫法都可以
```js
10.toString(2)
// SyntaxError: Unexpected token ILLEGAL
10.5.toString() // "10.5"
10.5.toString(2) // "1010.1"
10.5.toString(8) // "12.4"
10.5.toString(16) // "a.8"
```
可使用方括號呼叫
```js
10['toString'](2) // "1010"
```
如果想將其他進位制的數轉為十進位制,使用`parseInt`
- `Number.prototype.toFixed()`將一個數轉為指定位數的小數,然後返回個這小數對應的字串。
```js
(10).toFixed(2) // "10.00"
10.005.toFixed(2) // "10.01"
```
**由於浮點數的原因,js中小數5的四捨五入是不確定的,使用的時候必須小心。**
- `Number.prototype.toExponential()`將一個數轉為科學計數法形式
- `Number.prototype.toLocaleString()`接受地區碼作為引數,返回當前數字在該地區的當地書寫形式。
```js
(123).toLocaleString('zh-Hans-CN-u-nu-hanidec')
// "一二三"
```
`toLocaleString()`第二個引數是配置物件,可以定製返回的字串。比如style屬性指定輸出樣式,預設值`decimal`(十進位制形式),還可取值`percent`(百分比)、`currency`(貨幣格式)
```js
(123).toLocaleString('zh-Hans-CN', { style: 'percent' })
// "12,300%"
(123).toLocaleString('zh-Hans-CN', { style: 'currency', currency: 'CNY' })
// "¥123.00"
(123).toLocaleString('de-DE', { style: 'currency', currency: 'EUR' })
// "123,00 €"
(123).toLocaleString('en-US', { style: 'currency', currency: 'USD' })
// "$123.00"
```
- `Number.prototype`物件上可以自定義方法
```js
Number.prototype.add = function (x) {
return this + x;
};
Number.prototype.subtract = function (x) {
return this - x;
};
(8).add(2).subtract(4) // 6
```
## String物件
1. 靜態方法`String.fromCharCode()`返回Unicode碼點組成的字串
Unicode碼點不能大於`0xFFFF`,碼點大於`0xFFFF`的字元佔用四個位元組,而JavaScript預設支援的是兩個位元組的字元。比如`0x20BB7`需要拆成兩個字元來寫
```js
String.fromCharCode(0xD842, 0xDFB7) // "