今天看的是这个ES6入门文档,很详细地讲解了ES6.未来再看阮一峰的ECMAScript教程作为补充吧
ECMAScript是JavaScript的规格,JavaScript是ECMAscript的实现。
部署:
使用node.js
Babel转码器: 广泛使用的ES6转码器,可将ES6转化为ES5,从而在现有环境中进行。
Babel配置文件是.babelrc
,用于设置转码规则和插件
命令行转码工具:babel-cli
babel-node
直接运行ES6代码
Traceur转码器,也可以将ES6转为ES5
let和const
let所声明的变量只在所在的代码块内有效。适合在for循环内使用,避免值覆盖的情况。
1 | var a=[]; |
let 不存在变量提升,因此一定要先声明变量才能使用。var存在变量提升。
暂时性死区
一旦区块中存在let和const变量,就一定要先声明再使用。凡是在声明之前使用这些变量,就会报错。哪怕已经有一个var声明的全局变量。
用let声明的变量,在声明之前都是该变量的死区,用到就会报错。
暂时性死区的本质:已进入当前作用域,要使用的 变量就已经存在,但是不可获取。只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
let不允许在同一作用域内重复声明变量。
块级作用域
外层代码块不受内层代码块的影响。
外层作用域无法读取内层作用域的变量。
内层作用域可以定义外层作用域的同名变量。
ES5中函数只能在顶层作用域和函数作用域中声明,不能在块级作用域声明。ES6允许在块级作用域中声明函数。
const
声明的变量不能改变值,const一旦声明变量,就必须立刻初始化,不能留到最后赋值。
const声明的变量只在块级作用域中有效。同样存在暂时性死区
对于复合类型,const指向的是数据所在的地址,只是保证该地址不变,数据可以改变。Object.freeze
可以将对象冻结。
解构赋值
var [a,b,c] = [1,2,3]
——属于模式匹配的写法。
可以从数组中提取值,按照对应位置,对变量赋值。
只要两边的模式相同,左边的变量就会赋予对应的值。
不想要的值可以用,
隔开,如var [, , third] = [1,2,3];third//3
把多余的变量赋值成为数组:let [a,..b] = [1,2,3,4];a//1;b//[2,3,4]
注意:...
只能用在最末尾,不能用在中间。
解构不成功:变量等于undefined
默认值
在左端定义默认值
1 | var [foo=true] = [] |
对象的解构赋值
变量必须与属性同名才能取到正确的值。
先找到同名属性,再赋值给变量
模式不会被赋值
对象的解构也可以使用默认值
字符串的解构赋值
const [a,b,c,d,e] = 'hello'
还可以对length解构赋值let {length :len} = 'hello';len//5
对象
右边的值不是对象的话,则先转为对象。
函数参数的解构赋值
建议只要有可能,就不要在模式中放置圆括号。
不能使用圆括号:
- 变量声明中不能使用圆括号
- 函数参数中不能使用圆括号
- 赋值语句中不能将整个模式或嵌套模式中的一层放入圆括号
可以使用圆括号——赋值语句中的非模式部分
解构变量的用途
交换变量的值
[x,y] = [y,x]
从函数返回多个值
1
2
3
4
5
6
7
8
9
10
11
12
13
14function example() {
return [1, 2, 3];
}
var [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
var { foo, bar } = example();函数参数的定义
提取JSON的数据
函数参数的默认值
遍历map结构
输入模块的指定方法
字符串的扩展
加强对Unicode的支持:只要将码点放入{}。就能正确解读Unicode
codePointAt() 返回一个字符的码点(十进制)
string.fromCodePoint() 从码点返回字符
遍历器接口
使字符串可以被for...of
循环遍历
方法
at()返回字符串的字节 ‘hello‘.at(0) //h
repeat()返回一个将源字符串重复n次的新字符串'hello'.repeat(n)
,参数不能是负数和Infinity。参数是0到1之间的小数:等同于0。NaN等同于0。参数是字符串:则先转为数字。
补全长度:
padStart(),padEnd().长度之和若超过指定的最小长度:截去超出位数的补全字符串。省略第二个参数:用空格代替。
1 | 'x'.padStart(5, 'ab') // 'ababx' |
模板字符串
用反引号标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。
所有的空格和缩进都会被保留在输出之中。
模板字符串中嵌入变量,需要将变量名写在${}
之中。
正则的扩展
如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
1 | new RegExp(/abc/ig, 'i').flags |
正则方法
match()
,replace()
,search()
,split()
u修饰符
点字符
对于码点大于
0xFFFF
的Unicode字符,点字符不能识别,必须加上u
修饰符。1
2
3
4var s = '𠮷';
/^.$/.test(s) // false
/^.$/u.test(s) // true上面代码表示,如果不添加
u
修饰符,正则表达式就会认为字符串为两个字符,从而匹配失败。ES6新增了使用大括号表示Unicode字符,这种表示法在正则表达式中必须加上
u
修饰符,才能识别。1
2
3/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true上面代码表示,如果不加
u
修饰符,正则表达式无法识别\u{61}
这种表示法,只会认为这匹配61个连续的u
。使用
u
修饰符后,所有量词都会正确识别码点大于0xFFFF
的Unicode字符。1
2
3
4/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{2}/u.test('𠮷𠮷') // true另外,只有在使用
u
修饰符的情况下,Unicode表达式当中的大括号才会被正确解读,否则会被解读为量词。u
修饰符也影响到预定义模式,能否正确识别码点大于0xFFFF
的Unicode字符。1
2/^\S$/.test('𠮷') // false
/^\S$/u.test('𠮷') // true上面代码的
\S
是预定义模式,匹配所有不是空格的字符。只有加了u
修饰符,它才能正确匹配码点大于0xFFFF
的Unicode字符。
y修饰符
y
修饰符,叫做“粘连”(sticky)修饰符。
y
修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。
属性
sticky属性:是否设置了y修饰符
flags属性:返回正则表达式的修饰符
数值的扩展
二进制:0b(0B)
八进制:0o(0O)
转为十进制:使用Number()
Number.isFinite()
用来检查一个数值是否为有限的(finite)。
Number.isNaN()
用来检查一个值是否为NaN
。
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
Number.isInteger()
用来判断一个值是否为整数。需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值。
Number.EPSILON
设置误差范围
Number.isSafeInteger()
则是用来判断一个整数是否落在整数范围之内(整数范围在-2^53
到2^53
之间)。
Math对象的扩展
Math.trunc() 去除小数部分,返回整数部分。对于非数值,则先转为数值。对于空值和无法截去小数的值:返回NaN。
Math.sign() 判断一个数是正数、负数还是零。
正数返回+1
负数返回-1
参数为0,返回0
参数为-0,返回-0
其他值:返回NaN
Math.cbrt()计算一个数的立方根。对于非数值,则先转为数值。
Math.clz32() 返回一个数的32位无符号整数形式有多少个前导0。对于小数,只考虑整数部分。
Math.imul() 返回两个数以32位带符号整数形式相乘的结果,返回的也是一个32位的带符号整数。
Math.fround方法返回一个数的单精度浮点数形式。
Math.hypot
方法返回所有参数的平方和的平方根。
Math.expm1(x)
返回ex - 1,即Math.exp(x) - 1
。
Math.log1p(x)
方法返回1 + x
的自然对数,即Math.log(1 + x)
。如果x
小于-1,返回NaN
。
Math.log10(x)
返回以10为底的x
的对数。如果x
小于0,则返回NaN。
Math.log2(x)
返回以2为底的x
的对数。如果x
小于0,则返回NaN。
三角函数方法。
Math.sinh(x)
返回x
的双曲正弦(hyperbolic sine)Math.cosh(x)
返回x
的双曲余弦(hyperbolic cosine)Math.tanh(x)
返回x
的双曲正切(hyperbolic tangent)Math.asinh(x)
返回x
的反双曲正弦(inverse hyperbolic sine)Math.acosh(x)
返回x
的反双曲余弦(inverse hyperbolic cosine)Math.atanh(x)
返回x
的反双曲正切(inverse hyperbolic tangent)
数组的扩展
Array.from
方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。Array.of
方法用于将一组值,转换为数组。数组实例的
copyWithin
方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。
1 | Array.prototype.copyWithin(target, start = 0, end = this.length) |
- 数组实例的
find
方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true
的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined
。
1 | [1, 4, -5, 10].find((n) => n < 0) |
上面代码找出数组中第一个小于0的成员。
1 | [1, 5, 10, 15].find(function(value, index, arr) { |
上面代码中,find
方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
数组实例的
findIndex
方法的用法与find
方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
。fill
方法使用给定值,填充一个数组。数组中已有的元素,会被全部抹去。entries()
,keys()
和values()
——用于遍历数组。它们都返回一个遍历器对象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"Array.prototype.includes
方法返回一个布尔值,表示某个数组是否包含给定的值
空位
- 空位
Array(3) // [, , ,]
ES6则是明确将空位转为undefined
。 Array.from
方法会将数组的空位,转为undefined
,也就是说,这个方法不会忽略空位。- 扩展运算符(
...
)也会将空位转为undefined
。 copyWithin()
会连空位一起拷贝。fill()
会将空位视为正常的数组位置。for...of
循环也会遍历空位。entries()
、keys()
、values()
、find()
和findIndex()
会将空位处理成undefined
。- 由于空位的处理规则非常不统一,所以建议避免出现空位。
函数的扩展
允许为函数的参数设定默认值,即直接写在函数参数的后面。
1 | function log(x, y = 'World') { |
参数变量是默认声明的,所以不能用let
或const
再次声明。
参数默认值可以与解构赋值的默认值,结合起来使用。
1 | function foo({x, y = 5}) { |
rest参数(…val):获取函数的多余参数.使用rest参数,可以向函数传入任意数目的参数。
扩展运算符(…)
将一个数组转为用逗号分隔的参数序列
取代apply方法:
Math.max(...[14,3,7])
等同于Math.max(14,3,7)
通过push函数,将数组添加到另一个数组的尾部。
扩展运算符的应用:
- 合并数组
[...arr1, ...arr2, ...arr3]
- 与解构赋值结合
[a, ...rest] = list
- 函数的多个返回值
- 将字符串转为真正的数组
[...'hello']
- 实现Iterator接口对象.任何Iterator接口的对象,都可以用扩展运算符转为真正的数组。
- Map和Set结构,Generator函数
name
箭头函数
1 | var f = v => v; |
上面的箭头函数等同于:
1 | var f = function(v) { |
如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
1 | var f = () => 5; |
如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return
语句返回。
1 | var sum = (num1, num2) => { return num1 + num2; } |
由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。
1 | var getTempItem = id => ({ id: id, name: "Temp" }); |
箭头函数与变量解构结合使用。
简化回调函数
1 | // 正常函数写法 |
数组排序:
1 | // 正常函数写法 |
箭头函数有几个使用注意点。
(1)函数体内的this
对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new
命令,否则会抛出一个错误。
(3)不可以使用arguments
对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
(4)不可以使用yield
命令,因此箭头函数不能用作Generator函数。
箭头函数绑定this
1 | foo::bar; |
尾调用
概念:某个函数的最后一步是调用另一个函数。
不一定出现在函数尾部,只要最后一步操作是调用即可。
1 | function f(x) { |
优化:
函数调用会形成调用记录——调用帧。所有的调用帧形成一个调用栈。
尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。
“尾调用优化”(Tail call optimization),即只保留内层函数的调用帧。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。
注意,只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”。
尾递归
尾调用自身,就成为尾递归
尾递归优化过的fibonacci 递归算法
1 | function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { |
由此可见,“尾调用优化”对递归操作意义重大,所以一些函数式编程语言将其写入了语言规格。ES6也是如此,第一次明确规定,所有ECMAScript的实现,都必须部署“尾调用优化”。这就是说,在ES6中,只要使用尾递归,就不会发生栈溢出,相对节省内存。
函数式编程有一个概念,叫做柯里化(currying),意思是将多参数的函数转换成单参数的形式。这里也可以使用柯里化。
1 | function currying(fn, n) { |
上面代码通过柯里化,将尾递归函数 tailFactorial 变为只接受1个参数的 factorial 。
蹦床函数(trampoline)可以将递归执行转为循环执行。
1 | function trampoline(f) { |
将原来的递归函数,改写为每一步返回另一个函数。
1 | function sum(x, y) { |
上面代码中,sum
函数的每次执行,都会返回自身的另一个版本。
现在,使用蹦床函数执行sum
,就不会发生调用栈溢出。
1 | trampoline(sum(1, 100000)) |
对象的扩展
允许直接写入变量和函数,作为对象的属性和方法。
1 | var foo = 'bar'; |
属性的赋值器(setter)和取值器(getter),事实上也是采用这种写法。
1 | var cart = { |
如果某个方法的值是一个Generator函数,前面需要加上星号。
1 | var obj = { |
- Object.is() 比较两个值是否严格相等
1 | Object.is('foo', 'foo') |
Object.assign
方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。1
2
3
4
5
6
7var target = { a: 1 };
var source1 = { b: 2 };
var source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}Object.assign
方法的第一个参数是目标对象,后面的参数都是源对象实行的是浅拷贝,而不是深拷贝。
常见用途:
- 为对象添加属性
- 为对象添加方法
- 克隆对象
- 合并多个对象
- 为属性指定默认值
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或字符串,也不管是否可枚举。
以上的5种方法遍历对象的属性,都遵守同样的属性遍历的次序规则。
- 首先遍历所有属性名为数值的属性,按照数字排序。
- 其次遍历所有属性名为字符串的属性,按照生成时间排序。
- 最后遍历所有属性名为Symbol值的属性,按照生成时间排序。
方法
__proto__
属性,Object.setPrototypeOf(),Object.getPrototypeOf()__proto__
属性(前后各两个下划线),用来读取或设置当前对象的prototype
对象。Object.setPrototypeOf
方法的作用与__proto__
相同,用来设置一个对象的prototype
对象。它是ES6正式推荐的设置原型对象的方法。Object.getPrototypeOf
与setPrototypeOf方法配套,用于读取一个对象的prototype对象。
Object.keys
方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。Object.values
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。Object.create
方法的第二个参数添加的对象属性(属性p
),如果不显式声明,默认是不可遍历的。Object.values
不会返回这个属性。Object.entries
方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。Object.getOwnPropertyDescriptors
方法返回一个对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象。
Symbol
symbol 表示独一无二的值。
let s = Symbol()
Symbol()
函数前不能使用new。因为生成的Symbol是一个原始类型的值,不是对象。因此Symbol值也不能添加属性。
Symbol不能与其他类型的值进行运算,会报错。但是可以显式转为字符串。也可以转换为布尔值,但是不能转化为数值。
由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖。
Symbol值作为对象属性名时,不能用点运算符。
Symbol还可以用于定义一组常量,保证这组常量的值都是不相等的。
常用的消除魔术字符串的方法,就是把它写成一个变量。
1 | var shapeType = { |
上面代码中,我们把“Triangle”写成shapeType
对象的triangle
属性,这样就消除了强耦合。
改用Symbol值。
1 | const shapeType = { |
上面代码中,除了将shapeType.triangle
的值设为一个Symbol,其他地方都不用修改。
Symbol 作为属性名,该属性不会出现在for...in
、for...of
循环中,也不会被Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols
方法,可以获取指定对象的所有 Symbol 属性名。
Object.getOwnPropertySymbols
方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
Symbol.for
方法接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。
Symbol.keyFor
方法返回一个已登记的 Symbol 类型值的key
。
内置的Symbol值
对象的Symbol.hasInstance
属性,指向一个内部方法。当其他对象使用instanceof
运算符,判断是否为该对象的实例时,会调用这个方法。
对象的Symbol.isConcatSpreadable
属性等于一个布尔值,表示该对象使用Array.prototype.concat()
时,是否可以展开。
对象的Symbol.species
属性,指向一个方法。该对象作为构造函数创造实例时,会调用这个方法。即如果this.constructor[Symbol.species]
存在,就会使用这个属性作为构造函数,来创造新的实例对象。
对象的Symbol.match
属性,指向一个函数。当执行str.match(myObject)
时,如果该属性存在,会调用它,返回该方法的返回值。
对象的Symbol.replace
属性,指向一个方法,当该对象被String.prototype.replace
方法调用时,会返回该方法的返回值。
对象的Symbol.search
属性,指向一个方法,当该对象被String.prototype.search
方法调用时,会返回该方法的返回值。
对象的Symbol.split
属性,指向一个方法,当该对象被String.prototype.split
方法调用时,会返回该方法的返回值。
对象的Symbol.iterator
属性,指向该对象的默认遍历器方法。
对象的Symbol.toPrimitive
属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。
对象的Symbol.toStringTag
属性,指向一个方法。在该对象上面调用Object.prototype.toString
方法时,如果这个属性存在,它的返回值会出现在toString
方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object]
或[object Array]
中object
后面的那个字符串。
对象的Symbol.unscopables
属性,指向一个对象。该对象指定了使用with
关键字时,哪些属性会被with
环境排除。
Set和Map数据结构
Set:类似于数组,但是每个值都是唯一的。
1 | var s = new Set(); |
Set的属性
Set.prototype.constructor
:构造函数,默认就是Set
函数。Set.prototype.size
:返回Set
实例的成员总数。
操作方法:(操作数据)
add(value)
:添加某个值,返回Set结构本身。delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。has(value)
:返回一个布尔值,表示该值是否为Set
的成员。clear()
:清除所有成员,没有返回值。
Array.from
方法可以将Set结构转为数组。
去除数组重复成员:
1 | function dedupe(array) { |
遍历方法:
keys()
:返回键名的遍历器values()
:返回键值的遍历器entries()
:返回键值对的遍历器forEach()
:使用回调函数遍历每个成员,没有返回值
需要特别指出的是,Set
的遍历顺序就是插入顺序。
应用:
扩展运算符和Set结构相结合,就可以去除数组的重复成员。
1 | let arr = [3, 5, 2, 2, 5, 5]; |
实现并集(Union)、交集(Intersect)和差集(Difference)。
1 | let a = new Set([1, 2, 3]); |
weakSet
WeakSet的成员只能是对象,而不能是其他类型的值。
WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。这个特点意味着,无法引用WeakSet的成员,因此WeakSet是不可遍历的。
WeakSet结构有以下三个方法。
- **WeakSet.prototype.add(value)**:向WeakSet实例添加一个新成员。
- **WeakSet.prototype.delete(value)**:清除WeakSet实例的指定成员。
- **WeakSet.prototype.has(value)**:返回一个布尔值,表示某个值是否在WeakSet实例之中。
Map
Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。
Map的属性
- size 返回Map结构的成员总数
- set(key,value)
set
方法设置key
所对应的键值,然后返回整个Map结构。如果key
已经有值,则键值会被更新,否则就新生成该键。 可以采用链式写法 - get(key)
get
方法读取key
对应的键值,如果找不到key
,返回undefined
。 - has(key)
has
方法返回一个布尔值,表示某个键是否在Map数据结构中。 - delete(key)
delete
方法删除某个键,返回true。如果删除失败,返回false。 - clear()
clear
方法清除所有成员,没有返回值。
Map 的方法
遍历方法:
keys()
:返回键名的遍历器。values()
:返回键值的遍历器。entries()
:返回所有成员的遍历器。forEach()
:遍历Map的所有成员。
(1)Map转为数组
前面已经提过,Map转为数组最方便的方法,就是使用扩展运算符(…)。
1 | let myMap = new Map().set(true, 7).set({foo: 3}, ['abc']); |
(2)数组转为Map
将数组转入Map构造函数,就可以转为Map。
1 | new Map([[true, 7], [{foo: 3}, ['abc']]]) |
(3)Map转为对象
如果所有Map的键都是字符串,它可以转为对象。
1 | function strMapToObj(strMap) { |
(4)对象转为Map
1 | function objToStrMap(obj) { |
(5)Map转为JSON
Map转为JSON要区分两种情况。一种情况是,Map的键名都是字符串,这时可以选择转为对象JSON。
1 | function strMapToJson(strMap) { |
另一种情况是,Map的键名有非字符串,这时可以选择转为数组JSON。
1 | function mapToArrayJson(map) { |
(6)JSON转为Map
JSON转为Map,正常情况下,所有键名都是字符串。
1 | function jsonToStrMap(jsonStr) { |
但是,有一种特殊情况,整个JSON就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为Map。这往往是数组转为JSON的逆操作。
1 | function jsonToMap(jsonStr) { |
Proxy和Reflect
Proxy用于修改某些操作的默认行为。属于一种元编程,即对编程语言进行编程。
Reflect
Iterator和for…of循环
Iterator 遍历器,为各种不同的数据结构提供统一的访问机制。
模拟next
方法返回值的例子。
1 | var it = makeIterator(['a', 'b']); |
在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、Set和Map结构。
1 | let arr = ['a', 'b', 'c']; |
调用Iterator接口的场合:
- 解构赋值
- 扩展运算符
- yield*
- 关于数组遍历的场合
for…of
for…of 和for…in 的区别
1 | let arr = [3, 5, 7]; |
Generator函数
Generator函数——异步编程解决方案。返回一个遍历器对象。
yield语句
yield语句是暂停的标志。
遍历器对象的next
方法的运行逻辑如下。
(1)遇到yield
语句,就暂停执行后面的操作,并将紧跟在yield
后面的那个表达式的值,作为返回的对象的value
属性值。
(2)下一次调用next
方法时,再继续往下执行,直到遇到下一个yield
语句。
(3)如果没有再遇到新的yield
语句,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值,作为返回的对象的value
属性值。
(4)如果该函数没有return
语句,则返回的对象的value
属性值为undefined
。
需要注意的是,yield
语句后面的表达式,只有当调用next
方法、内部指针指向该语句时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
Promise对象
promise是一种异步编程的解决方案。
promise保存着某个未来才会结束的事件(通常是异步操作)。
promise是一个对象,可以获得异步操作的信息。
特点:
- 对象的状态不受外界影响。 promise的三种状态:
Pending
(进行中)、Resolved
(已完成,又称Fulfilled)和Rejected
(已失败)。状态只有异步操作的结果才能决定。 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
Promise
对象的状态改变,只有两种可能:从Pending
变为Resolved
和从Pending
变为Rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。
缺点:
- 无法取消promise
- 如果不设置回调函数,
Promise
内部抛出的错误,不会反应到外部 - 当处于
Pending
状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
基本用法
promise实例
1 | var promise = new Promise(function(resolve, reject) { |
可以用then
方法分别指定Resolved
状态和Reject
状态的回调函数。
1 | promise.then(function(value) { |
Promise对象的简单例子。
1 | function timeout(ms) { |
promise.prototype.then() 为Promise实例添加状态改变时的回调函数
Promise.prototype.catch
方法是.then(null, rejection)
的别名,用于指定发生错误时的回调函数。
Promise.all
方法用于将多个Promise实例,包装成一个新的Promise实例。
Promise.race
方法同样是将多个Promise实例,包装成一个新的Promise实例。
Promise.resolve
方法将现有对象转为Promise对象
Promise.reject(reason)
方法也会返回一个新的Promise实例,该实例的状态为rejected
。它的参数用法与Promise.resolve
方法完全一致