今天看的是这个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方法完全一致