你不知道的JS中卷 笔记

# 类型

## 内置类型

  • null
  • undefined
  • boolean
  • number
  • string
  • object
  • symbol
1
typeof null === "object": // true

历史遗留问题,用复合条件来检测null值的类型:

1
2
var a = null;
(!a && typeof a === "object"); // true

变量是没有类型的,只有值才有,对变量执行typeof操作时,得到的结果是该变量持有的值的类型

已在作用域中声明但还没有赋值的变量,是undefined的。相反,还没有在作用域中声明过的变量是undeclared的。

1
2
3
4
5
var a;

typeof a; // undefined

typeof b; // undefined (其实应当返回undeclared, typeof的安全防范机制)

typeof undeclared

1
2
3
4
5
6
7
8
9
10
11
// 这样会抛出错误
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 () { /* .. default feature .. */ };
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; // 1
a["length"]; // 2
a.foobar; // 2

特别注意如果字符串键值能够被强制类型转换为十进制的数字,它会被当作数字索引来处理
所以建议使用对象来存放键值/属性值,用数组来存放数字索引值。

类数组

有时候需要将类数组(一组通过数组索引的值)转换为真正的数组,这一般是通过数组工具函数实现

工具函数slice(..)经常被用于这一转换

1
2
3
4
5
function foo() {
var arr = Array.prototype.slice.call( arguments ); // slice返回的是一个数组的副本
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"; // 应当使用a.charAt(1) 获取
b[1] = "0";


a; // "foo
b; // ["f", "o", "o"]

字符串不可变是指字符串的成员函数不会改变其原始值,而是创建并返回一个新的字符串。而数组的成员函数都是在其原始值上进行操作。

1
2
3
4
5
6
7
c = a.toUpperCase();
a === c; //false
a; // "foo"
c; // "FOO"

b.push( "!" );
b; // ["f", "o", "o", "!"]

许多数组函数用来处理字符串很方便,虽然字符串没有这些函数,但可以通过“借用”数组的非变更方法来处理字符串

1
2
3
4
5
6
7
8
9
10
a.join; // undefined
a.map; // undefined

var c = Array.prototype.join.call(a, "-");
var d = Array.prototype.map.call(a, function(v) {
return v.toUpperCase() + ".";
}).join( "" );

c; // "f-o-o"
d; // "F.O.O."

字符串反转

1
2
3
4
5
6
Array.prototype.reverse.call(a); // 返回值仍然是"foo"的一个封装对象

// other way

var c = a.split("").reverse().join("");
c; // oof

数字

.运算符是一个有效的数字字符,回被优先识别为数字常量的一部分,然后才是对象属性访问运算符。

1
2
3
4
5
42.toFixed(3); // SyntaxError

(42).toFixed(3); // "42.000"
0.42.toFixed(3); // "0.420"
42..toFixed(3); // "42.000"

0.1+0.2 != 0.3问题

最常见的方法是设置一个误差范围值,通常称为机器精度(machine epsilon)

对于JS的数字来说,这个值通常是2^-52

ES6中内置了这个数字Number.EPSILON,或者写成Math.pow(2, -52);

1
2
3
4
//polyfill
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); // true

整数的安全范围

数字的呈现方式导致整数的安全范围远小于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); // true
Number.isInteger(42.000); // true
Number.isInteger(42.3); // false

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); // 不返回timeId而是返回undefined
}
}

一般会分开操作,效果都一样。

1
2
3
4
5
6
function doSomething() {
if (!APP.ready) {
setTimeout( doSomething, 100); // 不返回timeId而是返回undefined
return;
}
}

未完待续…