变量没有类型,值才有。变量可以随时持有任何类型的值。
所有的值都有一个类型,可能是:
Undefined
null
Number
String
Boolean
Object
-
Symbol
除对象外,其他统称为基本类型,我们可以使用typeof来获取一个值的类型,但null比较特殊。
typeof null === 'object' // true
这是一个由来已久的bug,我们需要使用符合条件检测null。
var a = null; (!a && typeof a === 'object'); // true
另外一个比较特殊的是函数。
var a = () => {}
typeof a; // function
查阅规范就会知道,function是object的一个“子类型”。具体而言,函数是“可调用对象”,他有一个内部属性[[Call]],该属性使其可以被调用。
null和undefined
Undefined
变量在未持有值的时候为undefined。大多数开发者倾向于将undefined等同于undeclared(未申明),但在JavaScript中完全是两回事情。
已在作用域中声明但还没有赋值的变量是undefined,相反,还没有在作用域中声明过得变量是undeclared。
var a;
a; // undefined
b; // ReferenceError: b is not defined
对UNdeclared的变量执行typeof,会得到undefined,这让人抓狂,因为typeof有一个特殊的安全防范机制。这种处理使得我们比较容易判断当前环境是否已经引入了某一变量。
if(typeof abc === 'undefined') {
abc = function() {
// do some thing
}
}
判断a是否已经在当前环境中,使用typeof a === 'undefined', 而不是 if(a)
Null还是Undefined
- null指的是空值(empty value)
- undefined指的是没有值(missing value),从未赋值过
void 0
鉴于在非严格模式下,我们可以为全局标识符undefined赋值。可以看到一些人这样写代码:
var a = void 0
表达式void没有返回值,它获得一个纯粹的undefined。void并不改变表达式的结果,只是让表达式不返回值。
数组
- 在JavaScript中,数组可以容纳任何类型的值
- 对数组声明之后即可向其加入值,不需要预设大小。
- 使用delete运算符可以将单元从数组中删除,但是单元删除之后,数组的length属性并不会发生变化。
- 数据通过数字进行索引,但有趣的是数组也是对象,所以也可以给它添加字符串键值。但这些不计算在数组长度之内。
类数组转换为数组
例如一些DOM查询返回的DOM元素列表,或者函数的arguments对象可以通过以下方法转换为数组:
- concat()
- forEach()
- indexOf()
- slice()
- Array.from
字符串
字符串是不可变的,意味着对字符串的操作往往不会直接去修改已有字符串,而是生成新的字符串。
数字
JavaScript只有一种数值类型:number。JavaScript的数字根据IEEE 754标准实现的。
二进制浮点数最大的问题(所有遵循IEEE 754规范的语言),会出现如下情况:
0.1 + 0.2 === 0.3 // false
以上情况的原因在于二进制浮点数中的0.1和0.2并不是十分精确,它们相加的结果比较接近0.300000000000004,所以判断结果是false。
为了解决上面的问题,最常见的方法是设置一个误差范围值,通常为“机器精度(machine epsilon)”, 对于JavaScript的数字而言,这个值通常是 2^-52。从ES6开始这个值定义在 Number.EPSILON中。所以判断小数是否相等可以这样写:
Math.abs(n1 - n2) < Number.EPSILON;
检测一个数字是否是整数,可以使用:
Number.isInteger(33); // true
Number.isInteger(33.00); // true
Number.isInteger(42.3) // false
不是数字的数字
NaN意指“不是一个数字”,将她理解为“无效的数值”可能更准确。例如:
var a = 2 / 'foo';
typeof a === 'number'; // true
NaN是一个“禁戒值”, 用于指出数字类型中的错误情况,即“执行数学运算没有成功”。
检测NaN
NaN是唯一一个不和自身相等的值(非自反, reflexive,即 x === x 不成立)。
- 不要使用内建的全局工具函数 isNaN
window.isNaN(NaN) // true
window.isNaN('foo') // true
很明显'foo'不是NaN。
- ES6提供了一个工具函数: Number.isNaN().
Number.isNaN(NaN) // true
Number.isNaN('foo') // false
-
利用NaN不等于自身这一点,可以使用:
if(!Number.isNaN) { Number.isNaN = n => n !== n; }
如果你仍在代码里使用window.isNaN, 迟早会出现bug。