数据类型
JavaScript有五种基本类型(primitive type):null
,undefined
,boolean
,string
,number
,一种引用类型:object
。可以通过typeof
操作符来判断数据类型。由于js是弱类型语言,变量类型可在运行时动态改变。当使用运算符操作不同类型的数据时,js将自动转换为同一类型进行计算,因为这一过程是透明的、不易察觉的,因而也称作隐式类型转换。
typeof | result |
---|---|
11 | number |
NaN | number |
“11” | string |
true | boolean |
undefined | undefined |
null | object |
{} | object |
[] | object |
/\d/ | object |
function() {} | function |
注意当typeof
操作符对null
,对象,数组,正则表达式的判断结果均为object
。
Number数值转换
上边提到JavaScript有五种基本数据类型,其中string
,number
,boolean
类型分别有着对应的包装类型String
,Number
,Boolean
。包装类型对象将在读取基本类型变量值的时候在后台被创建,读取后被立即销毁。
js中各种操作符引起的类型转换,通常是将其他类型转换为数值类型的操作。这就不得不要提到Number()
构造器的转换规则:
- 如果是
Boolean
值,true
转换为1,false
转换为0。 - 如果是
null
,返回0。 - 如果是
undefined
,返回NaN
。 - 如果是字符串,遵循以下规则:
- 字符串为纯数字,转换为十进制数值;
- 字符串为浮点数,转换为对应浮点数值;
- 字符串为16进制数值,如
"0xf"
,转换为对应10进制数值; - 空字符串,转换为0;
- 包含字母等其他字符,返回
NaN
。
- 如果是对象,则调用对象的
valueOf()
方法,然后依照前面规则转换返回的值。如果转换结果为NaN
,则调用对象的toString()
方法,然后再依照前面的规则转换返回的字符串值。
隐式类型转换
alert()
方法接收字符串参数,将会在后台调用参数对象或其包装对象的toString()
方法。
alert(100) // '100' -> Number(100).toString() -> '100'
算数操作符
++,–
- 操作数为纯数字字符串,字符串被转换为数值后操作。
- 操作数包含非数字字符串,返回
NaN
。
var a = "10",
b = "hello";
console.log(typeof a++); // number
console.log(typeof --a); // number
console.log(b++); // NaN
+,-,*,/
- 字符串与数字相加,数字转字符串,返回字符串拼接结果。
-
,*
,/
,操作数包含纯数字字符串,转换为数值后操作;否则返回NaN
。- 一元加(
+
)减(-
)操作符相当于调用Number()
构造器进行转换。
+ "5"; // 5
- "5"; // -5
+ new Date(); // 1436700521490,new Date().valueOf()
10 + "5"; // "105"
"5" + 10; // "510"
"10" - "5"; // 5
10 - true; // 9
"5" - [10]; // -5
10 * "5"; // 50
10 * "5s"; // NaN
10 / ""; // Infinity
逻辑操作符
!
- 逻辑非将操作数转化为
boolean
值,并取反。可使用!!
模拟Booelan()
转型。 - 在
if()
,while()
等语句中,条件参数将被转换为boolean
型:正数,非空字符串,对象返回true
,其余操作数返回false
。
!! (Boolean()) | result |
---|---|
1 | true |
{} | true |
“hello” | true |
0 | false |
“” | false |
null | false |
undefined | false |
NaN | false |
关系操作符
==,<,>,<=,>=
这些都属于二元操作符,同类型情况下的比较不再赘述。需要注意的是字符串间的大小比较是基于每一位字符的字符编码比较。对于不同的数据类型,实际上都其将转换为数值作为中间类型相比较:
- 字符串和数值比较,字符串转数值。
- 有操作数是布尔值,布尔值转数值。
- 对于相等性(
==
)比较,undefined
等于null
,不能将它们转换为其他值。 NaN
不等于任何值,包括NaN
本身。- 有一个操作数是对象,则调用对象的
valueOf()
方法,按基本类型值进行比较。如果对象没有valueOf()
方法,则调用toString()
方法对转换结果执行比较。
先来看大小比较:
10 > 5; // true
10 > "5"; // true
true > false; // true,1 > 0
"Abc" > "abc"; // false,字符编码比较"A":65 < "a":97
"10" > [5]; // false,[5] -> "5",字符编码比较"10" < "5"
再来看相等比较:
'0' == 0; // true
'0' == false; // true
undefined == null; // true
null == false; // false
undefined == false; // false
NaN == NaN; // false
NaN == false; // false
0 == []; // true,Number([]) -> 0
0 == {}; // false,Number({}) -> NaN
引用类型的转换
回顾前面的例子可以发现,基本数据类型与数值类型间的数据转换,近似于将待转换类型作为参数传入Number()
构造函数,生成一个新数值型实例的过程。
在if()
、while()
等语句中,若单独以引用类型作为条件参数,均返回true
。而在其他的比较中,则常常将引用类型转换为基本数值比较。在前面的例子中大家也许会对包含数组的比较存在疑惑,接下来我们就一起剖析一下它们内在的转换过程。
首先来看关于引用型操作数的转换规则:
如果有一个操作数是对象,则调用对象的
valueOf()
方法,按基本类型值进行比较。如果对象没有valueOf()
方法,则调用toString()
方法对转换结果执行比较。
这里提到两个关键方法:valueOf()
和toString()
。在JavaScript中这两个方法默认继承自Object
对象,并由它们当前所在对象的原型方法重写,还可被用户自定义的同名实例方法覆盖。
valueOf()
JavaScript调用
valueOf()
方法将一个对象转换为原始类型(基本类型)值,通常我们不必显式的调用它,因为当遇到一个存在原始值的对象时,JavaScript将会自动调用他它的valueOf()
方法读取原始值。
一般的,基本类型值的包装类型对象均包含原始值,也就是基本类型值本身。
var boo = new Boolean(true);
num = new Number(10);
str = new String("hello");
boo.valueOf(); // true
num.valueOf(); // 10
str.valueOf(); // "hello"
对于没有原始值的对象,调用valueOf()
方法返回对象本身。
/\d/.valueOf(); // /\d/
/\d/.valueOf() instanceof RegExp; // true
[1, 2].valueOf(); // [1, 2]
[1, 2].valueOf() instanceof Array; // true
[1, 2].valueOf()[1]; // 2
toString()
每个对象都有一个
toString()
方法,当对象要表示为文本值或被指定为字符串形式时,toString()
方法将自动被调用。默认的,toString()
继承自object对象。如果这个方法没有在自定义对象中被重写,toString()
将返回“[object type]
”,其中type
表示对象类型。
在JavaScript的几个内置对象中,大多都重写了toString()
方法。String
,Number
,Boolean
对象调用toString()
返回基本类型值的字符串形式,RegExp
对象返回表达式字面量。
var boo = new Boolean(true);
num = new Number(10);
str = new String("hello");
boo.toString(); // "true"
num.toString(); // "10",Number的toString()方法可接收一个参数,表示进制
str.toString(); // "hello"
/\d/.toString(); // "/\d/"
Array
对象也重写了toString()
方法,它将会调用数组每一项的toString()方法,最后返回由每一项值的字符串形式拼接而成的一个以逗号分隔的字符串。
typeof [1].toString(); // string
[1,2,3].toString(); // "1, 2, 3"
[].toString(); // ""
我们再来回顾之前的例子:
"10" > [5]; // false
分析这条语句,有一个操作数是对象,先将对象转换为基本类型值。对象为数组类型,则调用数组的toString()
方法,之后根据再关系操作符转换规则进一步转换,以下为分解步骤:
"10" > [5] // 原式
"10" > [5].toString // 调用数组toString()方法
"10" > (5).toString() // 对数组每一项调用toString()方法
"10" > "5" // 原式转化为两个字符串间的比较
"10".charCodeAt(0) > "5".charCodeAt(0) // 比较每一位的字符编码
49 > 53 // 原式转化为两个数值间的比较
true // 返回结果
类似的,true
和[1,2]
之间的比较将转换为数值1
和字符串"1,2"
的比较,字符串"1,2"
将进一步转换为数字:Number("1,2") -> NaN
,最终便转换为1
和NaN
的比较。
自定义对象
自定义对象将继承object
的valueOf()
与toString()
方法。默认的,valueOf()
返回对象本身,toString()
返回"[object Object]"
。
可以在对象中重写继承来的valueOf()
或toString()
方法,重写后的调用规则为:首先检查valueOf()
是否被重写,是则调用重写后的方法,否则继续检查toString()
是否被重写,是则调用重写后的方法,否则按照普通对象的规则调用默认方法。
var obj = {name:"Lee"};
obj.valueOf = function (){ return 1;};
obj.toString = function (){ return this.name;};
obj + 1; // 2, 重写的valueOf()先被调用
delete a.valueOf; // 删除自定义的valueOf()
obj + 1; // "Lee1", 调用重写的toString()
最后,关于引用类型之间的比较,比较的是内存地址,不再赘述。
var a = [];
var b = [];
var c = a;
a == b; // false
a == c; // true
显式转换
string
,number
,boolean
之间的显示转换,直接调用转型方法或构造函数进行转换。
转字符串
- 使用
String()
构造函数 - 调用对象的
toString()
方法 - 使用”
+
“号操作符
String(true); // "true"
(10).toString(); // "10"
true.toString(); // "true"
10 + ""; // "10"
转数值
- 使用
Number()
构造函数 - 使用
window.parseInt()
方法 - 使用一元”
+
“操作符
Number("10"); // 10
parseInt("10a"); // 10
+ true; // 1
+ undefined; // NaN
+ null; // 0
转Boolean值
- 使用
Boolean()
构造函数 - 使用”
!!
“操作符
Boolean(""); // false
Boolean(1); // true
!!undefined; // false
!!null; // false
!!"hello"; // true