书香隽秀 - 2.数据类型
type: channel
link: https://www.huangshuxiu.com/types/
description: 2.数据类型
items: 数组
items: 函数
items: 对象
items: 字符串
items: 数值
items: null, undefined 和布尔值
items: 数据类型概述

数组
type: item
date: 2022-12-07T11:19:54+08:00
description: 数值概述整数和浮点数JavaScript 内部，所有数字都是以64位浮点数形式储存，即使整数也是如此。所以，1与1.0是相同的，是同一个数。1 === 1.0 // true这就是说，JavaScript 语言的底层根本没有整数，所有数字都是小数（64位浮点数）。容易造成混淆的是，某些运算只有整数才能完成，此时 JavaScript 会自动把64位浮点数，转成32位整数，然后再进行运算，参见《运算符》一章的“位运算”部分。由于浮点数不是精确的值，所以涉及小数的比较和运算要特别小心。0.1 + 0.2 === 0.3 // false 0.3 / 0.1 // 2.9999999999999996 (0.3 - 0.2) === (0.2 - 0.1) // false数值精度根据国际标准 IEEE 754，JavaScript 浮点数的64个二进制位，从最左边开始，是这样组成的。第1位：符号位，0表示正数，1表示负数第2位到第12位（共11位）：指数部分第13位到第64位（共52位）：小数部分（即有效数字）符号位决定了一个数的正负，指数部分决定了数值的大小，小数部分决定了数值的精度。指数部分一共有11个二进制位，因此大小范围就是0到2047。IEEE 754 规定，如果指数部分的值在0到2047之间（不含两个端点），那么有效数字的第一位默认总是1，不保存在64位浮点数之中。也就是说，有效数字这时总是1.xx...xx的形式，其中xx..xx的部分保存在64位浮点数之中，最长可能为52位。因此，JavaScript 提供的有效数字最长为53个二进制位。(-1)^符号位 * 1.xx...xx * 2^指数部分上面公式是正常情况下（指数部分在0到2047之间），一个数在 JavaScript 内部实际的表示形式。精度最多只能到53个二进制位，这意味着，绝对值小于2的53次方的整数，即-253到253，都可以精确表示。Math.pow(2, 53) // 9007199254740992 Math.pow(2, 53) + 1 // 9007199254740992 Math.pow(2, 53) + 2 // 9007199254740994 Math.pow(2, 53) + 3 // 9007199254740996 Math.pow(2, 53) + 4 // 9007199254740996上面代码中，大于2的53次方以后，整数运算的结果开始出现错误。所以，大于2的53次方的数值，都无法保持精度。由于2的53次方是一个16位的十进制数值，所以简单的法则就是，JavaScript 对15位的十进制数都可以精确处理。Math.pow(2, 53) // 9007199254740992 // 多出的三个有效数字，将无法保存 9007199254740992111 // 9007199254740992000上面示例表明，大于2的53次方以后，多出来的有效数字（最后三位的111）都会无法保存，变成0。数值范围根据标准，64位浮点数的指数部分的长度是11个二进制位，意味着指数部分的最大值是2047（2的11次方减1）。也就是说，64位浮点数的指数部分的值最大为2047，分出一半表示负数，则 JavaScript 能够表示的数值范围为21024到2-1023（开区间），超出这个范围的数无法表示。如果一个数大于等于2的1024次方，那么就会发生“正向溢出”，即 JavaScript 无法表示这么大的数，这时就会返回Infinity。Math.pow(2, 1024) // Infinity如果一个数小于等于2的-1075次方（指数部分最小值-1023，再加上小数部分的52位），那么就会发生为“负向溢出”，即 JavaScript 无法表示这么小的数，这时会直接返回0。Math.pow(2, -1075) // 0下面是一个实际的例子。var x = 0.5; for(var i = 0; i < 25; i++) { x = x * x; } x // 0上面代码中，对0.5连续做25次平方，由于最后结果太接近0，超出了可表示的范围，JavaScript 就直接将其转为0。JavaScript 提供Number对象的MAX_VALUE和MIN_VALUE属性，返回可以表示的具体的最大值和最小值。Number.MAX_VALUE // 1.7976931348623157e+308 Number.MIN_VALUE // 5e-324数值的表示法JavaScript 的数值有多种表示方法，可以用字面形式直接表示，比如35（十进制）和0xFF（十六进制）。数值也可以采用科学计数法表示，下面是几个科学计数法的例子。123e3 // 1230…

函数
type: item
date: 2022-12-07T11:19:21+08:00
description: 函数函数是一段可以反复调用的代码块。函数还能接受输入的参数，不同的参数有唯一对应的返回值。概述函数的声明JavaScript 有三种声明函数的方法。（1）function 命令function命令声明的代码区块，就是一个函数。function命令后面是函数名，函数名后面是一对圆括号，里面是传入函数的参数。函数体放在大括号里面。function print(s) { console.log(s); }上面的代码命名了一个print函数，以后使用print()这种形式，就可以调用相应的代码。这叫做函数的声明（Function Declaration）。（2）函数表达式除了用function命令声明函数，还可以采用变量赋值的写法。var print = function(s) { console.log(s); };这种写法将一个匿名函数赋值给变量。这时，这个匿名函数又称函数表达式（Function Expression），因为赋值语句的等号右侧只能放表达式。采用函数表达式声明函数时，function命令后面不带有函数名。如果加上函数名，该函数名只在函数体内部有效，在函数体外部无效。var print = function x(){ console.log(typeof x); }; x // ReferenceError: x is not defined print() // function上面代码在函数表达式中，加入了函数名x。这个x只在函数体内部可用，指代函数表达式本身，其他地方都不可用。这种写法的用处有两个，一是可以在函数体内部调用自身，二是方便除错（除错工具显示函数调用栈时，将显示函数名，而不再显示这里是一个匿名函数）。因此，下面的形式声明函数也非常常见。var f = function f() {};需要注意的是，函数的表达式需要在语句的结尾加上分号，表示语句结束。而函数的声明在结尾的大括号后面不用加分号。总的来说，这两种声明函数的方式，差别很细微，可以近似认为是等价的。（3）Function 构造函数第三种声明函数的方式是Function构造函数。var add = new Function( 'x', 'y', 'return x + y' ); // 等同于 function add(x, y) { return x + y; }上面代码中，Function构造函数接受三个参数，除了最后一个参数是add函数的“函数体”，其他参数都是add函数的参数。你可以传递任意数量的参数给Function构造函数，只有最后一个参数会被当做函数体，如果只有一个参数，该参数就是函数体。var foo = new Function( 'return "hello world";' ); // 等同于 function foo() { return 'hello world'; }Function构造函数可以不使用new命令，返回结果完全一样。总的来说，这种声明函数的方式非常不直观，几乎无人使用。函数的重复声明如果同一个函数被多次声明，后面的声明就会覆盖前面的声明。function f() { console.log(1); } f() // 2 function f() { console.log(2); } f() // 2上面代码中，后一次的函数声明覆盖了前面一次。而且，由于函数名的提升（参见下文），前一次声明在任何时候都是无效的，这一点要特别注意。圆括号运算符，return 语句和递归调用函数时，要使用圆括号运算符。圆括号之中，可以加入函数的参数。function add(x, y) { return x + y; } add(1, 1) // 2上面代码中，函数名后面紧跟一对圆括号，就会调用这个函数。函数体内部的return语句，表示返回。JavaScript 引擎遇到return语句，就直接返回return后面的那个表达式的值，后面即使还有语句，也不会得到执行。也就是说，return语句所带的那个表达式，就是函数的返回值。return语句不是必需的，如果没有的话，该函数就不返回任何值，或者说返回undefined。函数可以调用自身，这就是递归（recursion）。下面就是通过递归，计算斐波那契数列的代码。function fib(num) { if (num === 0) return 0; if (num === 1) return 1; return fib(num - 2) + fib(num - 1); } fib(6) // 8上面代码中，fib函数内部又调用了fib，计算得到斐波那契数列的第6个元素是8。第一等公民JavaScript 语言将函数看作一种值，与其它值（数值、字符串、布尔值等等）地位相同。凡是…

对象
type: item
date: 2022-12-07T11:18:54+08:00
description: 对象概述生成方法对象（object）是 JavaScript 语言的核心概念，也是最重要的数据类型。什么是对象？简单说，对象就是一组“键值对”（key-value）的集合，是一种无序的复合数据集合。var obj = { foo: 'Hello', bar: 'World' };上面代码中，大括号就定义了一个对象，它被赋值给变量obj，所以变量obj就指向一个对象。该对象内部包含两个键值对（又称为两个“成员”），第一个键值对是foo: 'Hello'，其中foo是“键名”（成员的名称），字符串Hello是“键值”（成员的值）。键名与键值之间用冒号分隔。第二个键值对是bar: 'World'，bar是键名，World是键值。两个键值对之间用逗号分隔。键名对象的所有键名都是字符串（ES6 又引入了 Symbol 值也可以作为键名），所以加不加引号都可以。上面的代码也可以写成下面这样。var obj = { 'foo': 'Hello', 'bar': 'World' };如果键名是数值，会被自动转为字符串。var obj = { 1: 'a', 3.2: 'b', 1e2: true, 1e-2: true, .234: true, 0xFF: true }; obj // Object { // 1: "a", // 3.2: "b", // 100: true, // 0.01: true, // 0.234: true, // 255: true // } obj['100'] // true上面代码中，对象obj的所有键名虽然看上去像数值，实际上都被自动转成了字符串。如果键名不符合标识名的条件（比如第一个字符为数字，或者含有空格或运算符），且也不是数字，则必须加上引号，否则会报错。// 报错 var obj = { 1p: 'Hello World' }; // 不报错 var obj = { '1p': 'Hello World', 'h w': 'Hello World', 'p+q': 'Hello World' };上面对象的三个键名，都不符合标识名的条件，所以必须加上引号。对象的每一个键名又称为“属性”（property），它的“键值”可以是任何数据类型。如果一个属性的值为函数，通常把这个属性称为“方法”，它可以像函数那样调用。var obj = { p: function (x) { return 2 * x; } }; obj.p(1) // 2上面代码中，对象obj的属性p，就指向一个函数。如果属性的值还是一个对象，就形成了链式引用。var o1 = {}; var o2 = { bar: 'hello' }; o1.foo = o2; o1.foo.bar // "hello"上面代码中，对象o1的属性foo指向对象o2，就可以链式引用o2的属性。对象的属性之间用逗号分隔，最后一个属性后面可以加逗号（trailing comma），也可以不加。var obj = { p: 123, m: function () { ... }, }上面的代码中，m属性后面的那个逗号，有没有都可以。属性可以动态创建，不必在对象声明时就指定。var obj = {}; obj.foo = 123; obj.foo // 123上面代码中，直接对obj对象的foo属性赋值，结果就在运行时创建了foo属性。对象的引用如果不同的变量名指向同一个对象，那么它们都是这个对象的引用，也就是说指向同一个内存地址。修改其中一个变量，会影响到其他所有变量。var o1 = {}; var o2 = o1; o1.a = 1; o2.a // 1 o2.b = 2; o1.b // 2上面代码中，o1和o2指向同一个对象，因此为其中任何一个变量添加属性，另一个变量都可以读写该属性。此时，如果取消某一个变量对于原对象的引用，不会影响到另一个变量。var o1 = {}; var o2 = o1; o1 = 1; o2 // {}上面代码中，o1和o2指向同一个对象，然后o1的值变为1，这时不会对o2产生影响，o2还是指向原来的那个对象。但是，这种引用只局限于对象，如果两个变量指向同一个原始类型的值。那么，变量这时都是值的拷贝。var x = 1; var y = x; x = 2; y // 1上面的代码中，当x的值发生变化后，y的值并不变，这就表示y和x并不是指向同一个内存地址。表达式还是语句？对象采用大括号表示，这导致了一个问题：如果行首是一个大括号，它到底是表达式还是语句？{ foo: 123 }JavaScript 引擎读到上面这行代码，会发现可能有两种含义。第一种可能是，这是一个表达式，表示一个包含foo属性的对象；第二种可能是，这是一…

字符串
type: item
date: 2022-12-07T11:18:25+08:00
description: 字符串概述定义字符串就是零个或多个排在一起的字符，放在单引号或双引号之中。'abc' "abc"单引号字符串的内部，可以使用双引号。双引号字符串的内部，可以使用单引号。'key = "value"' "It's a long journey"上面两个都是合法的字符串。如果要在单引号字符串的内部，使用单引号，就必须在内部的单引号前面加上反斜杠，用来转义。双引号字符串内部使用双引号，也是如此。'Did she say \'Hello\'?' // "Did she say 'Hello'?" "Did she say \"Hello\"?" // "Did she say "Hello"?"由于 HTML 语言的属性值使用双引号，所以很多项目约定 JavaScript 语言的字符串只使用单引号，本教程遵守这个约定。当然，只使用双引号也完全可以。重要的是坚持使用一种风格，不要一会使用单引号表示字符串，一会又使用双引号表示。字符串默认只能写在一行内，分成多行将会报错。'a b c' // SyntaxError: Unexpected token ILLEGAL上面代码将一个字符串分成三行，JavaScript 就会报错。如果长字符串必须分成多行，可以在每一行的尾部使用反斜杠。var longString = 'Long \ long \ long \ string'; longString // "Long long long string"上面代码表示，加了反斜杠以后，原来写在一行的字符串，可以分成多行书写。但是，输出的时候还是单行，效果与写在同一行完全一样。注意，反斜杠的后面必须是换行符，而不能有其他字符（比如空格），否则会报错。连接运算符（+）可以连接多个单行字符串，将长字符串拆成多行书写，输出的时候也是单行。var longString = 'Long ' + 'long ' + 'long ' + 'string';如果想输出多行字符串，有一种利用多行注释的变通方法。(function () { /* line 1 line 2 line 3 */}).toString().split('\n').slice(1, -1).join('\n') // "line 1 // line 2 // line 3"上面的例子中，输出的字符串就是多行。转义反斜杠（\）在字符串内有特殊含义，用来表示一些特殊字符，所以又称为转义符。需要用反斜杠转义的特殊字符，主要有下面这些。\0 ：null（\u0000）\b ：后退键（\u0008）\f ：换页符（\u000C）\n ：换行符（\u000A）\r ：回车键（\u000D）\t ：制表符（\u0009）\v ：垂直制表符（\u000B）\' ：单引号（\u0027）\" ：双引号（\u0022）\\ ：反斜杠（\u005C）上面这些字符前面加上反斜杠，都表示特殊含义。console.log('1\n2') // 1 // 2上面代码中，\n表示换行，输出的时候就分成了两行。反斜杠还有三种特殊用法。（1）\HHH反斜杠后面紧跟三个八进制数（000到377），代表一个字符。HHH对应该字符的 Unicode 码点，比如\251表示版权符号。显然，这种方法只能输出256种字符。（2）\xHH\x后面紧跟两个十六进制数（00到FF），代表一个字符。HH对应该字符的 Unicode 码点，比如\xA9表示版权符号。这种方法也只能输出256种字符。（3）\uXXXX\u后面紧跟四个十六进制数（0000到FFFF），代表一个字符。XXXX对应该字符的 Unicode 码点，比如\u00A9表示版权符号。下面是这三种字符特殊写法的例子。'\251' // "©" '\xA9' // "©" '\u00A9' // "©" '\172' === 'z' // true '\x7A' === 'z' // true '\u007A' === 'z' // true如果在非特殊字符前面使用反斜杠，则反斜杠会被省略。'\a' // "a"上面代码中，a是一个正常字符，前面加反斜杠没有特殊含义，反斜杠会被自动省略。如果字符串的正常内容之中，需要包含反斜杠，则反斜杠前面需要再加一个反斜杠，用来对自身转义。"Prev \\ Next" // "Prev \ Next"字符串与数组字符串可以被视为字符数组，因此可以使用数组的方括号运算符，用来返回某个位置的字符（位置编号从0开始）。var s = 'hello'; s[0] // "h" s[1] // "e" s[4] // "o" // 直接对字符串使用方括号运算符 'hello'[1] // "e"如果方括号中的数字超过字符串的长度，或者方括号中根本不是数字，则…

数值
type: item
date: 2022-12-07T11:17:44+08:00
description: 数组定义数组（array）是按次序排列的一组值。每个值的位置都有编号（从0开始），整个数组用方括号表示。var arr = ['a', 'b', 'c'];上面代码中的a、b、c就构成一个数组，两端的方括号是数组的标志。a是0号位置，b是1号位置，c是2号位置。除了在定义时赋值，数组也可以先定义后赋值。var arr = []; arr[0] = 'a'; arr[1] = 'b'; arr[2] = 'c';任何类型的数据，都可以放入数组。var arr = [ {a: 1}, [1, 2, 3], function() {return true;} ]; arr[0] // Object {a: 1} arr[1] // [1, 2, 3] arr[2] // function (){return true;}上面数组arr的3个成员依次是对象、数组、函数。如果数组的元素还是数组，就形成了多维数组。var a = [[1, 2], [3, 4]]; a[0][1] // 2 a[1][1] // 4数组的本质本质上，数组属于一种特殊的对象。typeof运算符会返回数组的类型是object。typeof [1, 2, 3] // "object"上面代码表明，typeof运算符认为数组的类型就是对象。数组的特殊性体现在，它的键名是按次序排列的一组整数（0，1，2...）。var arr = ['a', 'b', 'c']; Object.keys(arr) // ["0", "1", "2"]上面代码中，Object.keys方法返回数组的所有键名。可以看到数组的键名就是整数0、1、2。由于数组成员的键名是固定的（默认总是0、1、2...），因此数组不用为每个元素指定键名，而对象的每个成员都必须指定键名。JavaScript 语言规定，对象的键名一律为字符串，所以，数组的键名其实也是字符串。之所以可以用数值读取，是因为非字符串的键名会被转为字符串。var arr = ['a', 'b', 'c']; arr['0'] // 'a' arr[0] // 'a'上面代码分别用数值和字符串作为键名，结果都能读取数组。原因是数值键名被自动转为了字符串。注意，这点在赋值时也成立。一个值总是先转成字符串，再作为键名进行赋值。var a = []; a[1.00] = 6; a[1] // 6上面代码中，由于1.00转成字符串是1，所以通过数字键1可以读取值。上一章说过，对象有两种读取成员的方法：点结构（object.key）和方括号结构（object[key]）。但是，对于数值的键名，不能使用点结构。var arr = [1, 2, 3]; arr.0 // SyntaxError上面代码中，arr.0的写法不合法，因为单独的数值不能作为标识符（identifier）。所以，数组成员只能用方括号arr[0]表示（方括号是运算符，可以接受数值）。length 属性数组的length属性，返回数组的成员数量。['a', 'b', 'c'].length // 3JavaScript 使用一个32位整数，保存数组的元素个数。这意味着，数组成员最多只有 4294967295 个（232 - 1）个，也就是说length属性的最大值就是 4294967295。只要是数组，就一定有length属性。该属性是一个动态的值，等于键名中的最大整数加上1。var arr = ['a', 'b']; arr.length // 2 arr[2] = 'c'; arr.length // 3 arr[9] = 'd'; arr.length // 10 arr[1000] = 'e'; arr.length // 1001上面代码表示，数组的数字键不需要连续，length属性的值总是比最大的那个整数键大1。另外，这也表明数组是一种动态的数据结构，可以随时增减数组的成员。length属性是可写的。如果人为设置一个小于当前成员个数的值，该数组的成员数量会自动减少到length设置的值。var arr = [ 'a', 'b', 'c' ]; arr.length // 3 arr.length = 2; arr // ["a", "b"]上面代码表示，当数组的length属性设为2（即最大的整数键只能是1）那么整数键2（值为c）就已经不在数组中了，被自动删除了。清空数组的一个有效方法，就是将length属性设为0。var arr = [ 'a', 'b', 'c' ]; arr.length = 0; arr // []如果人为设置length大于当前元素个数，则数组的成员数量会增加到这个值，新增的位置都是空位。var a = ['a']; a.length = …

null, undefined 和布尔值
type: item
date: 2022-12-07T11:17:16+08:00
description: null, undefined 和布尔值null 和 undefined概述null与undefined都可以表示“没有”，含义非常相似。将一个变量赋值为undefined或null，老实说，语法效果几乎没区别。var a = undefined; // 或者 var a = null;上面代码中，变量a分别被赋值为undefined和null，这两种写法的效果几乎等价。在if语句中，它们都会被自动转为false，相等运算符（==）甚至直接报告两者相等。if (!undefined) { console.log('undefined is false'); } // undefined is false if (!null) { console.log('null is false'); } // null is false undefined == null // true从上面代码可见，两者的行为是何等相似！谷歌公司开发的 JavaScript 语言的替代品 Dart 语言，就明确规定只有null，没有undefined！既然含义与用法都差不多，为什么要同时设置两个这样的值，这不是无端增加复杂度，令初学者困扰吗？这与历史原因有关。1995年 JavaScript 诞生时，最初像 Java 一样，只设置了null表示"无"。根据 C 语言的传统，null可以自动转为0。Number(null) // 0 5 + null // 5上面代码中，null转为数字时，自动变成0。但是，JavaScript 的设计者 Brendan Eich，觉得这样做还不够。首先，第一版的 JavaScript 里面，null就像在 Java 里一样，被当成一个对象，Brendan Eich 觉得表示“无”的值最好不是对象。其次，那时的 JavaScript 不包括错误处理机制，Brendan Eich 觉得，如果null自动转为0，很不容易发现错误。因此，他又设计了一个undefined。区别是这样的：null是一个表示“空”的对象，转为数值时为0；undefined是一个表示"此处无定义"的原始值，转为数值时为NaN。Number(undefined) // NaN 5 + undefined // NaN用法和含义对于null和undefined，大致可以像下面这样理解。null表示空值，即该处的值现在为空。调用函数时，某个参数未设置任何值，这时就可以传入null，表示该参数为空。比如，某个函数接受引擎抛出的错误作为参数，如果运行过程中未出错，那么这个参数就会传入null，表示未发生错误。undefined表示“未定义”，下面是返回undefined的典型场景。// 变量声明了，但没有赋值 var i; i // undefined // 调用函数时，应该提供的参数没有提供，该参数等于 undefined function f(x) { return x; } f() // undefined // 对象没有赋值的属性 var o = new Object(); o.p // undefined // 函数没有返回值时，默认返回 undefined function f() {} f() // undefined布尔值布尔值代表“真”和“假”两个状态。“真”用关键字true表示，“假”用关键字false表示。布尔值只有这两个值。下列运算符会返回布尔值：前置逻辑运算符： ! (Not)相等运算符：===，!==，==，!=比较运算符：>，>=，<，<=如果 JavaScript 预期某个位置应该是布尔值，会将该位置上现有的值自动转为布尔值。转换规则是除了下面六个值被转为false，其他值都视为true。undefinednullfalse0NaN""或''（空字符串）布尔值往往用于程序流程的控制，请看一个例子。if ('') { console.log('true'); } // 没有任何输出上面代码中，if命令后面的判断条件，预期应该是一个布尔值，所以 JavaScript 自动将空字符串，转为布尔值false，导致程序不会进入代码块，所以没有任何输出。注意，空数组（[]）和空对象（{}）对应的布尔值，都是true。if ([]) { console.log('true'); } // true if ({}) { console.log('true'); } // true更多关于数据类型转换的介绍，参见《数据类型转换》一章。参考链接Axel Rauschmayer, Categorizing values in JavaScript

数据类型概述
type: item
date: 2022-12-07T11:16:47+08:00
description: 数据类型概述简介JavaScript 语言的每一个值，都属于某一种数据类型。JavaScript 的数据类型，共有六种。（ES6 又新增了第七种 Symbol 类型的值，本教程不涉及。）数值（number）：整数和小数（比如1和3.14）。字符串（string）：文本（比如Hello World）。布尔值（boolean）：表示真伪的两个特殊值，即true（真）和false（假）。undefined：表示“未定义”或不存在，即由于目前没有定义，所以此处暂时没有任何值。null：表示空值，即此处的值为空。对象（object）：各种值组成的集合。通常，数值、字符串、布尔值这三种类型，合称为原始类型（primitive type）的值，即它们是最基本的数据类型，不能再细分了。对象则称为合成类型（complex type）的值，因为一个对象往往是多个原始类型的值的合成，可以看作是一个存放各种值的容器。至于undefined和null，一般将它们看成两个特殊值。对象是最复杂的数据类型，又可以分成三个子类型。狭义的对象（object）数组（array）函数（function）狭义的对象和数组是两种不同的数据组合方式，除非特别声明，本教程的“对象”都特指狭义的对象。函数其实是处理数据的方法，JavaScript 把它当成一种数据类型，可以赋值给变量，这为编程带来了很大的灵活性，也为 JavaScript 的“函数式编程”奠定了基础。typeof 运算符JavaScript 有三种方法，可以确定一个值到底是什么类型。typeof运算符instanceof运算符Object.prototype.toString方法instanceof运算符和Object.prototype.toString方法，将在后文介绍。这里介绍typeof运算符。typeof运算符可以返回一个值的数据类型。数值、字符串、布尔值分别返回number、string、boolean。typeof 123 // "number" typeof '123' // "string" typeof false // "boolean"函数返回function。function f() {} typeof f // "function"undefined返回undefined。typeof undefined // "undefined"利用这一点，typeof可以用来检查一个没有声明的变量，而不报错。v // ReferenceError: v is not defined typeof v // "undefined"上面代码中，变量v没有用var命令声明，直接使用就会报错。但是，放在typeof后面，就不报错了，而是返回undefined。实际编程中，这个特点通常用在判断语句。// 错误的写法 if (v) { // ... } // ReferenceError: v is not defined // 正确的写法 if (typeof v === "undefined") { // ... }对象返回object。typeof window // "object" typeof {} // "object" typeof [] // "object"上面代码中，空数组（[]）的类型也是object，这表示在 JavaScript 内部，数组本质上只是一种特殊的对象。这里顺便提一下，instanceof运算符可以区分数组和对象。instanceof运算符的详细解释，请见《面向对象编程》一章。var o = {}; var a = []; o instanceof Array // false a instanceof Array // truenull返回object。typeof null // "object"null的类型是object，这是由于历史原因造成的。1995年的 JavaScript 语言第一版，只设计了五种数据类型（对象、整数、浮点数、字符串和布尔值），没考虑null，只把它当作object的一种特殊值。后来null独立出来，作为一种单独的数据类型，为了兼容以前的代码，typeof null返回object就没法改变了。参考链接Axel Rauschmayer, Improving the JavaScript typeof operator