1. 程式人生 > >《JavaScript語言入門教程》記錄整理:運算子、語法和標準庫

《JavaScript語言入門教程》記錄整理:運算子、語法和標準庫

[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) // "