// 数值:转换后仍是原来的值 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](); }