这要是没搞懂你好意思说学过JS?
这怕是JavaScript中最坑、最有毒的一个部分了。
将值从一种类型转换成另一种类型叫做类型转换。例如:
var a = 1;
var b = String(a); // "1" 显式转换var c = "" + a; // "1" 隐式转换
在JavaScript中,我们把上面列举的两类转换都称为 强制类型转换 ,按照显式和隐式的区别我们可以将强制类型转换分为 显式强制类型转换 和 隐式强制类型转换 。
当然要注意,这里的显式和隐式都是相对的。假如你对JavaScript运行中的所有将要执行的类型转换都牢记于心,那当然可以把他们全部称为显式强制类型转换。本文主要参考《你不知道的JavaScript中卷》,因此参照书中的标准来界定显式和隐式。
JavaScript中的强制类型转换总是返回标量基本类型值,例如字符串、数字和布尔值,不会返回对象和函数。
这篇文章会学习抽象值操作。
ToString
基本类型的字符串化规则为:null转换为"null"
undefined转换为"undefined"
true转换为true
数字的字符串化则遵循通用规则,对于极小和极大的数使用指数形式
对于对象来说,字符串化的时候会调用对象的toString()方法,并使用其返回值。如果没有自行定义的话,toString()方法会默认返回内部属性[[Class]]的值。例如以下的例子:
"" + {}; // "[object Object]"
"" + [1,2,3]; // "1,2,3""" + {toString: function(){return "1"}}; // "1"
toString() 可以被显式调用,或在需要字符串化的时候自动调用。
JSON 字符串化
JSON应该是我们在JS中最常用的序列化和反序列化工具之一了。JSON.stringify()在将对象序列化为字符串的时候也用到了ToString。但是需要注意,JSON字符串化并非严格意义上的强制类型转换。
对于大多数基本类型值来说,JSON字符串化的结果和toString()是差不多的,只不过序列化的结果总是字符串:
JSON.stringify(1); // "1"JSON.stringify("1"); // ""1"" (带有双引号的字符串)JSON.stringify(null); // "null"JSON.stringify(true); // "true"
有些值JSON是无法处理的,例如:undefined、function、symbol 和包含循环引用(对象之间相互引用)的对象。我们把这些它们称作 不安全的JSON值 。
相对的,所有 安全的JSON值(JSON-safe) 都可以使用JSON.stringify()字符串化。
JSON.stringify() 在对象中遇到 undefined、 function 和 symbol的时候会自动忽略他们,在数组中这回返回null(为的是保证数据的下标不变),在遇到循环引用的对象时会报错。例如:
JSON.stringify(undefined); // undefinedJSON.stringify(function(){}); // undefined
JSON.stringfiy(
[1, undefined, function(){}, 4]
); // "[1,null,null,4]"JSON.stringify(
{a: 1, b: function(){}, c: undefined }
); // "{"a":1}"JSON.stringify(
{toString: function(){return "1"}}
); // "{}"
var a = {};
var b = {a: a};
a.b = b;
JSON.stringify(a); // Uncaught TypeError: Converting circular structure to JSON
如果对象中定义了toJSON()方法,JSON字符串化的时候会首先调用该方法,然后用它的返回值来进行序列化。如果你对某些非法JSON值定义了toJSON()方法,并返回一个安全的JSON值,那么这个值就能被字符串化了。 注意,对象是不自带toJSON()方法的,需要你主动定义它。 例如:
var a = {};
var b = {a: a};
a.b = b;
b.toJSON = function(){
return {};
};
JSON.stringify(a); // "{"b":{}}"
var foo = function(){}
foo.toJSON = function(){return 123}
JSON.stringify(foo); // "123"
var bar = {
a: undefined,
toJSON: function (){ return {a: null} }
}
JSON.stringify(bar); // "{"a":null}"
toJSON()返回的并不一定是JSON字符串化后的值(这样其实会对字符串再做一次字符串化),而应当是一个适当的值,可以是任何类型,然后再由JSON.stringify()对其字符串化。
ToNumber
ToNumber相比ToString来说就简单很多。
将基本数据类型转换为数字的规则为:
- true转换为1
- false转换为0
- undefined转换为NaN
- null转换为0
- 对字符串的转换遵循通用规则,处理失败时返回NaN,对以0开头的十六进制数按十进制处理而非十六进制。
对于对象来说,它们会先被转换成相应的基本类型值,如果返回的是非数字的基本类型值,则再遵循以上规则将其转换成数字。
对象转换成基本类型值的规则为:首先检查是否有valueOf()方法,如果有并且返回基本类型值,就使用改值进行转换。如果没有则使用toString()方法的返回值(如果存在)来进行强制类型转换。如果valueOf()和toString()都不反悔基本类型值,则会产生TypeError错误。
几个例子:
var a = {valueOf: function(){return "1"}};
var b = {toString: function(){return "2"}}
Number(a); // 1Number(b); // 2
var c = [1,2,3];
Number(c); // NaNc.valueOf = function(){
return 1;
}
Number(c); // 1
var d = [];
Number(d); // 0d.toString = function(){
return 1;
}
Number(d); // 1
var e = [1,2,3];
e.toString = function(){
return this.join("") // 123}
Number(e) //123
ToBoolean
boolean的两个字true和false分别代表着 真 和 假 。在JavaScript中,数字1和0与true和false并不等价。
对于基本类型值来说,JavaScript中的值可以分为两类:
- 可以被强制类型转换成false的值
- 其他(可以被强制类型转换成true)的值
以下的值为 假值 :
- undefined
- null
- false
- +0、-0和NaN
- ""
除了假值表以外的所有值都可以理解为 真值 。
一般来说,所有的对象都是真值,但是有一些特殊情况,我们可以把他们叫做 假值对象 。
先来说几个例子——
var a = new Boolean(false);
var b = new Number(0);
var c = new String("");
var d = new Boolean(0);
var foo = Boolean(a && b && c &&d);
foo; // true
foo为true,说明a、b、c、d都为true。
这些封装了假值的对象都并非假值。
我们来看看真正的假值对象——他们是来自浏览器在某些特定条件下创造的对象,例如document.all:
document.all; // 返回一个 HTMLAllCollection 对象!!document.all; // false
这是一个类数组对象,由DOM提供(而非JavaScript引擎)提供给JavaScript程序使用。它以前曾是一个真正意义上的对象,布尔强制类型转换的结果为true,但现在它是一个假值对象——并且这已经死一个被废止的方法了。
由于很多JavaScript程序依赖document.all来判断是否是旧版浏览器,因此一直没有把它去掉。
再说一说几个真值的情况——虽然刚才说过了假值表以外的都是真值,但是你还可能会遇到一些比较刁钻的状况:
var a = Boolean("false");
a; // true
var b = Boolean("0");
b; // true
var c = Boolean("\"\"");
c; // true
对于字符串来说,除了""以外都是真值。同时,对于[]、{}、function(){}这一类的对象都是真值。
参考《你不知道的JavaScript中卷》第一部分第四章