// 數值:轉換後仍是原來的值 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
(1)parseInt
和Number
函數都會自動過濾一個字符串前導和後綴的空格。
(2)Number
函數將字符串轉爲數值,要比parseInt
函數嚴格不少。基本上,只要有一個字符沒法轉成數值,整個字符串就會被轉爲NaN
。正則表達式
(2)簡單的規則是,Number
方法的參數是對象時,將返回NaN
,除非是包含單個數值的數組。編程
Number({a: 1}) // NaN Number([1, 2, 3]) // NaN Number([5]) // 5
(2)Number
背後的轉換規則比較複雜。數組
第一步,調用對象自身的valueOf
方法。若是返回原始類型的值,則直接對該值使用Number
函數,再也不進行後續步驟。ide
第二步,若是valueOf
方法返回的仍是對象,則改成調用對象自身的toString
方法。若是toString
方法返回原始類型的值,則對該值使用Number
函數,再也不進行後續步驟。函數
第三步,若是toString
方法返回的是對象,就報錯。this
var obj = {x: 1}; Number(obj) // NaN // 等同於 if (typeof obj.valueOf() === 'object') { Number(obj.toString()); } else { Number(obj.valueOf()); }
(3)默認狀況下,對象的valueOf
方法返回對象自己,因此通常老是會調用toString
方法,而toString
方法返回對象的類型字符串(好比[object Object]
)。因此,會有下面的結果。prototype
Number({}) // NaN
(4)valueOf
和toString
方法,都是能夠自定義的。code
Number({ valueOf: function () { return 2; } }) // 2 Number({ toString: function () { return 3; } }) // 3 Number({ valueOf: function () { return 2; }, toString: function () { return 3; } }) // 2
true
轉爲字符串"true"
,false
轉爲字符串"false"
。"undefined"
。"null"
。String(123) // "123" String('abc') // "abc" String(true) // "true" String(undefined) // "undefined" String(null) // "null"
(1)String
方法的參數若是是對象,返回一個類型字符串;若是是數組,返回該數組的字符串形式。orm
String({a: 1}) // "[object Object]" String([1, 2, 3]) // "1,2,3"
(2)轉換規則對象
toString
方法。若是返回原始類型的值,則對該值使用String
函數,再也不進行如下步驟。toString
方法返回的是對象,再調用原對象的valueOf
方法。若是valueOf
方法返回原始類型的值,則對該值使用String
函數,再也不進行如下步驟。valueOf
方法返回的是對象,就報錯。String({a: 1}) // "[object Object]" // 等同於 String({a: 1}.toString()) // "[object Object]"
var obj = { valueOf: function () { return {}; }, toString: function () { return {}; } }; String(obj) // TypeError: Cannot convert object to primitive value
String({ toString: function () { return 3; } }) // "3" String({ valueOf: function () { return 2; } }) // "[object Object]" String({ valueOf: function () { return 2; }, toString: function () { return 3; } }) // "3"
(1)它的轉換規則相對簡單:除了如下五個值的轉換結果爲false
,其餘的值所有爲true
。
undefined
null
0
(包含-0
和+0
)NaN
''
(空字符串)Boolean(undefined) // false Boolean(null) // false Boolean(0) // false Boolean(NaN) // false Boolean('') // false
Boolean(true) // true Boolean(false) // false
(2)全部對象(包括空對象)的轉換結果都是true
Boolean({}) // true Boolean([]) // true Boolean(new Boolean(false)) // true
(1)自動轉換的規則是這樣的:預期什麼類型的值,就調用該類型的轉換函數。
(2)因爲自動轉換具備不肯定性,並且不易除錯,建議在預期爲布爾值、數值、字符串的地方,所有使用Boolean()
、Number()
和String()
函數進行顯式轉換。
(3)加法運算符(+
)有可能把運算子轉爲字符串
null + 1 // 1 undefined + 1 // NaN
(4)一元運算符也會把運算子轉成數值。
+'abc' // NaN -'abc' // NaN +true // 1 -false // 0
(1)JavaScript 原生提供Error
構造函數,全部拋出的錯誤都是這個構造函數的實例。
var err = new Error('出錯了'); err.message // "出錯了"
(2)Error
的屬性
if (error.name) { console.log(error.name + ': ' + error.message); }
function throwit() { throw new Error(''); } function catchit() { try { throwit(); } catch(e) { console.log(e.stack); // print stack trace } } catchit() // Error // at throwit (~/examples/throwcatch.js:9:11) // at catchit (~/examples/throwcatch.js:3:9) // at repl:1:5
(1)SyntaxError`對象是解析代碼時發生的語法錯誤。
// 變量名錯誤 var 1a; // Uncaught SyntaxError: Invalid or unexpected token // 缺乏括號 console.log 'hello'); // Uncaught SyntaxError: Unexpected string
(2)ReferenceError
對象是引用一個不存在的變量時發生的錯誤。
// 使用一個不存在的變量 unknownVariable // Uncaught ReferenceError: unknownVariable is not defined
(3)將一個值分配給沒法分配的對象,好比對函數的運行結果賦值。
// 等號左側不是變量 console.log() = 1 // Uncaught ReferenceError: Invalid left-hand side in assignment
(4)RangeError
對象是一個值超出有效範圍時發生的錯誤。主要有幾種狀況,一是數組長度爲負數,二是Number
對象的方法參數超出範圍,以及函數堆棧超過最大值。
// 數組長度不得爲負數 new Array(-1) // Uncaught RangeError: Invalid array length
(5)TypeError
對象是變量或參數不是預期類型時發生的錯誤。好比,對字符串、布爾值、數值等原始類型的值使用new
命令,就會拋出這種錯誤,由於new
命令的參數應該是一個構造函數。
new 123 // Uncaught TypeError: number is not a func var obj = {}; obj.unknownMethod() // 調用對象不存在的方法 // Uncaught TypeError: obj.unknownMethod is not a function
(6)URIError
對象是 URI 相關函數的參數不正確時拋出的錯誤,主要涉及encodeURI()
、decodeURI()
、encodeURIComponent()
、decodeURIComponent()
、escape()
和unescape()
這六個函數。
decodeURI('%2') // URIError: URI malformed
(7)eval
函數沒有被正確執行時,會拋出EvalError
錯誤。該錯誤類型已經再也不使用了,只是爲了保證與之前代碼兼容,才繼續保留。
(8)以上這6種派生錯誤,連同原始的Error
對象,都是構造函數。開發者可使用它們,手動生成錯誤對象的實例。這些構造函數都接受一個參數,表明錯誤提示信息(message)。
var err1 = new Error('出錯了!'); var err2 = new RangeError('出錯了,變量超出有效範圍!'); var err3 = new TypeError('出錯了,變量類型無效!'); err1.message // "出錯了!" err2.message // "出錯了,變量超出有效範圍!" err3.message // "出錯了,變量類型無效!"
function UserError(message) { this.message = message || '默認信息'; this.name = 'UserError'; } UserError.prototype = new Error(); UserError.prototype.constructor = UserError;
new UserError('這是自定義的錯誤!');
(1)throw
語句的做用是手動中斷程序執行,拋出一個錯誤。
if (x <= 0) { throw new Error('x 必須爲正數'); } // Uncaught ReferenceError: x is not defined
(2)throw
也能夠拋出自定義錯誤。
function UserError(message) { this.message = message || '默認信息'; this.name = 'UserError'; } throw new UserError('出錯了!'); // Uncaught UserError {message: "出錯了!", name: "UserError"}
(3)throw
能夠拋出任何類型的值。也就是說,它的參數能夠是任何值。
// 拋出一個字符串 throw 'Error!'; // Uncaught Error! // 拋出一個數值 throw 42; // Uncaught 42 // 拋出一個布爾值 throw true; // Uncaught true // 拋出一個對象 throw { toString: function () { return 'Error!'; } }; // Uncaught {toString: ƒ}
(1)avaScript 提供了try...catch
結構,容許對錯誤進行處理,選擇是否往下執行。
try { throw new Error('出錯了!'); } catch (e) { console.log(e.name + ": " + e.message); console.log(e.stack); } // Error: 出錯了! // at <anonymous>:3:9 // ...
(2)catch
代碼塊捕獲錯誤以後,程序不會中斷,會按照正常流程繼續執行下去。
try { throw "出錯了"; } catch (e) { console.log(111); } console.log(222); // 111 // 222
(3)catch
代碼塊之中,還能夠再拋出錯誤,甚至使用嵌套的try...catch
結構。
var n = 100; try { throw n; } catch (e) { if (e <= 50) { // ... } else { throw e; } } // Uncaught 100
(4)爲了捕捉不一樣類型的錯誤,catch
代碼塊之中能夠加入判斷語句。
try { foo.bar(); } catch (e) { if (e instanceof EvalError) { console.log(e.name + ": " + e.message); } else if (e instanceof RangeError) { console.log(e.name + ": " + e.message); } // ... }
(1)表示不論是否出現錯誤,都必需在最後運行的語句。
function cleansUp() { try { throw new Error('出錯了……'); console.log('此行不會執行'); } finally { console.log('完成清理工做'); } } cleansUp() // 完成清理工做 // Uncaught Error: 出錯了…… // at cleansUp (<anonymous>:3:11) // at <anonymous>:10:1
(2)return
語句的執行是排在finally
代碼以前,只是等finally
代碼執行完畢後才返回。
var count = 0; function countUp() { try { return count; } finally { count++; } } countUp() // 0 count // 1
(3)catch
代碼塊結束執行以前,會先執行finally
代碼塊。
function f() { try { console.log(0); throw 'bug'; } catch(e) { console.log(1); return true; // 這句本來會延遲到 finally 代碼塊結束再執行 console.log(2); // 不會運行 } finally { console.log(3); return false; // 這句會覆蓋掉前面那句 return console.log(4); // 不會運行 } console.log(5); // 不會運行 } var result = f(); // 0 // 1 // 3 result // false
(4)進入catch
代碼塊以後,一遇到throw
語句,就會去執行finally
代碼塊,其中有return false
語句,所以就直接返回了,再也不會回去執行catch
代碼塊剩下的部分了。
function f() { try { throw '出錯了!'; } catch(e) { console.log('捕捉到內部錯誤'); throw e; // 這句本來會等到finally結束再執行 } finally { return false; // 直接返回 } } try { f(); } catch(e) { // 此處不會執行 console.log('caught outer "bogus"'); }
(5)try
代碼塊內部,還能夠再使用try
代碼塊。
try { try { consle.log('Hello world!'); // 報錯 } finally { console.log('Finally'); } console.log('Will I run?'); } catch(error) { console.error(error.message); } // Finally // consle is not defined
(1)縮進,可以使用空格,也可使用TAB
(2)老是使用大括號表示區塊。
(3)JavaScript 會自動添加句末的分號,致使一些難以察覺的錯誤。
return { key: value }; // 至關於 return; { key: value };
(4)圓括號
(5)行尾不使用分號的狀況
(6)do...while
循環是有分號的。
(7)函數表達式仍然要使用分號。
var f = function f() { };
(8)若是沒有使用分號,大多數狀況下,JavaScript 會自動添加。
var a = 1 // 等同於 var a = 1;
(9)若是下一行的開始能夠與本行的結尾連在一塊兒解釋,JavaScript 就不會自動添加分號。
// 等同於 var a = 3 var a = 3 // 等同於 'abc'.length 'abc' .length // 等同於 return a + b; return a + b; // 等同於 obj.foo(arg1, arg2); obj.foo(arg1, arg2); // 等同於 3 * 2 + 10 * (27 / 6) 3 * 2 + 10 * (27 / 6)
x = y (function () { // ... })(); // 等同於 x = y(function () {...})();
// 引擎解釋爲 c(d+e) var a = b + c (d+e).toString(); // 引擎解釋爲 a = b/hi/g.exec(c).map(d) // 正則表達式的斜槓,會看成除法運算符 a = b /hi/g.exec(c).map(d); // 解釋爲'b'['red', 'green'], // 即把字符串看成一個數組,按索引取值 var a = 'b' ['red', 'green'].forEach(function (c) { console.log(c); }) // 解釋爲 function (x) { return x }(a++) // 即調用匿名函數,結果f等於0 var a = 0; var f = function (x) { return x } (a++)
(10)只有下一行的開始與本行的結尾,沒法放在一塊兒解釋,JavaScript 引擎纔會自動添加分號。
if (a < 0) a = 0 console.log(a) // 等同於下面的代碼, // 由於 0console 沒有意義 if (a < 0) a = 0; console.log(a)
(11)若是一行的起首是「自增」(++
)或「自減」(--
)運算符,則它們的前面會自動添加分號。
a = b = c = 1 a ++ b -- c console.log(a, b, c) // 1 2 0
// 等同於 a = b = c = 1; a; ++b; --c;
(12)若是continue
、break
、return
和throw
這四個語句後面,直接跟換行符,則會自動添加分號。
(13)因爲解釋引擎自動添加分號的行爲難以預測,所以編寫代碼的時候不該該省略行尾的分號。
(14)有的代碼庫在第一行語句開始前,會加上一個分號。能夠避免與其餘腳本合併時,排在前面的腳本最後一行語句沒有分號,致使運行出錯的問題。
;var a = 1; // ...
(15)建議避免使用全局變量。若是不得不使用,能夠考慮用大寫字母表示變量名,這樣更容易看出這是全局變量,好比UPPER_CASE
。
(16)JavaScript 會自動將變量聲明「提高」(hoist)到代碼塊(block)的頭部。
if (!x) { var x = {}; } // 等同於 var x; if (!x) { x = {}; }
(17)全部函數都應該在使用以前定義。函數內部的變量聲明,都應該放在函數的頭部。
(18)with
能夠減小代碼的書寫,可是會形成混淆。
with (o) { foo = bar; }
上面的代碼,能夠有四種運行結果:
o.foo = bar; o.foo = o.bar; foo = bar; foo = o.bar;
這四種結果均可能發生,取決於不一樣的變量是否有定義。所以,不要使用with
語句。
(19)相等運算符會自動轉換變量類型,形成不少意想不到的狀況。
0 == ''// true 1 == true // true 2 == true // false 0 == '0' // true false == 'false' // false false == '0' // true ' trn ' == 0 // true
所以,建議不要使用相等運算符(==
),只使用嚴格相等運算符(===
)。
(20)建議不要將不一樣目的的語句,合併成一行。
(21)全部的++
運算符均可以用+= 1
代替。
++x // 等同於 x += 1;
(22)switch...case
不使用大括號,不利於代碼形式的統一。此外,這種結構相似於goto
語句,容易形成程序流程的混亂,使得代碼結構混亂不堪,不符合面向對象編程的原則。建議switch...case
結構能夠用對象結構代替。
function doAction(action) { switch (action) { case 'hack': return 'hack'; case 'slash': return 'slash'; case 'run': return 'run'; default: throw new Error('Invalid action.'); } }
上面的代碼建議改寫成對象結構。
function doAction(action) { var actions = { 'hack': function () { return 'hack'; }, 'slash': function () { return 'slash'; }, 'run': function () { return 'run'; } }; if (typeof actions[action] !== 'function') { throw new Error('Invalid action.'); } return actions[action](); }