JavaScript中的“类型”
JavaScript有七种数据类型,六种基本类型和一种复杂数据类型:
- 数值(Number)
- 字符串(String)
- 布尔值(Boolean)
- 空值(Null)
- 未定义(Undefined)
- 符号(Symbol)
- 对象(Object)
typeof操作符
我们可以使用typeof来检查值得类型,它返回的是类型的字符串值。
| 类型 | 结果 |
|---|---|
| Number | "number" |
| String | "string" |
| Boolean | "boolean" |
| Null | "object" |
| Undefined | "undefined" |
| Symbol | "symbol" |
| Object | "object" |
| Function | "function" |
typeof 2019 === "number";
typeof "tom" === "string";
typeof true === "boolean";
typeof null === "object";
typeof undefined === "undefined";
typeof Symbol() === "symbol";
typeof {name:"tom"} === "object";
typeof function foo(){} === "function";
typeof null === "object":
在JavaScript最初的实现中,JavaScript中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于null代表的是空指针(大多数平台下值为0x00),因此,null的类型标签也成为了 0,typeof null就错误的返回了"object"。(mdn官方typeof有说明)
typeof function foo(){} === "function":
函数实际上object的一个“子类型”,函数是“可调用的对象”。它有一个内部属性[[Call]],该属性使其可以被调用。函数不仅是对象,理所当然可以给函数添加属性,例如:
function foo(name,age) {};
foo.job = "code";
console.log(foo.length); //2 因为该函数声明了两个命名参数
console.log(foo.job); //"code"
类型与值
JavaScript中的变量是没有类型的,只有值才有。变量可以随时持有任何类型的值。
var foo = 2019;
typeof foo === "number";//true
foo = "tom";
typeof === "string"; //true
在对变量执行typeof操作时,得到的结果并不是该变量的类型,而是该变量持有的值的类型。
undefined
变量在未持有值的时候为undefined。此时typeof返回"undefined":
var foo;
typeof foo; //"undefined"
var bar = "cat";
var x;
bar = x;
typeof bar; // "undefined"
typeof x; // "undefined"
当变量在作用域中还没有声明,我们有时会当它是undeclared:
var foo;
foo; //"undefined"
bar; //Uncaught ReferenceError: bar is not defined
浏览器对一个没有声明的变量抛出bar is not defined,我们就会理解为undefined,如果浏览器能抛出bar is not found可能更准确。
更让人意外的是typeof处理未声明的变量,例如:
var foo;
typeof foo;// "undefined"
typeof bar; // "undefined"
对于一个未声明的变量(bar),typeof返回"undefined"。但这次执行typeof bar浏览器没有抛出异常,这是因为typeof有一个特殊的安全防范机制。
这个安全机制能帮助我们在运行JavaScript代码的时候避免一些异常情况的发生。对于不存在的变量及时做出处理。
例如有一个全局变量global在common.js中,如果我们不小心没有加载就会导致使用异常。
if(global){
// ...其他代码
}
if(typeof global !== "undefined"){
// ...其他代码
}
JavaScript中的“值”
数字
JavaScript中的数字类型是基于IEEE754标准来实现的,该标准通常也被称为“浮点数”。JavaScript使用的是“双精 度”格式(即 64 位二进制)。
语法
默认情况下大部分数字都以十进制显示,小数部分最后面的 0 被省略。
var a = 11;
var b = 11.1;
var c = 0.11;
var d = .11;
var e = 11.0;
var f = 11.;
特别大和特别小的数字默认用指数格式显示,与toExponential()函数的输出结果相同。
var a = 9e11;
console.log(a); //900000000000
a.toExponential(); //"9e+11"
var b = 1 / a;
console.log(b); //1.1111111111111112e-12
由于数字值可以使用 Number 对象进行封装,因此数字值可以调用 Number.prototype 中的方法。

var a = 11.11;
a.toFixed(1);
console.log(a); //11.1
a.toPrecision(2);
console.log(a); //"11"
上面的方法不仅适用于数字变量,也适用于数字常量。不过对于.运算符需要给予特别注 意,因为它是一个有效的数字字符,会被优先识别为数字常量的一部分,然后才是对象属 性访问运算符。
11.toFixed(2); //Uncaught SyntaxError: Invalid or unexpected token
(11).toFixed(2); // "11.00"
0.11.toFixed(2); // "0.11"
11..toFixed(2); // "11.00"
11 .toFixed(2); // "11.00"
11.toFixed(2)抛出异常时因为.被视为与常量是一个整体11.所以没有.属性访问运算符来调用tofixed方法。11..toFixed(2)则没有问题。
数值问题
由于JavaScript遵循IEEE 754规范的语言,所以会出现如下情况:
0.1 + 0.2 = 0.30000000000000004
0.1 + 0.2 === 0.3; //false
从数学的角度,上面的结果应该为0.3。二进制浮点数中的0.1和0.2并不是十分精确,它们相加的结果并非刚好等于0.3,而是一个比较接近的数字0.30000000000000004,所以条件判断结果为false。
JavaScript在处理带有小数的数字时需要特别注意。最常见的方法是设置一个误差范围值,通常称为“机器精度”,对JavaScript的数字来说,这个值通常是2^-52(2.220446049250313e-16)。
function numberEqual(x, y){
return Math.abs(x - y) < Math.pow(2,-52)
};
var foo = 0.1 + 0.2;
var bar = 0.3;
numberEqual(foo, bar);
在新的语法ES6中Number.EPSILON可以取代Math.pow(2,-52)。
整数安全范围
JavaScript中最大的整数2^53-1即9007199254740991,ES6使用Number.MAX_SAFE_INTEGER,最小则是-9007199254740991,ES6使用Number. MIN_SAFE_INTEGER。
整数检测
检测一个值是否是整数,如下:
function isInteger(num) {
return typeof num == "number" && num % 1 == 0;
};
//ES6中的Number.isInteger(..)
isInteger(12231);
Number.isInteger(123111);
检测一个值是否是安全的整数,如下:
function isInteger(num) {
return typeof num == "number" && num % 1 == 0;
};
function isSafeInteger(num){
return isInteger(num) && Math.abs(num) <= (Math.pow(2,53) - 1);
};
isSafeInteger(Math.pow(2, 53 )); //false
//ES6中的Number.isSafeInteger(..)
Number.isSafeInteger(Math.pow(2, 53 ) - 1); //true
字符串
JavaScript中的字符串由零个或多个16位Unicode字符组成的字符序列。
var foo = "foo";
String拥有丰富的API,也是日常开发我们使用最多的一种类型。

字符串字面量
| 字面量 | 含义 |
|---|---|
| \0 | 空字符 |
| \' | 单引号 |
| \" | 双引号 |
| \\ | 反斜杠 |
| \n | 换行 |
| \r | 回车 |
| \v | 垂直制表符 |
| \t | 水平制表符 |
| \b | 退格 |
| \f | 换页 |
| \uXXXX | unicode 码 |
这些字符字面量可以出现在字符串任意位置,例如:
var foo = "hello world /n/r";
字符串转换为数组
var foo = "abcdefg";
var arr = foo.split(""); //["a", "b", "c", "d", "e", "f", "g"]
其他类型转换为字符串
var a = 123;
a.toString(); // "123"
var b = [1,2,3];
b.join(''); //"123"
var c = true;
c.toString(); //"true"
在不知道转换的值是不是null或undefined的情况下,还可以使用String():
- 如果值有
toString()方法,则调用改方法 - 如果值是
null,则返回"null" - 如果值是
undefined,则返回"undefined"
var v1 = 123123;
var v2 = true;
var v3 = null;
var v4;
console.log(v1); //"123123"
console.log(v2); //"true"
console.log(v3); //"null"
console.log(v4); //"undefined"
布尔值
布尔值只有2种值:true和false。
Boolean类型的true和false区分大小写。
我们还可以使用Boolean()来进行类型转换。
var foo = "hello";
Boolean(foo); //true
其他类型转换成布尔值
| 数据类型 | 转换为true | 转换为false |
|---|---|---|
| String | 任何非空字符串 | ""(空字符串) |
| Number | 任何非零数值 | 0和NaN |
| Object | 任何对象 | null |
| Undefined | undefined |
特殊值
undefined和null
undefined类型只有一个值,即undefined。null类型也只有一个值,即null。它们的名
称既是类型也是值。
undefined和null通常用来表示“空”或“不是值”。二者有一点细微的差别。
null指空值(曾赋过值,但是目前没有值)undefined指没有值(从未赋值)
在非严格模式下,我们可以为全局标识符undefined赋值:
undefined = 2;
undefined是一个内置标识符,它的值为undefined, 通过void运算符即可得到该值。
void并不改变表达式的结果,只是让表达式不返回值
var foo = "hello";
console.log(void foo); //undefined
特殊数字
- 不是数字
如果数学运算的操作数不是数字类型(或者无法解析为常规的十进制或十六进制数字), 就无法返回一个有效的数字,这种情况下返回值为 NaN。
var a = 2019 / "hello"; //NaN
typeof a === "number"; //true
结果出人意料,a的值是NaN,但是a的类型还是number,NaN是一个特殊值,用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”。
如何检测一个变量的值是否为NaN,判断有些特殊,例如:
var b = 123 / "abc";
a == NaN; // false
a === NaN; // false
NaN === NaN; // false
NaN != NaN; //true
isNaN(NaN); //true
可以看出NaN,它和自身不相等,而NaN!=NaN为true,可以使用全局(window)工具函数isNaN(..) 来判断一个值是否是NaN。但是这个函数有一个严重的缺陷,例如:
var a = 11 / "ab";
var b = "foo";
window.isNaN(a); //true
window.isNaN(b); //true
很明显"foo"不是一个数字,但是它也不是NaN。ES6我们可以使用工具函数Number.isNaN(..)。
var a = 11 / "ab";
var b = "foo";
Number.isNaN(a); //true
Number.isNaN(b); //false
ES6 之前的浏览器的 polyfill 如下:
if (!Number.isNaN) {
Number.isNaN = function (n) {
return (
typeof n === "number" &&
window.isNaN(n)
);
};
}
if (!Number.isNaN) {
Number.isNaN = function (n) {
return n !== n;
};
}
- 无穷数
在JavaScript,有一些计算结果出乎意料。
var a = 123 / 0; //Infinity
var b = -123 / 0; //-Infinity
ES规范规定:Division of a nonzero finite value by a zero results in a signed infinity. The sign is determined by the rule already stated above.(将零非零有限值除以零会产生有符号无穷大)
由于JavaScript采用IEEE754所以和纯粹的数学运算不同,JavaScript的运算结果有可能溢出,此时结果为 Infinity 或者 -Infinity。例如:
var max = Number.MAX_VALUE; //1.7976931348623157e+308
var a = max + max; // Infinity
var b = Math.pow( 2, 970 ) + max; // Infinity
var c = Math.pow( 2, 969 ) + max; //1.7976931348623157e+308
这个结果也比较意外。规范规定,如果数学运算(如加法)的结果超出处理范围,则由IEEE 754规范中的“就近取整”模式来决定最后的结果。这里推荐一篇文章介绍了这里的细节(点击)
- 零
JavaScript有一个常规的 0(也叫作 +0)和一个 -0。加法和减法运算不会得到负零。
var a = 0 / 123; // 0
var b = 0 * -123; // -0
对负零进行字符串化会返回 "0":
var a = 0 / -123;
a.toString(); // "0"
a + ""; // "0"
String(a); // "0"
如果反过来将其从字符串转换为数字,得到的结果是准确的:
+"-0"; // -0
Number("-0"); // -0
JSON.parse("-0"); // -0
负零转换为字符串的结果令人费解:
var a = 0;
var b = 0 / -123;
a == b; //true
-0 == 0; //true
a === b; //true
-0 === 0; //true
0 > -0; //false
a > b; //false
区分 -0 和 0:
function isPositiveZero(n) {
n = Number(n);
return (n === 0) && (1 / n === Infinity);
}
isPositiveZero(-0) //false
isPositiveZero(0) //true