共计 9010 个字符,预计需要花费 23 分钟才能阅读完成。
数据类型的转换
强制转换
Number()
原始类型
// 数值:转换后还是原来的值 | |
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
函数,不再进行后续步骤。
第二步,如果 valueOf
办法返回的还是对象,则改为调用对象本身的 toString
办法。如果 toString
办法返回原始类型的值,则对该值应用 Number
函数,不再进行后续步骤。
第三步,如果 toString
办法返回的是对象,就报错。
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]
)。所以,会有上面的后果。
Number({}) // NaN
(4)valueOf
和 toString
办法,都是能够自定义的。
Number({valueOf: function () {return 2;} | |
}) | |
// 2 | |
Number({toString: function () {return 3;} | |
}) | |
// 3 | |
Number({valueOf: function () {return 2;}, | |
toString: function () {return 3;} | |
}) | |
// 2 |
String()
原始类型值
- 数值:转为相应的字符串。
- 字符串:转换后还是原来的值。
- 布尔值 :
true
转为字符串"true"
,false
转为字符串"false"
。 - undefined:转为字符串
"undefined"
。 - null:转为字符串
"null"
。
String(123) // "123" | |
String('abc') // "abc" | |
String(true) // "true" | |
String(undefined) // "undefined" | |
String(null) // "null" |
对象
(1)String
办法的参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串模式。
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" |
Boolean()
(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 |
错误处理机制
Error 实例对象
(1)JavaScript 原生提供 Error
构造函数,所有抛出的谬误都是这个构造函数的实例。
var err = new Error('出错了'); | |
err.message // "出错了" |
(2)Error
的属性
- message:谬误提示信息
- name:谬误名称(非标准属性)
- stack:谬误的堆栈(非标准属性)
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('这是自定义的谬误!');
throw 语句
(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: ƒ} |
try…catch 构造
(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); | |
} | |
// ... | |
} |
finally 代码块
(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)行尾不应用分号的状况
- for 和 while 循环
- 分支语句:if,switch,try
- 函数的申明语句
(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]();} |