Number类型

JavaScript不区分整数和浮点数,统一用Number表示,以下是特殊的Number类型:

1
2
3
4
5
6
// 科学计数法表示1.2345x1000,等同于1234.5-99
1.2345e3;
// NaN表示Not a Number,当无法计算结果时用NaN表示
NaN;
// Infinity表示无限大,当数值超过了JavaScript的Number所能表示的最大值时,就表示为Infinity
Infinity;

关于判断相等

JavaScript有两种比较相等运算符:
第一种是==比较,它会自动转换数据类型再比较,很多时候,会得到非常诡异的结果;
第二种是===比较,它不会自动转换数据类型,如果数据类型不一致,返回false,如果一致,再比较。
由于JavaScript这个设计缺陷,不要使用==比较,始终坚持使用===比较。

另一个例外是NaN这个特殊的Number与所有其他值都不相等,包括它自己:NaN === NaN; // false,
唯一能判断NaN的方法是通过isNaN()函数:isNaN(NaN); // true.

最后要注意浮点数的相等比较:
1 / 3 === (1 - 2 / 3); // false
这不是JavaScript的设计缺陷。浮点数在运算过程中会产生误差,因为计算机无法精确表示无限循环小数。
要比较两个浮点数是否相等,只能计算它们之差的绝对值,看是否小于某个阈值:
Math.abs(1 / 3 - (1 - 2 / 3)) < 0.0000001; // true

null和undefined

null表示一个“空”的值,它和0以及空字符串’’不同,0是一个数值,’’表示长度为0的字符串,而null表示“空”。
在其他语言中,也有类似的表示,如Python中的None。

undefined,它表示“未定义”。
JavaScript的设计者希望用null表示一个空的值,而undefined表示值未定义。
事实证明,区分两者的意义不大。大多数情况下,我们都应该用null。
undefined仅仅在判断函数参数是否传递的情况下有用。

数组

数组是一组按顺序排列的集合,集合的每个值称为元素。JavaScript的数组可以包括任意数据类型。如:[1, 2, 3.14, 'Hello', null, true];
另一种创建数组的方法是通过Array()函数实现: new Array(1, 2, 3); // 创建了数组[1, 2, 3],
出于代码的可读性考虑,强烈建议直接使用[]。
注意:

  1. 直接给Array的length赋一个新的值会导致Array大小的变化
  2. 如果通过索引赋值时,索引超过了范围,同样会引起Array大小的变化:
    大多数其他编程语言不允许直接改变数组的大小,越界访问索引会报错。然而,JavaScript的Array却不会有任何错误。
    在编写代码时,不建议直接修改Array的大小,访问索引时要确保索引不会越界。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    var arr = [1, 2, 3];
    arr.length; // 3
    arr.length = 6;
    arr; // arr变为[1, 2, 3, undefined, undefined, undefined]
    arr.length = 2;
    arr; // arr变为[1, 2]
    var arr = [1, 2, 3];
    arr[5] = 'x';
    arr; // arr变为[1, 2, 3, undefined, undefined, 'x']

对象

JavaScript的对象是一组由键-值组成的无序集合,例如:

1
2
3
4
5
6
7
8
var person = {
name: 'Bob',
age: 20,
tags: ['js', 'web', 'mobile'],
city: 'Beijing',
hasCar: true,
zipcode: null
};

JavaScript对象的键都是字符串类型,值可以是任意数据类型。相当于python中的字典.

循环遍历(for … of/for … in)

Array可以采用下标循环,遍历Map和Set就无法使用下标.
为了统一集合类型,ES6标准引入了新的iterable类型,Array、Map和Set都属于iterable类型。
具有iterable类型的集合可以通过新的for … of循环来遍历。

for … of循环和for … in循环有何区别?
for … in循环由于历史遗留问题,它遍历的实际上是对象的属性名称。一个Array数组实际上也是一个对象,它的每个元素的索引被视为一个属性。
当我们手动给Array对象添加了额外的属性后,for … in循环将带来意想不到的意外效果:

1
2
3
4
5
var a = ['A', 'B', 'C'];
a.name = 'Hello';
for (var x in a) {
alert(x); // '0', '1', '2', 'name'
}

for … in循环将把name包括在内,但Array的length属性却不包括在内。

for … of循环则完全修复了这些问题,它只循环集合本身的元素.
然而,更好的方式是直接使用iterable内置的forEach方法,它接收一个函数,每次迭代就自动回调该函数。以Array为例:

1
2
3
4
5
6
7
var a = ['A', 'B', 'C'];
a.forEach(function (element, index, array) {
// element: 指向当前元素的值
// index: 指向当前索引
// array: 指向Array对象本身
alert(element);
});

set和Map

Set与Array类似,但Set没有索引,因此回调函数的前两个参数都是元素本身:

1
2
3
4
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {
alert(element);
});

Map的回调函数参数依次为value、key和map本身:

1
2
3
4
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {
alert(value);
});

如果对某些参数不感兴趣,由于JavaScript的函数调用不要求参数必须一致,因此可以忽略它们。例如,只需要获得Array的element:

1
2
3
4
var a = ['A', 'B', 'C'];
a.forEach(function (element) {
alert(element);
});

函数定义

1
2
3
4
5
6
7
function abs(x) {
if (x >= 0) {
return x;
} else {
return -x;
}
}

请注意,函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回.
如果没有return语句,函数执行完毕后也会返回结果,只是结果为undefined,
就像python中不写return,默认return None。

匿名函数(lambda表达式)

1
2
3
4
5
6
7
// 六种语言中的简单函数示例
function (a) { return a > 0; } // JS
[](int a) { return a > 0; } // C++
(lambda (a) (> a 0)) ;; Lisp
lambda a: a > 0 # Python
a => a > 0 // C#
a -> a > 0 // Java

关于arguments
JavaScript有一个关键字arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。
利用arguments,你可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值:

箭头函数(=>)

ES6中引入了一种编写函数的新语法, 允许使用“箭头”(=>)定义函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var f = v => v;
//上面的箭头函数等同于:
var f = function(v) {
return v;
};
//如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
// 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。
var sum = (num1, num2) => { return num1 + num2; }
// 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
// 报错
let getTempItem = id => { id: id, name: "Temp" };
// 不报错
let getTempItem = id => ({ id: id, name: "Temp" });

当需要只有一个参数的简单函数时,可以使用新标准中的箭头函数,它的语法:标识符=>表达式。
你无需输入function和return,一些小括号、大括号以及分号也可以省略。
如果要写一个接受多重参数(也可能没有参数,或者是不定参数、默认参数、参数解构)的函数,则需要用小括号包裹参数list。

小提示:当使用箭头函数创建普通对象时,你总是需要将对象包裹在小括号里。

普通function函数和箭头函数的行为有一个微妙的区别:
箭头函数没有它自己的this值,箭头函数内的this值继承自外围作用域。

箭头函数的一个用处是简化回调函数

箭头函数有几个使用注意点:
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
上面四点中,第一点尤其值得注意。this对象的指向是可变的,但是在箭头函数中,它是固定的。