1. let 和 const 命令
在ES6之前,JavaScript 声明变量只能用 var
这个关键字;而在ES6中,则引入了其他两个关键字 let
和 const
,那么,它们俩,到底与 var
有哪些异同呢?请看下表:
名称 |
作用 |
是否存在变量提升 |
是否存在块级作用域 |
是否能重复声明 |
var |
变量声明 |
是 |
否 |
能 |
let |
变量声明 |
否 |
是 |
不能 |
const |
常量声明 |
否 |
是 |
不能 |
2. 变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
数据类型 |
常见解构形式 |
描述 |
数组 |
let [a, b, c] = [1, 2, 3]; |
按照对应位置,对变量赋值 |
对象 |
let { foo, bar } = { foo: 'aaa', bar: 'bbb' }; |
变量必须与属性同名 |
字符串 |
const [a, b, c, d, e] = 'hello'; |
字符串被转成类数组对象 |
数值 |
let {toString: s} = 123; |
解构前转为对象 |
布尔值 |
let {toString: s} = true; |
解构前转为对象 |
函数参数 |
let add = ([x, y]) => x + y; |
类似数组解构 |
函数参数 |
let sub = ({x, y}) => x - y; |
类似对象解构 |
3. 字符串扩展
3.1 模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识。
// es5
var name = 'Checkson';
var str = 'Hello ' + name + '!';
// es6
const name = 'Checkson';
const str = `Hello ${Checkson}!`;
3.2 includes() 实例方法
返回布尔值,表示是否找到了参数字符串(接受第二参数,代表开始搜索下标)。
// es5
var str = 'This is just test!';
if (str.indexOf('is') > -1) {
// do something...
}
// es6
const str = 'This is just test!';
if (str.includes('is')) {
// do something...
}
3.3 startsWith(), endsWith() 实例方法
两者都返回布尔值,前者表示参数字符串是否在原字符串的头部,后者表示参数字符串是否在原字符串的尾部(都接受第二参数,代表开始搜索下标)。
// es5
var str = 'Learn Once, Write Anywhere';
if (/^Learn/.test(str) || /Anywhere$/.test(str)) {
// do something...
}
// es6
const str = 'Learn Once, Write Anywhere';
if (str.startsWith('Learn') || str.endsWith('Anywhere')) {
// do something...
}
3.4 repeat() 实例方法
repeat方法返回一个新字符串,表示将原字符串重复n次,n为非负整数。
// es5
var str = '';
for (var i = 0; i < 6; i++) {
str += 'template';
}
// es6
var str = 'template'.repeat(6);
3.5 padStart(), padEnd() 实例方法
ES2017 新增了 padStart()
,padEnd()
实例方法用来分别为字符串首、尾补全指定的字符。
// padStart
// es5
function prevZero (num) {
return ('0' + num).substr(-2);
}
// es6
function prevZero (num) {
return ('' + num).padStart(2, '0');
}
// padEnd 同理
3.6 trimStart(), trimEnd() 实例方法
ES2019 新增了 trimStart()
和 trimEnd()
实例方法。它们的行为与trim()
一致,前者消除字符串头部的空格,后者消除尾部的空格。
// trimStart
// es5
function trimStartSpace (str) {
var res = '', isFirst = true ;
for (var i = 0, len = str.length; i < len; i++) {
if (isFirst && str[i] === ' ') {
continue;
}
isFirst && (isFirst = !isFirst);
res += str[i];
}
return res;
}
// es6
function trimStartSpace (str) {
return str.trimStart();
}
// trimEnd 同理
4. 正则的扩展
字符串对象共有 4 个方法,可以使用正则表达式:match()
、replace()
、search()
和split()
。ES6 将这 4 个方法,在语言内部全部调用RegExp
的实例方法,从而做到所有与正则相关的方法,全都定义在RegExp
对象上。
5. 数值的扩展
5.1 二进制和八进制表示法
ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b
(或0B
)和0o
(或0O
)表示。
// 二进制
0b1000 === 8 // true
// 八进制
0o10 === 8 // true
// 转成十进制
Number(0b1000); // 8
Number(0o10); // 8
5.2 Number.isFinite(), Number.isNaN()
ES6在Number对象上,新提供了Number.isFinite()
和Number.isNaN()
两个方法。前者检查传入值是否有限,后者检查传入值是否为NaN。
// es5
isFinite(25) // true
isFinite("25") // true
isFinite(true) // true
// es6
Number.isFinite(25) // true
Number.isFinite("25") // false
Number.isFinite(true) // false
// es5
isNaN(NaN) // true
isNaN("NaN") // true
// es6
Number.isNaN(NaN) // true
Number.isNaN("NaN") // false
Number.isNaN(1) // false
它们与传统的全局方法isFinite()
和isNaN()
的区别在于,传统方法先调用Number()
将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,Number.isFinite()
对于非数值一律返回false
, Number.isNaN()
只有对于NaN
才返回true
,非NaN
一律返回false
。
5.3 Number.parseInt(), Number.parseFloat()
ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。
Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true
5.4 指数运算符
ES2016 新增了一个指数运算符(**
)。
// es5
Math.pow(2, 2); // 4
Math.pow(2, 3); // 8
// es6
2 ** 2 // 4
2 ** 3 // 8
6. 函数的扩展
6.1 函数参数的默认值
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
// es5
function foo (x, y) {
y = y || 'World';
return x + y;
}
// es6
function foo (x, y = 'World') {
return x + y;
}
6.2 解构 + 函数参数默认值
// es5
function foo (obj) {
obj.y = obj.y || 2;
return obj.x + obj.y;
}
// es6
function foo ({x, y = 2}) {
return x + y;
}
6.3 rest 参数
ES6 引入 rest 参数(形式为...
变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。
// es5
function add () {
var args = [].slice.call(arguments);
var sum = 0;
args.forEach(function (val) {
sum += val;
});
return sum;
}
// es6
function add(...args) {
let sum = 0;
for (let val of args) {
sum += val;
}
return sum;
}
6.4 箭头函数
ES6 允许使用“箭头”(=>
)定义函数。
// es5
var foo = function () {
return false;
}
// es6
const foo = () => false;
注意: 对于箭头函数,函数体内的this
对象,就是定义时所在的对象,而不是使用时所在的对象;不可以当作构造函数(不能被new
);不可以使用arguments
对象;不可以使用yield
命令。
6.5 尾递归
函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
// 非尾递归 - 容易栈溢出
function factorial (n) {
if (n === 1) return 1;
return n * factorial(n - 1);
}
// 尾递归
function factorial (n, total) {
if (n === 1) return total;
return factorial(n - 1, total * n);
}
7. 数组的扩展
7.1 Array.from()
Array.from
方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// es5
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// es6
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
7.2 Array.of()
Array.of
方法用于将一组值,转换为数组。
// es5
var arr = [];
[].push.call(arr, 1, 2, 3);
// es6
const arr = Array.of(1, 2, 3);
7.3 find() 实例方法
数组实例的find方法,用于找出第一个符合条件的数组成员。若果找到符合条件的成员,则返回第一个满足条件的成员;如果找不到满足条件的成员,返回undefined
。
var arr = [1, 2, 3, 4], res = 'undefine';
// es5
for (var i = 0; i < arr.length; i++) {
if (arr[i] > 2) {
res = arr[i]; // 3
break;
}
}
// es6
res = arr.find(item => item > 2); // 3
7.4 findIndex() 实例方法
findIndex
方法与 indexOf
方法作用类似,但是findIndex
能识别数组是否存在NaN
。
// es5
[1, NaN, 2, NaN].indexOf(NaN); // -1
// es6
[1, NaN, 2, NaN].findIndex(item => isNaN(item)); // 1
7.5 fill() 实例方法
fill方法使用给定值,填充一个数组。
// es5
new Array(6, 6, 6); // [6, 6, 6]
// es6
new Array(3).fill(6); // [6, 6, 6]
7.6 includes() 实例方法
ES2016引入Array.prototype.includes
方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes
方法类似。
// es5
[1, 2, 3].indexOf(2) > -1 // true
// es6
[1, 2, 3].includes(2); // true
[1, 2, NaN].includes(NaN); // true
7.7 flat 实例方法
数组的成员有时还是数组,Array.prototype.flat()
用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。
// es5
function flatten(arr) {
return arr.reduce((a, b) => {
return a.concat(Array.isArray(b) ? flatten(b) : b);
}, []);
};
flatten([1, 2, [3, 4]]); // [1, 2, 3, 4]
// es6
[1, 2, [3, 4]].flat(); // [1, 2, 3, 4]
8. 对象的扩展
8.1 属性的简洁表示法
ES6 允许直接写入变量和函数,作为对象的属性和方法。
// es5
function foo (x, y) {
return { x: x, y: x };
}
// es6
function foo (x, y) {
return {x, y};
}
8.2 属性名表达式
ES6 允许字面量定义对象时,表达式作为对象的属性名,即把表达式放在方括号内。
// es5
var obj = {
foo: 1
};
obj['b' + 'ar'] = 2;
// es6
var obj = {
foo: 1,
['b' + 'ar']: 2
}
8.3 属性遍历方法
ES6 一共有 5 种方法可以遍历对象的属性。
(1)for...in
for...in
循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
(2)Object.keys(obj)
Object.keys
返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames
返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols
返回一个数组,包含对象自身的所有 Symbol 属性的键名。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys
返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。
8.4 扩展运算符
对象的扩展运算符(...
)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
var foo = { a: 1 };
var bar = { b: 2, c: 3 };
// es5
var baz = {};
for (var k in foo) baz[k] = foo[k];
for (var k in bar) baz[k] = bar[k];
// es6
var baz = {
...foo,
...bar
}
8.5 Object.is()
Object.is()
方法用来判断两值是否一样。
// es5
-0 === +0 // true
NaN === NaN // false
// es6
Object.is(-0, +0); // false
Object.is(NaN, NaN); // true;
8.6 Object.assign()
Object.assign
方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
var foo = { a: 1 };
var bar = { b: 2 };
var baz = { c: 3 };
// es5 - 对象合并
for (var prop in bar) foo[prop] = bar[prop];
for (var prop in baz) foo[prop] = baz[prop];
console.log(foo); // { a: 1, b: 2, c: 3 }
// es6 - 对象合并
Object.assign(foo, bar, baz);
console.log(foo); // { a: 1, b: 2, c: 3};
8.7 Object.values()
Object.values
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
var obj = { foo: 'bar', baz: 42 };
// es5
var arr = [];
for (var prop in obj) arr.push(obj[prop]); // ['bar', 42]
// es6
Object.values(obj); // ['bar', 42]
8.8 Object. entries()
Object.entries()
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
var obj = { foo: 'bar', baz: 42 };
// es5
var arr = [];
for (var prop in obj) arr.push([prop, obj[prop]]); // [["foo", "bar"], ["baz", 42]]
// es6
Object.entries(obj); // [["foo", "bar"], ["baz", 42]]
9. Symbol
ES6 引入了一种新的原始数据类型Symbol
,表示独一无二的值,可以保证不会与其他属性名产生冲突。
var x = Symbol();
var y = Symbol();
x === y // false
typeof x; // "Symbol"
9.1 可以为Symbol添加描述
const sym = Symbol('foo');
sym.description // foo
9.2 作为属性名的 Symbol
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
9.3 Symbol.for
该方法可以重用同一个 Symbol 值。
const s1 = Symbol.for('foo');
const s2 = Symbol.for('foo');
s1 === s2 // true
9.4 Symbol.keyFor
该方法返回一个已登记的 Symbol 类型值的key
。
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
10. Set 和 Map 数据解构
10.1 Set 定义
ES6 提供了新的数据结构 Set (集合)。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set
本身是一个构造函数,它可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。下面罗列几种常见的创建形式:
// 方式一
const set = new Set();
[2, 3, 3, 4, 5, 5, 6].forEach(x => set.add(x));
console.log(set); // Set(5) {2, 3, 4, 5, 6}
// 方式二
const set = new Set([2, 3, 3, 4, 5, 5, 6]);
console.log(set); // Set(5) {2, 3, 4, 5, 6}
// 方式三
const set = new Set(document.querySelectorAll('div'));
10.2 Set 实例的属性和方法
- Set 实例的属性:
Set.prototype.constructor
: 构造函数,默认是就是 Set
函数。
Set.prototype.size
: 返回 Set
实例的成员总数。
- Set 实例的方法:
add(value)
:添加某个值,返回 Set
结构本身。
delete(value)
: 删除某个值,返回一个布尔值,表示删除是否成功。
has(value)
: 返回一个布尔值,表示该值是否为 Set
的成员。
clear()
:清除所有成员,没有返回值。
var set = new Set();
set.add(1).add(2).add(2);
set.size // 2
set.has(1); // true
set.has(2); // true
set.has('2'); // false
set.has(3); // false
set.delete(2); // true
set.has(2); // false
set.clear();
set.size: // 0
10.3 Set 遍历
Set 结构的实例有四个遍历方法,可以用于遍历成员。
keys()
:返回键名的遍历器
values()
:返回键值的遍历器
entries()
:返回键值对的遍历器
forEach()
:使用回调函数遍历每个成员
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
10.4 Set 应用
[...new Set([1, 1, 2, 3, 3, 4, 4, 4, 5])]; // [1, 2, 3, 4, 5]
[...new Set('abababcd')].join(''); // abcd
- 实现并集(Union)、交集(Intersect)和差集(Difference)
let a = new Set([1, 2, 3]);
let b = new Set([2, 3, 4]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// Set {2, 3}
// 差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
10.5 Map 定义
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
var a = { foo: 1 }, b = {};
b[a] = 1;
console.log(b); // {[object Object]: 1} 自动将对象转化为字符串
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
Map作为构造函数,也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"
10.6 Map 实例属性和方法
- Map 属性:
Map.prototype.constructor
: 构造函数,默认是就是 Map
函数。
Map.prototype.size
: 返回 Map
实例的成员总数。
- Map 操作方法:
set(key, value)
方法设置键名key对应的键值为value,然后返回整个 Map 结构。
get(key)
方法读取 key
对应的键值,如果找不到 key
,返回 undefined
。
has(key)
方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
delete(key)
方法删除某个键,返回 true
。如果删除失败,返回 false
。
clear()
方法清除所有成员。
const map = new Map();
map.set('name', 'checkson');
map.get('name'); // checkson
map.set('age', 23).set('sex', 'male'); // 链式用法
map.has('name');
map.size; // 3
map.delete('age');
map.size; // 2
map.clear();
map.size; // 0
10.7 Map遍历
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
keys()
:返回键名的遍历器。
values()
:返回键值的遍历器。
entries()
:返回所有成员的遍历器。
forEach()
:遍历 Map 的所有成员。
需要特别注意的是,Map 的遍历顺序就是插入顺序。
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
参考图书
《ECMAScript 6 入门》