# 概述
本文主要来自 GitHub 中作者开放的文档
在此书之前第一章中已经提到过,JavaScript 中的值有类型之分,而变量是不存在类型的,目前 JavaScript 拥有如下几种类型,区分 null 和 undefined 的话总共 7 种类型。
- string
- number
- boolean
- null and undefined
- object
- symbol (new to ES6)
如果你不知到某个值的类型,JavaScript 里提供了一个名字为 typeof 的函数,用这个函数可以检测你的值的类型。
var a; | |
typeof a; // "undefined" | |
a = "hello world"; | |
typeof a; // "string" | |
a = 42; | |
typeof a; // "number" | |
a = true; | |
typeof a; // "boolean" | |
a = null; | |
typeof a; // "object" -- weird, bug | |
a = undefined; | |
typeof a; // "undefined" | |
a = { b: "c" }; | |
typeof a; // "object" |
注意上面的 6 种类型返回的都是 string 类型的字符串(如果算上 es6 的 "symbol" 类型,就是 7 种),如 typeof "abc" 返回的是 "string",而不是 string。
同时可以看到上面只定义了一个变量,仅是在不同时刻赋予的值是不同的,我们这里使用的 typeof a 其实际意义上并不是指 type of a,而是 type of the value current in a,是指值的类型而非变量,JavaScript 中只有值拥有类型,而变量只是这些具体值的一个容器。
这其中有一个值的类型比较有趣,那就是 typeof null,因为如果你测试一下的话,就会发现她返回的竟然是一个 "object",而你希望她应该正确的返回一个 "null",注意,这是一个由来已久的 JavaScript bug,由于现在很多 web 项目中使用了这个 bug,所以不可能再去修复,否则会引发更多的问题.
假如你定义 a = undefined,再定义 var a,当你使用 typeof 输出 a 时,会发现其实都是 "undefined",这说明这两种操作可以等同看待,类似这种返回 "undefined" 的操作还有一些,如将一个没有返回值的函数(即 void 类型)赋值给 a,其返回的也会是一个 "undefined"。
function fun(){ | |
} | |
var a = fun(); | |
typeof a; // "undefined" |
下面我们就来仔细看一下
# "object"
首先她是一个复合值,简单说就是她可以包含各种类型的值,也是我们最常用的一个类型。
var obj = { | |
a: "hello world", | |
b: 42, | |
c: true | |
}; | |
obj.a; // "hello world" | |
obj.b; // 42 | |
obj.c; // true | |
obj["a"]; // "hello world" | |
obj["b"]; // 42 | |
obj["c"]; // true |
可以抽象成下面这幅图
注意上面两种获取值的方式,一般优先选择第一种使用点号获取的方式(主要是由于其简短易读的特点)。但是某些情况下,例如以 property/key 形式定义的值,一般会使用后者获取,如
var obj = { | |
a: "hello world", | |
b: 42 | |
}; | |
var b = "a"; | |
obj[b]; // "hello world" | |
obj["b"]; // 42 |
类似地,还有一些常用的类型,如数组 array 和函数 function,这些类型不是内置类型(build-in type),但可以想象成 object 的子类型。其中数组不是以 property/key 形式定义的,而是以数字作为下标按一定顺序排列的,以下标 0 作为起点,如
var arr = [ | |
"hello world", | |
42, | |
true | |
]; | |
arr[0]; // "hello world" | |
arr[1]; // 42 | |
arr[2]; // true | |
arr.length; // 3 | |
typeof arr; // "object" |
建议在使用数字定位的值(numerically positioned values)时定义成数组的形式,而在使用具体名字获取值(property/key 形式,named properties)时定义成 object 类型。
function 也是 object 的一个子类型,如下
function foo() { | |
return 42; | |
} | |
foo.bar = "hello world"; | |
typeof foo; // "function" | |
typeof foo(); // "number" | |
typeof foo.bar; // "string" |
注意,通常只是在真正需要时才会调用函数 function 里的值(如上面定义的 foo.bar 这种取值方式)。
# 内置类型的方法
在 JavaScript 中,如果你定义的数据类型是内置类型,那么你可以使用其自带的方法去做一些操作。如字符串的大小写转换,长度大小的获取等。
var a = "hello world"; | |
var b = 3.14159; | |
a.length; // 11 | |
a.toUpperCase(); // "HELLO WORLD" | |
b.toFixed(4); // "3.1416" |
一个字符串值可以被一个 String 对象包装,一个数字可以被一个 Number 对象包装,并且一个布尔值可以被一个 Boolean 对象包装。大多数情况下,你不必担心或直接使用这些值的对象包装形式,基本上我们都是在使用原始数据类型,而不必理会其自身的拆箱和装箱操作。
# 值的比较
(1)强制转换:分为显式和隐式两种,显式转换可以简单地从代码中判断,而隐式的类型转换相对比较隐蔽,可能存在一些不够明显的副作用(或不好的行为)。我们通常会认为强制做类型转换是很邪恶的一种行为,这是因为强制类型转换可能会存在一些出人意料的结果,但事实上,当我们使用强制转换时,很多时候场景是相对合理的,容易被人理解的,甚至可以提高代码的可读性。
显式转换:
var a = "42"; | |
var b = Number( a ); | |
a; // "42" | |
b; |
隐式转换:
var a = "42"; | |
var b = a * 1; // "42" implicitly coerced to 42 here | |
a; // "42" | |
b; // 42 -- the number! |
(2)布尔值判断(Truthy & Falsy):当非布尔值被强制转为布尔值时,它是真还是假?下面列一下默认的一些情况。在实际使用中,如果你的 ajax 返回的数据类型比较复杂,那就要小心了。
默认为 falsy 的情况:
- "" (empty string)
- 0, -0, NaN (invalid number)
- null, undefined
- false
默认为 truthy 的情况:
- "hello"
- 42
- true
- [ ], [ 1, "2", 3 ] (arrays)
- { }, { a: 42 } (objects)
- function foo() { .. } (functions)
举个例子说明一下
var t = ""; | |
if (t){ | |
console.info("1"); | |
}else{ | |
console.info("2"); // 输出 2 | |
} |
可以看到默认空字符串做布尔转换时,其实就是 false。
(3)相等判断:这个是一个比较常见的问题,一般使用判断是不做类型区别的,而 = 会首先判断你的数据类型是否一致,如
var a = "42"; | |
var b = 42; | |
a == b; // true | |
a === b; // false |
所以:
- 如果比较中的任何一个值(aka side)可以是真或假值,则避免并使用 =。
- 如果比较中的任一值可能是这些特定值(0,“” 或 [] - 空数组),请避免并使用 =。
- 在所有其他情况下,可以安全地使用 ==。这不仅是安全的,而且在很多情况下它可以简化代码,提高可读性。
想要具体了解的话可以阅读 es5 官方文档的 11.9.3 章节。
另外,在做数组判断时,可能会遇到下面这种情况。至于原因是什么,可以参考 es5 的官方文档。
var a = [1,2,3]; | |
var b = [1,2,3]; | |
var c = "1,2,3"; | |
a == c; // true | |
b == c; // true | |
a == b; // false |
(4)不等式判断:通常我们认为做不等判别的知识 number 类型,然而在 JavaScript 中,string 类型值同样可以,string 类型的判断使用的是字母排列规则,如 b<f,a<b
console.info("bar" < "foo"); // 输出 true |
console.info("24" < "21"); // 输出 false |
当然,你也可以将 number 和 string 做比较,如
var a = 41; | |
var b = "42"; | |
var c = "43"; | |
a < b; // true | |
b < c; // true |
然而,有时我们会遇到比较奇葩的判断,致使两边的数据类型 “严格不想等”(strict inequality),如
var a = 42; | |
var b = "foo"; | |
a < b; // false | |
a > b; // false | |
a == b; // false |
此时,上面的 b 会判定为 "invalid number value" ,即 NaN,在 es5 文档上讲:NaN 既不大于也不小于任何其他值。所以都是 false。