JavaScript 是一种动态类型语言,变量没有类型限制,可以随时赋予任意值。
数据类型的转换
强制转换
强制转换主要指使用Number()
、String()
和Boolean()
三个函数,手动将各种类型的值,分别转换成数字、字符串或者布尔值。
Number()
Number
函数,可以将任意类型的值转化成数值。
- 转换规则
//字符串
Number('324') // 324
Number('324abc') // NaN Number函数将字符串转换为数值,要比parseInt函数严格得多,只要遇到字符无法转成数值,整个字符串就会被转换为NaN
//布尔值
Number(true) // 1
Number(false) // 0
//空字符串转为0;null 转为0;undefined转为NaN
Number('') // 0
Number(null) // 0
Number(undefined) // NaN
//parseInt和Number函数都会自动过滤一个字符串前导和后缀的空格
- Number方法的参数是对象时,将返回NaN,除非是包含单个数值的数组。
Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5
Number背后的转换规则:
var obj = {x: 1};
Number(obj) // NaN
// 等同于
if (typeof obj.valueOf() === 'object') {
Number(obj.toString());
} else {
Number(obj.valueOf());
}
/*
* 第一步调用对象自身的valueOf方法,如果返回的是原始类型的值,则直接对该值使用Number函数,不再进行后续步骤
第二步,如果valueOf返回的还是对象,则改为调用对象自身的toString方法,如果toString方法返回原始类型的值,则对该值用Number函数,不再进行后续步骤
第三步 如果toString方法返回的是对象,就报错
*/
String()
String
函数可以将任意类型的值转化成字符串
- 转换规则
数值:转为相应的字符串
字符串:转换后还是原来的值
布尔值:true转为字符串"true",false转为字符串"false"
undefined:转为字符串"undefined"
null:转为字符串"null"
- String方法的参数如果是对象,返回一个类型字符串;如果是数组,返回该数组的字符串形式
String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"
/* 第一步,先调用自身的toString方法,如果返回原始类型的值,调用String函数。
* 第二步,如果toString方法返回的是对象,再调用对象的valueOf方法,如果valueOf方法返回原始类型的值,则对该值使用String函数,不再进行以下步骤
* 如果valueOf方法返回的是对象,就报错
Boolean()
Boolean()
函数可以将任意类型的值转为布尔值。
转换规则:除了以下五个值的转换结果为false
,其他的值全部为true
。
undefined
null
0
(包含-0
和+0
)NaN
''
(空字符串)
- 所有对象(包括空对象)的转换结果都是true,甚至连false对应的布尔对象new Boolean(false)也是true
Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true
自动转换
- 第一种自动转换的情况:不同类型的数据互相运算
123 + 'abc' // "123abc"
- 第二种: 对非布尔值类型的数据求布尔值
if ('xx') {
console.log('xxx')
} // "xxx"
- 第三种:对非数值类型的值使用一元运算符( + -)
+ {foo: 'bar'} // NaN
- [1, 2, 3] // NaN
// 由于自动转换具有不确定性,而且不易除错,建议在预期为布尔值、数值、字符串的地方,全部使用Boolean()、Number()和String()函数进行显式转换
- 自动转换为布尔值
if ( !undefined
&& !null
&& !0
&& !NaN
&& !''
) {
console.log('true');
} // true
//将一个表达式转为布尔值
// 写法一
expression ? true : false
// 写法二
!! expression
- 自动转换为字符串,主要发生在字符串的加法运算时
'5' + 1 // '51'
'5' + true // "5true"
'5' + false // "5false"
'5' + {} // "5[object Object]"
'5' + [] // "5"
'5' + function (){} // "5function (){}"
'5' + undefined // "5undefined"
'5' + null // "5null"
// 但这种转换容易出错
var obj = {
width: '100'
};
obj.width + 20 // "10020"
- 自动转换为数值
// 除了加法运算符(+)有可能把运算子转为字符串,其他运算符都会把运算子自动转成数值
'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
'1' - 1 // 0
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
null + 1 // 1
undefined + 1 // NaN // null转为数值时为0,而undefined转为数值时为NaN
// 一元运算符也会把运算子转成数值
+'abc' // NaN
-'abc' // NaN
+true // 1
-false // 0
错误处理机制
Error实例对象
JavaScript 解析或运行时,一旦发生错误,引擎就会抛出一个错误对象。JavaScript 原生提供Error
构造函数,所有抛出的错误都是这个构造函数的实例。
let err = new Error("xx");
if (err.name) {
console.log(err.name + ":" + err.message);
}
// 'Error:xx'
原生错误类型
Syntax
语法 ; Reference
引用 参考; Range
范围;
- SyntaxError对象: 解析代码时发生的语法错误
// 变量名错误
var 1a;
// Uncaught SyntaxError: Invalid or unexpected token
// 缺少括号
console.log 'hello');
// Uncaught SyntaxError: Unexpected string
- ReferenceError对象:引用一个不存的变量时发生的错误或将一个值分配给无法分配的对象
xx; // ReferenceError: xx is not defined
console.log() =1; // ReferenceError: Invalid left-hand side in assignment
- RangeError对象: 一个值超出有效范围时发生的错误
// 数组长度不得为负数
new Array(-1) // Uncaught RangeError: Invalid array length
- TypeError 对象:对象是变量或参数不是预期类型时发生的错误
new 123;// Uncaught TypeError: 123 is not a constructor new命令的参数应该是一个构造函数
var obj = {};
obj.unknownMethod();// Uncaught TypeError: obj.unknownMethod is not a function
// 调用对象不存在的方法也会报TypeError错误
- URIError 对象: 是 URI 相关函数的参数不正确时抛出的错误,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()这六个函数
//URI,统一资源标志符(Uniform Resource Identifier, URI),表示的是web上每一种可用的资源,如 HTML文档、图像、视频片段、程序等都由一个URI进行标识的。URI通常由三部分组成:①资源的命名机制;②存放资源的主机名;③资源自身的名称。
// URL是URI的子集,URL和URI都定义了资源是什么,但URL还定义了该如何访问资源。
- EvalError 对象: eval函数没有被正确执行时会抛出EvalError错误
**************************************************************************
以上这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';
}
// 让他继承Error对象
UserError.prototype = new Error();
UserError.prototype.constructor = UserError;
new UserError('这是自定义的错误!');
throw语句:手动中断程序执行,抛出一个错误
// throw抛出的错误就是它的参数
- 参数是Error实例
if (x <= 0) {
throw new Error('x 必须为正数');
}
- 参数是自定义错误
function UserError(message) {
this.message = message || '默认信息';
this.name = 'UserError';
}
throw new UserError('出错了!');
- 抛出字符串,数值,布尔值, 对象
// 抛出一个字符串
throw 'Error!';
// Uncaught Error!
// 抛出一个数值
throw 42;
// Uncaught 42
// 抛出一个布尔值
throw true;
// Uncaught true
// 抛出一个对象
throw {
toString: function () {
return 'Error!';
}
};
// Uncaught {toString: ƒ}
try…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: 结合try...catch结构使用,表示不管是否出现错误,都必需在最后运行的语句。
openFile();
try {
writeFile(Data);
} catch(e) {
handleError(e);
} finally {
closeFile();
}
// 首先打开一个文件,然后在try代码块中写入文件,如果没有发生错误,则运行finally代码块关闭文件;一旦发生错误,则先使用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