# 类型
## 内置类型
- null
- undefined
- boolean
- number
- string
- object
- symbol
1
| typeof null === "object":
|
历史遗留问题,用复合条件来检测null值的类型:
1 2
| var a = null; (!a && typeof a === "object");
|
变量是没有类型的,只有值才有,对变量执行typeof操作时,得到的结果是该变量持有的值的类型
已在作用域中声明但还没有赋值的变量,是undefined的。相反,还没有在作用域中声明过的变量是undeclared的。
1 2 3 4 5
| var a;
typeof a;
typeof b;
|
typeof undeclared
1 2 3 4 5 6 7 8 9 10 11 12
| if(DEBUG) { console.log("Debugging is staring"); }
if(typeof DEBUG !== "undefined") { console.log("Debugging is starting"); }
<!--more-->
|
在没有全局变量可用的时候,使用typeof的安全防范机制做检查;或是使用“依赖注入”(dependency injection)设计模式将参数显式地传递到函数中
1 2 3 4 5
| function doSomethingCool(Feature) { var helper = Feature || function () { }; var val = helper(); }
|
小结
- 变量没有类型,但它们持有的值有类型。类型定义了值的行为特征。
- undeclared表示变量还没有被声明过
- 访问undeclared变量时会报错”ReferenceError: a is not defined”,并且typeof因为安全防范机制,对于undeclared、undefined都返回undefined
值
数组
使用delete运算符可以将单元从数组中删除,但是单元删除后,数组的length属性不会发生变化。
创建“稀疏”数组时,要注意其中的空白单元”empty slot”
数组通过数字进行索引,又去的是他们也是对象,所以也可以包含字符串键值和属性,但是不计算在数组长度内。
1 2 3 4 5 6 7 8
| var a = [];
a[0] = 1; a["foobar"] = 2;
a.length; a["length"]; a.foobar;
|
特别注意如果字符串键值能够被强制类型转换为十进制的数字,它会被当作数字索引来处理
所以建议使用对象来存放键值/属性值,用数组来存放数字索引值。
类数组
有时候需要将类数组(一组通过数组索引的值)转换为真正的数组,这一般是通过数组工具函数实现
工具函数slice(..)经常被用于这一转换
1 2 3 4 5
| function foo() { var arr = Array.prototype.slice.call( arguments ); arr.push( "bam" ); console.log( arr ); }
|
用ES6中的内置工具函数Array.from(..)可以实现同样的功能
字符串
字符串和数组很相似,都是类数组,都有length属性以及indexOf(..)和concat(..)方法
1 2
| var a = "foo"; var b = ["f", "o", "o"];
|
字符串不是字符数组,字符串是不可改变的,而数组是可以改变的。
1 2 3 4 5 6
| a[1] = "0"; b[1] = "0";
a; b;
|
字符串不可变是指字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串。而数组的成员函数都是在其原始值上进行操作。
1 2 3 4 5 6 7
| c = a.toUpperCase(); a === c; a; c;
b.push( "!" ); b;
|
许多数组函数用来处理字符串很方便,虽然字符串没有这些函数,但可以通过“借用”数组的非变更方法来处理字符串
1 2 3 4 5 6 7 8 9 10
| a.join; a.map;
var c = Array.prototype.join.call(a, "-"); var d = Array.prototype.map.call(a, function(v) { return v.toUpperCase() + "."; }).join( "" );
c; d;
|
字符串反转
1 2 3 4 5 6
| Array.prototype.reverse.call(a);
var c = a.split("").reverse().join(""); c;
|
数字
.
运算符是一个有效的数字字符,回被优先识别为数字常量的一部分,然后才是对象属性访问运算符。
1 2 3 4 5
| 42.toFixed(3);
(42).toFixed(3); 0.42.toFixed(3); 42..toFixed(3);
|
0.1+0.2 != 0.3问题
最常见的方法是设置一个误差范围值,通常称为机器精度(machine epsilon)
对于JS的数字来说,这个值通常是2^-52
ES6中内置了这个数字Number.EPSILON,或者写成Math.pow(2, -52);
1 2 3 4
| if (!Number.EPSILON) { Number.EPSILON = Math.pow(2, -52); }
|
于是我们可以解决0.1+0.2 != 0.3这个问题了
1 2 3 4 5 6
| function numbersCloseEnoughToEqual(n1, n2) { return Math.abs(n1 - n2) < Number.EPSILON; } var a = 0.1 + 0.2; var b = 0.3; numbersCloseEnoughToEqual(a, b);
|
整数的安全范围
数字的呈现方式导致整数的安全范围远小于Number.MAX_VALUE (1.7976931348623157e+308)
能够被安全呈现的最大整数是2^53 - 1(去除1符号位,11位指数,还有52位) Number.MAX_SAFE_INTEGER (9007199254740991)
整数检测
ES6 中 Number.isInteger(..)
1 2 3
| Number.isInteger(42); Number.isInteger(42.000); Number.isInteger(42.3);
|
polyfill方法
1 2 3 4 5
| if (!Number.isInteger) { Number.isInteger = function(num) { return typeof num == "number" && num % 1 == 0; } }
|
32位有符号整数
虽然整数最大能够达到53位,但是有些数字操作(如数位操作)只适用于32位数字,所以在这些操作中数字的安全范围就回从
Math.pow(-2,31)~Math.pow(2,31)
-2147483648 ~ 2147483648
如a | 0
可以将变量a中的数值转换为32位有符号整数,因为数位操作符|
只适用于32位整数。因此与0进行操作可以截取a中的32位数位。
某些特殊的值并不是32位安全范围的,比如NaN和Infinity
特殊数值
undefined
在非严格模式下,可以为全局标识符undefined赋值,undefined是一个内部标识符,它的值为undefined(可以被改变)
通过void运算符可以获得该值
void并不改变表达式的结果,只是让表达式不返回值。
一些应用
1 2 3 4 5
| function doSomething() { if (!APP.ready) { return void setTimeout( doSomething, 100); } }
|
一般会分开操作,效果都一样。
1 2 3 4 5 6
| function doSomething() { if (!APP.ready) { setTimeout( doSomething, 100); return; } }
|
未完待续…