ES6提供了默认参数值机制,允许你为参数设置默认值,防止在函数被调用时没有传入这些参数。
在下面的例子中,我们写了一个required()函数作为参数a和b的默认值。这意味着如果a或b其中有一个参数没有在调用时传值,会默认required()函数,然后抛出错误。
`const required = () => {``throw` `new` `Error(``'Missing parameter'``)};`
`const add = (a = required(), b = required()) => a + b;`
`add(1, 2)` `//3`
`add(1)` `// Error: Missing parameter.`
- 强大的reduce
数组的reduce方法用途很广。它一般被用来把数组中每一项规约到单个值。但是你可以利用它做更多的事。
2.1 使用reduce同时实现map和filter
假设现在有一个数列,你希望更新它的每一项(map的功能)然后筛选出一部分(filter的功能)。如果是先使用map然后filter的话,你需要遍历这个数组两次。
在下面的代码中,我们将数列中的值翻倍,然后挑选出那些大于50的数。有注意到我们是如何非常高效地使用reduce来同时完成map和filter方法的吗?
`const numbers = [10, 20, 30, 40];`
`const doubledOver50 = numbers.reduce((finalList, num) => {`
`num = num * 2; `
`if` `(num > 50) {`
`finalList.push(num);`
`}`
`return` `finalList;`
`}, []);`
`doubledOver50;` `// [60, 80]`
欢迎加入全栈开发交流划水交流圈:731771211
面向划水1-3年前端人员
帮助突破划水瓶颈,提升思维能力
2.2 使用reduce取代map和filter
如果你认真阅读了上面的代码,你应该能理解reduce是可以取代map和filter的。
2.3 使用reduce匹配圆括号
reduce的另外一个用途是能够匹配给定字符串中的圆括号。对于一个含有圆括号的字符串,我们需要知道(和)的数量是否一致,并且(是否出现在)之前。
下面的代码中我们使用reduce可以轻松地解决这个问题。我们只需要先声明一个counter变量,初值为0。在遇到(时counter加一,遇到)时counter减一。如果左右括号数目匹配,那最终结果为0。
`//Returns 0 if balanced.`
`const isParensBalanced = (str) => {`
`return` `str.split(``''``).reduce((counter, char) => {`
`if``(counter < 0) {` `//matched ")" before "("`
`return` `counter;`
`}` `else` `if``(char ===` `'('``) {`
`return` `++counter;`
`}` `else` `if``(char ===` `')'``) {`
`return` `--counter;`
`}` `else` `{` `//matched some other char`
`return` `counter;`
`}`
`}, 0);` `//<-- starting value of the counter}`
`isParensBalanced(``'(())'``)` `// 0 <-- balanced`
`isParensBalanced(``'(asdfds)'``)` `//0 <-- balanced`
`isParensBalanced(``'(()'``)` `// 1 <-- not balanced`
`isParensBalanced(``')('``)` `// -1 <-- not balanced`
2.4 统计数组中相同项的个数
很多时候,你希望统计数组中重复出现项的个数然后用一个对象表示。那么你可以使用reduce方法处理这个数组。
下面的代码将统计每一种车的数目然后把总数用一个对象表示。
`var` `cars = [``'BMW'``,``'Benz'``,` `'Benz'``,` `'Tesla'``,` `'BMW'``,` `'Toyota'``];`
`var` `carsObj = cars.reduce(``function` `(obj, name) {`
`obj[name] = obj[name] ? ++obj[name] : 1;`
`return` `obj;`
`}, {});`
`carsObj;` `// => { BMW: 2, Benz: 2, Tesla: 1, Toyota: 1 }`
reduce的其他用处实在是太多了,建议阅读MDN的相关代码示例。
2.4 统计数组中相同项的个数
很多时候,你希望统计数组中重复出现项的个数然后用一个对象表示。那么你可以使用reduce方法处理这个数组。
下面的代码将统计每一种车的数目然后把总数用一个对象表示。
`var` `cars = [``'BMW'``,``'Benz'``,` `'Benz'``,` `'Tesla'``,` `'BMW'``,` `'Toyota'``];`
`var` `carsObj = cars.reduce(``function` `(obj, name) {`
`obj[name] = obj[name] ? ++obj[name] : 1;`
`return` `obj;`
`}, {});`
`carsObj;` `// => { BMW: 2, Benz: 2, Tesla: 1, Toyota: 1 }`
reduce的其他用处实在是太多了,建议阅读MDN的相关代码示例。
- 对象解构
3.1 删除不需要的属性
有时候你不希望保留某些对象属性,也许是因为它们包含敏感信息或仅仅是太大了(just too big)。你可能会枚举整个对象然后删除它们,但实际上只需要简单的将这些无用属性赋值给变量,然后把想要保留的有用部分作为剩余参数就可以了。
下面的代码里,我们希望删除_internal和tooBig参数。我们可以把它们赋值给internal和tooBig变量,然后在cleanObject中存储剩下的属性以备后用。
`let {_internal, tooBig, ...cleanObject} = {el1:` `'1'``, _internal:``"secret"``, tooBig:{}, el2:` `'2'``, el3:` `'3'``};`
`console.log(cleanObject);` `// {el1: '1', el2: '2', el3: '3'}`
3.2 在函数参数中解构嵌套对象
在下面的代码中,engine是对象car中嵌套的一个对象。如果我们对engine的vin属性感兴趣,使用解构赋值可以很轻松地得到它。
`var` `car = {`
`model:` `'bmw 2018'``,`
`engine: {`
`v6:` `true``,`
`turbo:` `true``,`
`vin: 12345`
`}`
`}`
`const modelAndVIN = ({model, engine: {vin}}) => {`
`console.log(`model: ${model} vin: ${vin}`);`
`}`
`modelAndVIN(car);` `// => model: bmw 2018 vin: 12345`
3.3 合并对象
ES6带来了扩展运算符(...)。它一般被用来解构数组,但你也可以用它处理对象。
接下来,我们使用扩展运算符来展开一个新的对象,第二个对象中的属性值会改写第一个对象的属性值。比如object2的b和c就会改写object1的同名属性。
`let object1 = { a:1, b:2,c:3 }`
`let object2 = { b:30, c:40, d:50}`
`let merged = {…object1, …object2}` `//spread and re-add into merged`
`console.log(merged)` `// {a:1, b:30, c:40, d:50}`
- Sets
4.1 使用Set实现数组去重
在ES6中,因为Set只存储唯一值,所以你可以使用Set删除重复项。
`let arr = [1, 1, 2, 2, 3, 3];`
`let deduped = [...``new` `Set(arr)]` `// [1, 2, 3]`
4.2 对Set使用数组方法
使用扩展运算符就可以简单的将Set转换为数组。所以你可以对Set使用Array的所有原生方法。
比如我们想要对下面的Set进行filter操作,获取大于3的项。
`let mySet =` `new` `Set([1,2, 3, 4, 5]);`
`var` `filtered = [...mySet].filter((x) => x > 3)` `// [4, 5]`
- 数组解构
有时候你会将函数返回的多个值放在一个数组里。我们可以使用数组解构来获取其中每一个值。
5.1 数值交换
`let param1 = 1;`
`let param2 = 2;`
`//swap and assign param1 & param2 each others values`
`[param1, param2] = [param2, param1];`
`console.log(param1)` `// 2`
`console.log(param2)` `// 1`
5.2 接收函数返回的多个结果
在下面的代码中,我们从/post中获取一个帖子,然后在/comments中获取相关评论。由于我们使用的是async/await,函数把返回值放在一个数组中。而我们使用数组解构后就可以把返回值直接赋给相应的变量。
`async` `function` `getFullPost(){`
`return` `await Promise.all([`
`fetch(``'/post'``),`
`fetch(``'/comments'``)`
`]);`
`}`
`const [post, comments] = getFullPost();`
什么是ES6?
ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了。Mozilla公司将在这个标准的基础上,推出JavaScript 2.0。
ECMAScript和JavaScript到底是什么关系?很多初学者会感到困惑,简单来说,ECMAScript是JavaScript语言的国际标准,JavaScript是ECMAScript的实现。
一、let和const
在JavaScript中咱们以前主要用关键var来定义变量,ES6之后,新增了定义变量的两个关键字,分别是let和const。
对于变量来说,在ES5中var定义的变量会提升到作用域中所有的函数与语句前面,而ES6中let定义的变量则不会,let声明的变量会在其相应的代码块中建立一个暂时性死区,直至变量被声明。
let和const都能够声明块级作用域,用法和var是类似的,let的特点是不会变量提升,而是被锁在当前块中。
一个非常简单的例子:
`function` `test() {`
`if``(``true``) {`
`console.log(a)``//TDZ,俗称临时死区,用来描述变量不提升的现象`
`let a = 1`
`}`
`}`
`test()` `// a is not defined`
`function` `test() {`
`if``(``true``) {`
`let a = 1`
`}`
`console.log(a)`
`}`
`test()` `// a is not defined`
唯一正确的使用方法:先声明,再访问。
`function` `test() {`
`if``(``true``) {`
`let a = 1`
`console.log(a)`
`}`
`}`
`test()` `// 1`
const
声明常量,一旦声明,不可更改,而且常量必须初始化赋值。
const虽然是常量,不允许修改默认赋值,但如果定义的是对象Object,那么可以修改对象内部的属性值。
`const type = {`
`a: 1`
`}`
`type.a = 2` `//没有直接修改type的值,而是修改type.a的属性值,这是允许的。`
`console.log(type)` `// {a: 2}`
const和let的异同点
相同点:const和let都是在当前块内有效,执行到块外会被销毁,也不存在变量提升(TDZ),不能重复声明。
不同点:const不能再赋值,let声明的变量可以重复赋值。
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
块级作用域的使用场景
除了上面提到的常用声明方式,我们还可以在循环中使用,最出名的一道面试题:循环中定时器闭包的考题
for循环中使用var声明的循环变量,会跳出循环体污染当前的函数。
`for``(``var` `i = 0; i < 5; i++) {`
`setTimeout(() => {`
`console.log(i)` `//5, 5, 5, 5, 5`
`}, 0)`
`}`
`console.log(i)` `//5 i跳出循环体污染外部函数`
`//将var改成let之后`
`for``(let i = 0; i < 5; i++) {`
`setTimeout(() => {`
`console.log(i)` `// 0,1,2,3,4`
`}, 0)`
`}`
`console.log(i)``//i is not defined i无法污染外部函数`
在实际开发中,我们选择使用var、let还是const,取决于我们的变量是不是需要更新,通常我们希望变量保证不被恶意修改,而使用大量的const。使用const声明,声明一个对象的时候,也推荐使用const,当你需要修改声明的变量值时,使用let,var能用的场景都可以使用let替代。
symbol
ES6 以前,我们知道5种基本数据类型分别是Undefined,Null,Boolean,Number以及String,然后加上一种引用类型Object构成了JavaScript中所有的数据类型,但是ES6出来之后,新增了一种数据类型,名叫symbol,像它的名字表露的一样,意味着独一无二,意思是每个 Symbol类型都是独一无二的,不与其它 Symbol 重复。
可以通过调用 Symbol() 方法将创建一个新的 Symbol 类型的值,这个值独一无二,不与任何值相等。
`var` `mySymbol=Symbol();`
`console.log(``typeof` `mySymbol)` `//"symbol"`
二、字符串
ES6字符串新增的方法
UTF-16码位:ES6强制使用UTF-16字符串编码。关于UTF-16的解释请自行百度了解。
codePointAt():该方法支持UTF-16,接受编码单元的位置而非字符串位置作为参数,返回与字符串中给定位置对应的码位,即一个整数值。
String.fromCodePoiont():作用与codePointAt相反,检索字符串中某个字符的码位,也可以根据指定的码位生成一个字符。
normalize():提供Unicode的标准形式,接受一个可选的字符串参数,指明应用某种Unicode标准形式。
在ES6中,新增了3个新方法。每个方法都接收2个参数,需要检测的子字符串,以及开始匹配的索引位置。
模板字符串
字符串是JavaScript中基本类型之一,应该算是除了对象之外是使用最为频繁的类型吧,字符串中包含了例如substr,replace,indexOf,slice等等诸多方法,ES6引入了模板字符串的特性,用反引号来表示,可以表示多行字符串以及做到文本插值(利用模板占位符)。
`// 以前的多行字符串我们这么写:`
`console.log(``"hello world 1\n\`
`hello cala"``);`
`// "hello world`
`// hello cala"`
`//有了模板字符串之后`
`console.log(`hello world`
`string text line 2`);`
`// "hello world`
`// hello cala"`
可以用${}来表示模板占位符,可以将你已经定义好的变量传进括弧中,例如:
`var` `name=``"cala"``;`
`var` `age=22;`
`console.log(`hello,I``'am ${name},my age is ${age}`)`
`//hello,I'``am cala,my age is 22`
`includes(str, index):如果在字符串中检测到指定文本,返回``true``,否则``false``。`
`let t =` `'abcdefg'`
`if``(t.includes(``'cde'``)) {`
`console.log(2)`
`}`
`//true`
`startsWith(str, index):如果在字符串起始部分检测到指定文本,返回``true``,否则返回``false``。`
`let t =` `'abcdefg'`
`if``(t.startsWith(``'ab'``)) {`
`console.log(2)`
`}`
`//true`
`endsWith(str, index):如果在字符串的结束部分检测到指定文本,返回``true``,否则返回``false``。`
`let t =` `'abcdefg'`
`if``(t.endsWith(``'fg'``)) {`
`console.log(2)`
`}`
`//true`
如果你只是需要匹配字符串中是否包含某子字符串,那么推荐使用新增的方法,如果需要找到匹配字符串的位置,使用indexOf()。
三、函数
函数的默认参数
在ES5中,我们给函数传参数,然后在函数体内设置默认值,如下面这种方式。
`function` `a(num, callback) {`
`num = num || 6`
`callback = callback ||` `function` `(data) {console.log(``'ES5: '``, data)}`
`callback(num * num)`
`}`
`a()` `//ES5: 36,不传参输出默认值`
`//你还可以这样使用callback`
`a(10,` `function``(data) {`
`console.log(data * 10)` `// 1000, 传参输出新数值`
`})`
在ES6中,我们使用新的默认值写法
`function` `a(num = 6, callback =` `function` `(data) {console.log(``'ES6: '``, data)}) {`
`callback(num * num)`
`}`
`a()` `//ES6: 36, 不传参输出默认值`
`a(10,` `function``(data) {`
`console.log(data * 10)` `// 1000,传参输出新数值`
`})`
四、箭头函数(=>)
(箭头函数比较重要,现在简单提一下,迟一点有空专门写一篇箭头函数的文章。)
`const arr = [5, 10]`
`const s = arr.reduce((sum, item) => sum + item)`
`console.log(s)` `// 15`
箭头函数中this的使用跟普通函数也不一样,在JavaScript的普通函数中,都会有一个自己的this值,主要分为:
普通函数:
1、函数作为全局函数被调用时,this指向全局对象
2、函数作为对象中的方法被调用时,this指向该对象
3、函数作为构造函数的时候,this指向构造函数new出来的新对象
4、还可以通过call,apply,bind改变this的指向
箭头函数:
1、箭头函数没有this,函数内部的this来自于父级最近的非箭头函数,并且不能改变this的指向。
2、箭头函数没有super
3、箭头函数没有arguments
4、箭头函数没有new.target绑定。
5、不能使用new
6、没有原型
7、不支持重复的命名参数。
箭头函数的简单理解
1、箭头函数的左边表示输入的参数,右边表示输出的结果。
`const s = a => a`
`cosole.log(s(2))` `// 2`
2、在箭头函数中,this属于词法作用域,直接由上下文确定,对于普通函数中指向不定的this,箭头函数中处理this无疑更加简单,如下:
`//ES5普通函数`
`function` `Man(){`
`this``.age=22;`
`return` `function``(){`
`this``.age+1;`
`}`
`}`
`var` `cala=``new` `Man();`
`console.log(cala())``//undefined`
`//ES6箭头函数`
`function` `Man(){`
`this``.age=22;`
`return` `() =>` `this``.age+1;`
`}`
`var` `cala=``new` `Man();`
`console.log(cala())``//23`
3、箭头函数中没有arguments(我们可以用rest参数替代),也没有原型,也不能使用new 关键字,例如:
`//没有arguments`
`var` `foo=(a,b)=>{``return` `arguments[0]*arguments[1]}`
`console.log(foo(3,5))`
`//arguments is not defined`
`//没有原型`
`var` `Obj = () => {};`
`console.log(Obj.prototype);`
`// undefined`
`//不能使用new 关键字`
`var` `Obj = () => {``"hello world"``};`
`var` `o =` `new` `Obj();`
`// TypeError: Obj is not a constructor`
4、箭头函数给数组排序
`const arr = [5, 10]`
`const s = arr.reduce((sum, item) => sum + item)`
`console.log(s)` `// 15`
尾调用优化
尾调用是指在函数return的时候调用一个新的函数,由于尾调用的实现需要存储到内存中,在一个循环体中,如果存在函数的尾调用,你的内存可能爆满或溢出。
ES6中,引擎会帮你做好尾调用的优化工作,你不需要自己优化,但需要满足下面3个要求:
1、函数不是闭包
2、尾调用是函数最后一条语句
3、尾调用结果作为函数返回
尾调用实际用途——递归函数优化
在ES5时代,我们不推荐使用递归,因为递归会影响性能。
但是有了尾调用优化之后,递归函数的性能有了提升。
`//新型尾优化写法`
`"use strict"``;`
`function` `a(n, p = 1) {`
`if``(n <= 1) {`
`return` `1 * p`
`}`
`let s = n * p`
`return` `a(n - 1, s)`
`}`
`//求 1 x 2 x 3的阶乘`
`let sum = a(3)`
`console.log(sum)` `// 6`
五、ES6对象新增方法
Object.assign()
Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。
String类型和 Symbol 类型的属性都会被拷贝。
合并对象
`var` `o1 = { a: 1 };`
`var` `o2 = { b: 2 };`
`var` `o3 = { c: 3 };`
`var` `obj = Object.assign(o1, o2, o3);`
`console.log(obj);` `// { a: 1, b: 2, c: 3 }`
`console.log(o1);` `// { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。`
合并具有相同属性的对象
`var` `o1 = { a: 1, b: 1, c: 1 };`
`var` `o2 = { b: 2, c: 2 };`
`var` `o3 = { c: 3 };`
`var` `obj = Object.assign({}, o1, o2, o3);`
`console.log(obj);` `// { a: 1, b: 2, c: 3 }`
欢迎加入全栈开发交流划水交流圈:731771211
面向划水1-3年前端人员
帮助突破划水瓶颈,提升思维能力
六、Map和Set
Map和Set都叫做集合,但是他们也有所不同。Set常被用来检查对象中是否存在某个键名,Map集合常被用来获取已存的信息。
Set是有序列表,含有相互独立的非重复值。
Array和Set对比
都是一个存储多值的容器,两者可以互相转换,但是在使用场景上有区别。如下:
Array的indexOf方法比Set的has方法效率低下
Set不含有重复值(可以利用这个特性实现对一个数组的去重)
Set通过delete方法删除某个值,而Array只能通过splice。两者的使用方便程度前者更优
Array的很多新方法map、filter、some、every等是Set没有的(但是通过两者可以互相转换来使用)
Object和Map对比
Object是字符串-值,Map是值-值
Object键为string类型,Map的键是任意类型
手动计算Object尺寸,Map.size可以获取尺寸
Map的排序是插入顺序
Object有原型,所以映射中有一些缺省的键。可以理解为Map=Object.create(null)
Set操作集合
`let set =` `new` `Set()`
`// Set转化为数组`
`let arr = Array.from(set)`
`let arr = [...set]`
`// 实例属性(继承自Set)`
`set.constructor === Set`
`set.size`
`// 操作方法`
`set.add(1)` `// 添加一个值`
`set.``delete``(1)` `//删除一个值`
`set.has(1)` `//判断是否有这个值(Array中的indexOf)`
`set.clear()` `//清除所有值`
`// 获取用于遍历的成员方法(Set的遍历顺序就是插入顺序)`
`set.keys()` `// 返回键名的遍历器`
`set.values()` `// 返回键值得遍历器`
`set.entries()` `// 返回键值对的遍历器`
`set.forEach()` `// 循环遍历每个值(和Array的方法一致)`
`for` `(let key of set.keys()){}`
`for` `(let val of set.values()){}`
`for` `(let entry of set.entries()){}`
`// 使用数组方法来处理set值`
`set =` `new` `Set(arr)`
`set =` `new` `Set([...set].map((x) => x = x * 2))`
`set =` `new` `Set([...set].filter((x) => x > 2))`
Map的方法集合
`let map =` `new` `Map()`
`// 实例属性(继承自Map)`
`map.constructor === Map`
`map.size`
`// 操作方法`
`map.set(1,2)`
`map.get(1)`
`map.``delete``(1)`
`map.has(1)`
`map.clear()`
`// 遍历方法`
`map.keys()`
`map.values()`
`map.entries()`
`map.forEach()`
`// Map和数组的转换`
`map =` `new` `Map([[``'key'``,``'val'``],[2,1]])` `// 要求双成员数组`
`let arr = [...map]`
`// 值得注意的是Map的键是跟内存绑定的`
`map.set([1],` `'s'``)`
`map.get([1])`
`let arr = [1]`
`let arr1 = [1]`
`map.set(arr,` `'s'``)`
`map.get(arr)`
`map.set(arr1,` `'s'``)`
`map.get(arr1)`
七、迭代器(Iterator)
1、entries() 返回迭代器:返回键值对
`//数组`
`const arr = [``'a'``,` `'b'``,` `'c'``];`
`for``(let v of arr.entries()) {`
`console.log(v)`
`}`
`// [0, 'a'] [1, 'b'] [2, 'c']`
`//Set`
`const arr =` `new` `Set([``'a'``,` `'b'``,` `'c'``]);`
`for``(let v of arr.entries()) {`
`console.log(v)`
`}`
`// ['a', 'a'] ['b', 'b'] ['c', 'c']`
`//Map`
`const arr =` `new` `Map();`
`arr.set(``'a'``,` `'a'``);`
`arr.set(``'b'``,` `'b'``);`
`for``(let v of arr.entries()) {`
`console.log(v)`
`}`
`// ['a', 'a'] ['b', 'b']`
2、values() 返回迭代器:返回键值对的value
`//数组`
`const arr = [``'a'``,` `'b'``,` `'c'``];`
`for``(let v of arr.values()) {`
`console.log(v)`
`}`
`//'a' 'b' 'c'`
`//Set`
`const arr =` `new` `Set([``'a'``,` `'b'``,` `'c'``]);`
`for``(let v of arr.values()) {`
`console.log(v)`
`}`
`// 'a' 'b' 'c'`
`//Map`
`const arr =` `new` `Map();`
`arr.set(``'a'``,` `'a'``);`
`arr.set(``'b'``,` `'b'``);`
`for``(let v of arr.values()) {`
`console.log(v)`
`}`
`// 'a' 'b'`
3、keys() 返回迭代器:返回键值对的key
`//数组`
`const arr = [``'a'``,` `'b'``,` `'c'``];`
`for``(let v of arr.keys()) {`
`console.log(v)`
`}`
`// 0 1 2`
`//Set`
`const arr =` `new` `Set([``'a'``,` `'b'``,` `'c'``]);`
`for``(let v of arr.keys()) {`
`console.log(v)`
`}`
`// 'a' 'b' 'c'`
`//Map`
`const arr =` `new` `Map();`
`arr.set(``'a'``,` `'a'``);`
`arr.set(``'b'``,` `'b'``);`
`for``(let v of arr.keys()) {`
`console.log(v)`
`}`
`// 'a' 'b'`
虽然上面列举了3种内建的迭代器方法,但是不同集合的类型还有自己默认的迭代器,在for of中,数组和Set的默认迭代器是values(),Map的默认迭代器是entries()。
for of循环解构
对象本身不支持迭代,但是我们可以自己添加一个生成器,返回一个key,value的迭代器,然后使用for of循环解构key和value。
`const obj = {`
`a: 1,`
`b: 2,`
`*[Symbol.iterator]() {`
`for``(let i` `in` `obj) {`
`yield [i, obj[i]]`
`}`
`}`
`}`
`for``(let [key, value] of obj) {`
`console.log(key, value)`
`}`
`// 'a' 1, 'b' 2`
字符串迭代器
`const str =` `'abc'``;`
`for``(let v of str) {`
`console.log(v)`
`}`
`// 'a' 'b' 'c'`
ES6给数组添加了几个新方法:find()、findIndex()、fill()、copyWithin()
1、find():传入一个回调函数,找到数组中符合当前搜索规则的第一个元素,返回它,并且终止搜索。
`const arr = [1,` `"2"``, 3, 3,` `"2"``]`
`console.log(arr.find(n =>` `typeof` `n ===` `"number"``))` `// 1`
2、findIndex():传入一个回调函数,找到数组中符合当前搜索规则的第一个元素,返回它的下标,终止搜索。
`const arr = [1,` `"2"``, 3, 3,` `"2"``]`
`console.log(arr.findIndex(n =>` `typeof` `n ===` `"number"``))` `// 0`
3、fill():用新元素替换掉数组内的元素,可以指定替换下标范围。
`arr.fill(value, start, end)`
4、copyWithin():选择数组的某个下标,从该位置开始复制数组元素,默认从0开始复制。也可以指定要复制的元素范围。
`arr.copyWithin(target, start, end)`
`const arr = [1, 2, 3, 4, 5]`
`console.log(arr.copyWithin(3))` `// [1,2,3,1,2] 从下标为3的元素开始,复制数组,所以4, 5被替换成1, 2`
`const arr1 = [1, 2, 3, 4, 5]`
`console.log(arr1.copyWithin(3, 1))` `// [1,2,3,2,3] 从下标为3的元素开始,复制数组,指定复制的第一个元素下标为
1,所以4, 5被替换成2, 3`
`const arr2 = [1, 2, 3, 4, 5]`
`console.log(arr2.copyWithin(3, 1, 2))` `// [1,2,3,2,5] 从下标为3的元素开始,复制数组,指定复制的第一个元素下标为1,结束位置为2,所以4被替换成2`
ES6中类class、Promise与异步编程、代理(Proxy)和反射(Reflection)API,这几块内容比较复杂,以后有机会再详细写。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。