Professional JavaScript for Web Developers, 4th Edition Chapter7-Chapter12

版本 更新说明 时间 地点
1.0 新增本笔记 2021/01/28 于创美办公室


文章目录

  • 第7章 迭代器与生成器
    • 7.1 理解迭代
    • 7.2 迭代器模式
      • 7.2.1 可迭代协议
      • 7.2.2 迭代器协议
      • 7.2.3 自定义迭代器
      • 7.2.4 提前终止迭代器
    • 7.3 生成器
      • 7.3.1 生成器基础
      • 7.3.2 通过yield中断执行
        • 1. 生成器对象作为可迭代对象
        • 2. 使用yield实现输入输出
        • 3. 产生可迭代对象
      • 7.3.3 生成器作为默认迭代器
      • 7.3.4 提前终止生成器
  • 第8章 对象、类与面向对象编程
    • 8.1 理解对象
      • 8.1.1 属性的类型
        • 1. 数据属性 Data Properties
        • 2. 访问器属性 Accessor Properties
      • 8.1.2 定义多个属性
      • 8.1.3 读取属性的特性
      • 8.1.4 合并对象
      • 8.1.5 对象标识及相等判定
      • 8.1.6 增强的对象语法
        • 2. 可计算属性键 Computed Property Keys

第7章 迭代器与生成器

【重点】
理解迭代
迭代器模式
生成器

7.1 理解迭代

使用数组进行迭代的缺点:

  1. 需要事先知道数组的数据结构
  2. 递增索引迭代元素的方式只适用于数组

7.2 迭代器模式

迭代器模式 Iterator Pattern (特指ECMAScript语义下)一个结构实现Iteratable接口,并能被一个Iterator所消费。在迭代器模式下,无需知道迭代对象的内部数据结构,只需要使用其暴露的迭代器进行迭代即可。

  • iterable 可迭代对象 一种抽象的说法,可以理解成数组,一是元素有限,而是可以无歧义的遍历
  • iterator 迭代器

临时性可迭代对象可实现为生成器

7.2.1 可迭代协议

可迭代协议 Iterable Protocol 迭代时自我识别的能力和产生迭代器的能力

实现可迭代协议的数据类型

  1. 数组
  2. 字符串
  3. map
  4. set
  5. arguments
  6. NodeList等DOM集合类型

默认接受可迭代对象的原生语言支持有:

  1. for-of循环
  2. 数组解构
  3. 扩展运算符
  4. Array.from()
  5. Map的构造函数
  6. Set的构造函数
  7. Promise.all()接收由Promise组成的可迭代对象
  8. Promise.race()接收由Promise组成的可迭代对象
  9. yield *操作符
let arr = ['foo', 'bar', 'baz']

// 解构赋值
let [a, b, c] = arr
log(a, b, c) // foo bar baz

// 扩展操作符
let arr2 = [...arr]
log(arr2) // [ 'foo', 'bar', 'baz' ]

7.2.2 迭代器协议

迭代器 Iterator 迭代器是一种一次性对象,用于迭代与其关联的迭代对象

  • next(),返回一个nextResult对象
  • nextResult对象,包含value属性和done属性,前者是当前迭代的值,后者指示迭代结束与否,false表示仍有值
log = console.log

let arr = ['foo', 'bar', 'baz']

let iterator = arr[Symbol.iterator]()
log(iterator) // Object [Array Iterator] {}

let iteratorResult1= iterator.next()
let iteratorResult2= iterator.next()
let iteratorResult3= iterator.next()
let iteratorResult4= iterator.next()


log(iteratorResult1) // { value: 'foo', done: false }
log(iteratorResult2) // { value: 'bar', done: false }
log(iteratorResult3) // { value: 'baz', done: false }
log(iteratorResult4) // { value: undefined, done: true }

7.2.3 自定义迭代器


class Counter {
     
    constructor(limit){
     
        this.limit = limit
    }

    [Symbol.iterator](){
     
        let counter = 1
        let limit = this.limit

        return {
     
            next(){
     
                return counter <= limit ? {
      value: counter++, done: false } 
                                       : {
      value: undefined, done: true }
            }
        }
    }
}

let counter = new Counter(3)
for(const value of counter){
     
    console.log(value)
}
for(const value of counter){
     
    console.log(value)
}
/*Output:
    1
    2
    3
    1
    2
    3
*/

7.2.4 提前终止迭代器

for-of循环遇到break/return/throw提前退出时,会调用迭代器中的return()方法(调用return()方法不会强制关闭迭代器,所以可以在迭代器中断的地方调用next()方法继续遍历)

⚠️书中提到continue也会提前退出时,这是明显错误的,continue只会会跳过当此循环而已。

class Counter {
     
    constructor(limit) {
     
        this.limit = limit
    }

    [Symbol.iterator]() {
     
        let counter = 1
        let limit = this.limit
        return {
     
            next() {
     
                return counter <= limit ? {
      value: counter++, done: false }
                    : {
      value: counter++, done: true }
            },
            return() {
     
                console.log('return() : I am be called')
                return {
      value: undefined, done: true }
            }

        }
    }
}

const counter = new Counter(3)
for (const value of counter) {
     

    if (value === 2) {
     
        continue
    }
    console.log(value)

}
// output:
// 1
// 3

for (const value of counter) {
     

    if (value === 2) {
     
        break
    }
    console.log(value)

}
(() => {
     
    for (const value of counter) {
     
        if (value === 2) {
     
            return
        }
        console.log(value)
    }
})()
// Output:
// 1
// return() : I am be called

try {
     
    for (const value of counter) {
     
    
        if (value === 2) {
     
            throw 'Error'
        }
        console.log(value)
    }
} catch (error) {
     
    console.log(error)
}

// Output:
// 1
// return() : I am be called
// Error

7.3 生成器

  • 生成器 Generators 由生成器函数返回并符合可迭代协议和迭代器协议

The Generator object is returned by a generator function and it conforms to both the iterable protocol and the iterator protocol.

7.3.1 生成器基础

  • 生成器的声明,只需要在函数前面加一个星号*
    • 只要在有函数的地方,就可以定义生成器
    • 生成器灵活之处在于能够在函数体暂停和回复代码的执行
 // 生成器函数声明
 function * generatorFn(){
     }

箭头函数不能用来定义生成器

  • 生成器对象 Generator Object 调用生成器来生成,生成器对象一开始处于暂停执行(suspended executioni)的状态,生成器由于实现了Iterable接口,因此具有next()方法,调用这个方法会让生成器开始或恢复执行。

7.3.2 通过yield中断执行

  • yield关键字可以让生成器停止和开始执行,也是生成器最有用的地方。

生成器对象由生成器产生后,通过调用next()方法让生成器对象从suspend状态转为执行状态,它会去执行生成器函数,在遇到yield关键字的时候,执行yield关键字后的语句,并将该语句的结果作为value值封装在nextResult对象中,作为next()方法的返回值,此时生成器它的函数作用域的状态会被保存起来,在下次生成器对象调用next()方法后,从yield关键字的下一条语句继续执行,如果再次遇到yield关键字,则做相同的操作,当遇到return或者生成器函数体执行结束后,作为next()的返回值nextResult对象,它的value为return的返回值或者undefined,它的donetrue

function* generatorFn() {
     
    console.log('开始执行')
    console.log('正常执行中...')
    console.log('正常执行中...')
    yield '中断执行'
    console.log('恢复执行')
    console.log('正常执行中...')
    console.log('执行结束...')
}

const generatorObject = generatorFn()
console.log(generatorObject.next())
console.log(generatorObject.next())

/*Output

	开始执行
	正常执行中...
	正常执行中...
	{ value: '中断执行', done: false }
	恢复执行
	正常执行中...
	执行结束...
	{ value: undefined, done: true }
	
*/

function* generatorFn(n) {
     
    while (n--) {
     
        yield n
        if (n === 2) {
     
            return 'evil may cry'
        }
    }
}

const n = 5
let generatorObject = generatorFn(5)
for (let i = 0; i < n; i++) {
     
    console.log(generatorObject.next())
}

/*Output:

	{ value: 4, done: false }
	{ value: 3, done: false }
	{ value: 2, done: false }
	{ value: 'evil may cry', done: true }
	{ value: undefined, done: true }
	
*/

1. 生成器对象作为可迭代对象

2. 使用yield实现输入输出

yield关键字会接受到next()方法传递过来的值,并将其后的语句运算结果放到nextResult对象中返回

⚠️注意,yield关键字会无法接受生成器对象第一次调用next()方法的值,因为它第一次调用next()方法时启动生成器函数

function* generatorFn() {
     
    console.log(yield 'hello')
    console.log(yield 'generator')
}

const generatorObject = generatorFn()
console.log(generatorObject.next('foo'))
console.log(generatorObject.next('baz'))
console.log(generatorObject.next('qux'))
/*Output:
	{ value: 'hello', done: false }
	baz
	{ value: 'generator', done: false }
	qux
	{ value: undefined, done: true }
*/

3. 产生可迭代对象

  • 使用*可以增强yield的行为,让它能够迭代一个对象,yield *的返回值依迭代器类型而定:
    • 普通迭代器:undefined
    • 由生成器函数产生的迭代器,值为生成器函数返回的值

function* generatorFnA() {
     
    for (const x of [1, 2, 3]) {
     
        yield x
    }
}

for (const x of generatorFnA()) {
     
    console.log(x)
}
/*OutPut:
    1
    2
    3
*/

function* generatorFnB() {
     
    yield* [1, 2, 3]
}

for (const x of generatorFnB()) {
     
    console.log(x)
}
/*Output:
	1
	2
	3
*/
function* innerGeneratorFn() {
     
    yield 'foo'
    yield 'baz'
    return 'qux'
}

function* outerGeneratorFn() {
     
    console.log('iter value', yield* innerGeneratorFn())
}

for (const x of outerGeneratorFn()) {
     
    console.log('value: ', x)
}
/*OutPut:
	value:  foo
	value:  baz
	iter value qux
*/

7.3.3 生成器作为默认迭代器

class Foo {
     
    constructor() {
     
        this.value = [1, 2, 3]
    }
    *[Symbol.iterator]() {
     
        yield* this.value
    }
}

const f = new Foo()
for (const x of f) {
     
    console.log(x)
}

7.3.4 提前终止生成器

生成器对象有两个方法可以使强制生成器进入关闭状态

  1. return()方法,调用此方法后就永久关闭生成器
  2. throw()方法,该方法会在暂停的时候将一个提供的错误注入到生成器对象中,如果错误未被处理,生成器就会关闭,如果生成器内部处理了该错误,那么生成器就不会被关闭
function* generatorFn() {
     
    for (const x of [1, 2, 3]) {
     
        try {
     
            yield x
        } catch (e) {
     
            console.log("I am catch the ", e)
        }
    }
}

const generatorObject = generatorFn()
console.log(generatorObject.next())
console.log(generatorObject.throw(new Error('Wrong')))
console.log(generatorObject.next())
/*Output:
{ value: 1, done: false }
I am catch the  Error: Wrong
	...
{ value: 2, done: false }
{ value: 3, done: false }
*/

第8章 对象、类与面向对象编程

  • ECMA-262将对象定义为一组属性的无序集合

8.1 理解对象

创建对象

  1. new Object(),然后再添加属性
  2. 使用对象字面量
job = 'Software engineer'

let person = {
     
    name: 'Nicholas',
    age: 29,
    // 增强的属性简写,原本应写为 job: job
    job, 
    // 增强的方法简写,原本应写为sayName: function(){...}
    sayName(){
      
        console.log(this.name)
    }
}

8.1.1 属性的类型

对象中的属性有两种类型

1. 数据属性 Data Properties

数据属性有四个特性(attribute):

  1. [[Configurable]]:是否可以通过delete删除并重定义、是否可以修改它的特性、是否可以把它改为访问器属性
  2. [[Enumerable]]
  3. [[Writable]]
  4. [[Value]]

2. 访问器属性 Accessor Properties

访问器属性只能通过Object.defineProperty()方法进行定义:

  1. [[Configurable]]:是否可以通过delete删除并重定义、是否可以修改它的特性、是否可以把它改为访问器属性
  2. [[Enumerable]]
  3. [[Get]]:获取函数
  4. [[Set]] :设置函数

8.1.2 定义多个属性

Object.defineProperties()

8.1.3 读取属性的特性

  • 获取一个属性:Object.getOwnPropertyDescriptor()
  • 获取全部属性:Object.getOwnPropertyDescriptors()

8.1.4 合并对象

  • Object.assign(targetObject, resourceObject...)方法:将resourceObject中可枚举和自有属性赋值到targetObject中

该方法执行的是浅复制(Shallow Copy)

8.1.5 对象标识及相等判定

Object.is()

8.1.6 增强的对象语法

2. 可计算属性键 Computed Property Keys

  • 可计算属性键 Computed Property Keys:可进行动态赋值的属性键

const nameKey = `name`
const ageKey = `age`
const jobKey = `job`

let person = {
     
    [nameKey]: `Matt`,
    [ageKey]: 27,
    [jobKey]: `Software Engineer`
}

console.log(person) 
// { name: 'Matt', age: 27, job: 'Software Engineer' }

你可能感兴趣的:(#,JavaScript,js)