Undefined && Null
Undefined表示未定义。但是因为Undefined是一个变量,不是一个关键字,所以为了避免无意中被篡改,很多时候建议用void 0 来获取undefined值。
Null表示定义了但是为空,null是JavaScript关键字。
String
String表示文本数据,它有最大长度2^53 - 1。不过字符串的最大长度,是受字符串的编码长度影响的,也就是字符串的UTF16编码,字符串的操作charAt、charCodeAt、length等方法针对的都是UTF16编码。
字符串一旦构造出来,无法用任何方式改变字符串的内容,也就是值类型的特征。
Number
JavaScript中有 +0 和 -0,在加法类运算中它们没有区别,但是除法的场合则需要特别留意区分,“忘记检测除以 -0,而得到负无穷大”的情况经常会导致错误。而区分 +0 和 -0 的方式,就是检测 1/x 是 Infinity 还是 -Infinity。
关于浮点数,非整数的 Number类型无法用 ==/=== 来比较,就比如
console.log(0.1 + 0.2 == 0.3)
输出的结果是false,说明两边不相等。这是浮点运算的特点,Number运算会先转换为二进制进行运算,而后转换为十进制。 0.1 转换后会变成无限循环小数,即浮点数运算的精度问题导致等式左右两边的结果并不是严格相等,而是相差了个微小的值。
但是这里错误的只是比较的方法,而不是结论。正确的比较方法是使用JavaScript提供的最小精度值:
console.log( Math.abs( 0.1 + 0.2 - 0.3) <= Number.EPSILON)
检查等式左右两边差的绝对值是否小于最小精度,才是正确的比较浮点数的方法。这段代码的结果就是true了。
Symbol
Symbol是ES6中引入的一个新类型,它是一切非字符串的对象key的集合。ES5的对象属性名都是字符串,字符串容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但是又想为这个对象添加新的方法,新方法的名字就有可能与已有方法产生冲突。Symbol提供了一种机制,保证每个属性的名字都是独一无二的,这样就能从根本上防止属性名的冲突。
Symbol有一个属性des,类型是字符串,用以描述Symbol,但是即使描述相同,Symbol也不相等。
创建Symbol的方式是使用全局的Symbol函数。
var mySymbol = Sym('mySymbol');
MDN中有一块众所周知的symbols,可以在全局的Symbol函数的属性中找到,这块也构成了语言的一类接口形式,他们允许编写与语言结合更紧密的API。
比如ES6为不同数据结构遍历新增了一个统一访问的接口 Iterator,为任何部署了 Iterator接口的数据结构提供了统一的访问机制。我们就可以使用Symbol.iterator来自定义for...of在对象上的行为:
var o = new Object
o[Symbol.iterator] = function() {
var v = 0
return {
next: function() {
return { value: v++, done: v > 10 }
}
}
};
for(var v of o)
console.log(v); // 0 1 2 3 ... 9
为什么给对象添加的方法可以用在基本类型上?
举个例子,Object.prototype.toString() 这个对象原型上的 toString() 返回一个表示该对象的字符串 “[object type]”。其中 type 是对象的类型,比如
var o = new Object(); o.toSSString(); // [object Object]
按道理,这个 toString() 只能用于对象类型的数据,但是其实其他数据类型也可以用,比如
123.toString()
这是因为.运算符发生了装箱转换。它使用new Number(123) 生成对象后调用 toString()。如果我们改变 toString() 中 this 的指向,那就可以识别任何数据类型了。
类型转换
StringToNumber
在不传入第二个参数的情况下,parseInt只支持16进制前缀 "0x",而且会忽略非数字字符,也不支持科学记数法。
在一个古老的浏览器环境中,parseInt还支持 0 开头的数字作为 8 进制前缀,这是很多错误的来源。所以,在任何环境下,都建议传入 parseInt 的第二个参数,而 parseFloat则直接把原字符串作为十进制来解析,它不会引入任何的其他进制。
多数情况下,Number 是比 parseInt 和 parseFloat 更好的选择。
装箱转换
所谓装箱转换,是把基本类型转换为对应的对象,它是类型转换中一种相当重要的种类。
在JavaScript中,没有任何方法可以更改私有的Class属性,因此 Object.prototype.toString 是可以准备识别对象对应的基本类型的方法,它比 instanceOf 更加准确。
拆箱转换
在 JavaScript标准中,规定了 ToPrimitive函数,它是对象类型到基本类型的转换。
对象到 String 和 Number的转换都遵循 “先拆箱再转换”的规则。
拆箱转换会尝试调用 valueOf 和 toString 来获得拆箱后的基本类型。如果 valueof 和 toString 都不存在,或者没有返回基本类型,则会产生类型错误 TypeError。
var o = {
valueOf: () => {console.log("valueof"); return {}},
toString: () => {console.log("toString"); return {}}
}
o * 2
// valueof
// toString
// TypeError
上面这个例子,我们定义了一个对象o,o有 valueOf 和 toString 两个方法,这两个方法都返回一个对象,然后我们进行 o*2 运算的时候,你会看见先执行了 valueOf(), 然后是 toString(),最后抛出一个 TypeError,这就说明这个拆箱转换失败了。
到String的拆箱转换会优先调用 toString。我们把刚才的运算从 o*2 转换成 String(o),那么你会看到调用顺序变了。
var o = {
valueOf : () => {console.log("valueOf"); return {}},
toString : () => {console.log("toString"); return {}}
}
String(o)
// toString
// valueOf
// TypeError