for...of
基本用法
const arr = [100, 200, 300, 400]
for (const item of arr) {
console.log(item)
if (item > 100) {
break // 通过break进行终止循环
}
}
相比forEach,可通过break进行终止循环,而forEach不能通过break中断循环
遍历Set和Map
// 遍历Set
const s = new Set(['foo', 'bar'])
for (const item of s) {
console.log(item)
}
// 遍历Map
const m = new Map()
m.set('foo', '123')
m.set('bar', '345')
for (const [key, value] of m) {
console.log(key, value)
}
输出
foo 123
bar 345
缺点
- 不能遍历普通对象
- 必须实现iterator接口才可以被for...of遍历
const obj = { foo: 123, bar: 456 }
for (const item of obj) {
console.log(item)
}
// 报错 TypeError: obj is not iterable
可迭代接口iterator
for...of内部的原理:
通过调用被遍历对象的iterator方法得到一个迭代器从而去遍历内部的数据
const set = new Set(['foo', 'bar', 'baz'])
const iterator = set[Symbol.iterator]()
console.log(iterator.next()) // { value: 'foo', done: false }
console.log(iterator.next()) // { value: 'bar', done: false }
console.log(iterator.next()) // { value: 'baz', done: false }
console.log(iterator.next()) // { value: undefined, done: true }
普通对象实现迭代器
const obj = {
store: ['foo', 'bar', 'baz'],
[Symbol.iterator]: function () {
let index = 0
const self = this
return {
next: function () {
const result = {
value: self.store[index],
done: index >= self.store.length
}
index++
return result
}
}
}
}
for (const item of obj) {
console.log('循环体', item)
}
对外实现统一遍历接口
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '外语'],
work: ['喝茶'],
// 提供迭代器(ES2015 统一遍历访问接口)
[Symbol.iterator]: function () {
const all = [...this.life, ...this.learn, ...this.work]
let index = 0
return {
next: function () {
return {
value: all[index],
done: index++ >= all.length
}
}
}
}
}
for (const item of todos) {
console.log(item)
}
生成器(惰性执行)
语法(也实现了iterator接口)
function * foo () {
yield 100
yield 200
yield 300
}
const generator = foo()
console.log(generator.next()) // 遇到第一个 yield 暂停 { value: 100, done: false }
console.log(generator.next()) //遇到下一个 yield 再次暂停 { value: 200, done: false }
console.log(generator.next()) // { value: 300, done: false }
console.log(generator.next()) // { value: undefined, done: true }
基本用法
- 发号器
function * createIdMaker () {
let id = 1
while (true) {
yield id++
}
}
const idMaker = createIdMaker()
console.log(idMaker.next().value) // 1
console.log(idMaker.next().value) // 2
console.log(idMaker.next().value) // 3
console.log(idMaker.next().value) // 4
- 使用 Generator 函数实现 iterator 方法
const todos = {
life: ['吃饭', '睡觉', '打豆豆'],
learn: ['语文', '数学', '外语'],
work: ['喝茶'],
[Symbol.iterator]: function * () {
const all = [...this.life, ...this.learn, ...this.work]
for (const item of all) {
yield item
}
}
}
for (const item of todos) {
console.log(item)
}
ES2016新增特性
includes
可检测包含NaN
const arr = ['foo', 1, NaN, false]
console.log(arr.indexOf(NaN) // false
console.log(arr.includes(NaN) // true
指数运算符
2 ** 10 等同于 Math.pow(2, 10)
ES2017新增特性
Object.values()
const obj = {
foo: 'value1',
bar: 'value2'
}
console.log(Object.values(obj)); // [ 'value1', 'value2' ]
Object.entries()
for (const [key, value] of Object.entries(obj)) {
console.log(key, value);
}
输出
foo value1
bar value2
Object.getOwnPropertyDescriptors()
- 复制完整的数据信息
const p1 = {
firstName: 'Lei',
lastName: 'Wang',
get fullName () {
return this.firstName + ' ' + this.lastName
}
}
console.log(p1.fullName);
const p2 = Object.assign({}, p1) // 会将fullName当做普通对象来复制
p2.firstName = 'zce'
console.log(p2.fullName); // Lei Wang
const descriptors = Object.getOwnPropertyDescriptors(p1) // 复制完整的信息
const p2 = Object.defineProperties({}, descriptors)
p2.firstName = 'zce'
console.log(p2.fullName); // zce Wang
padEnd/padStart
- 给定的字符串去填充目标字符串的开始或者结尾直到达到指定的长度
- padEnd(长度, 给定的字符串)
const books = {
html: 5,
css: 16,
javascript: 128
}
for (const [name, count] of Object.entries(books)) {
console.log(`${name.padEnd(16, '-')}|${count.toString().padStart(3, 0)}`);
}
输出:
html------------|005
css-------------|016
javascript------|128