JS知识点备忘

知识点分享:

    一.中间件
        1.1  为啥要用中间件?
            中间件是一个函数,为了解决异步操作,传统的action -> reducer 变为 action -> middleware -> reducer
        1.2  react-thunk?
            在异步抓取数据的时候,可以发出三种action,分别是:操作发起,操作成功,操作失败
            使用react-thunk可以dispatch(function),react-thunk接收到后,如果是一个函数,则返回这个函数的 调用
            function 来自 action creactor ,此函数又return出一个函数,参数时dispatch与getstate,在return出的函数可以进行异步处理,还可以dispatch action
            举个栗子:

            export function fetchTopics() {
                return (dispatch,getState) => {
                    apis.getTopic.getList()
                    .then(res => dispatch(fetchTopicsSuccess(res.data)))
                    .catch(error => dispatch(fetchTopicsFailed(error)))
                }
            }

    二.遍历器iterator
        1.遍历器是什么?
            是一种接口,位各种不同的数据结构提供统一的访问机制,
            任何数据结构只要部署了iterator接口,就可以完成遍历操作。
        2.作用是什么?
            1.为各种数据结构,提供一个统一的访问接口
            2.使得数据结构的成员能够按照某种次序排列
            3.ES6创造了一种新的遍历命令for...of循环,iterator主要供for...of消费
        3.遍历过程是怎样的?
            1.创建一个指针对象,指向当前数据结构的起始位置。
            2.第一次调用指针的next()方法,可以将指针指向数据结构的第一个成员
            3.第二次调用next()指针指向第二个成员
            4.不断调用指针的next方法,直到它指向数据结构结束的位置
              每一次调用next方法, 都会返回数据结构的当前成员的信息。如{value:1,done:false}
        4.默认的iterator接口
            1.当使用for...of循环遍历某种数据结构时,该循环会自动寻找iterator接口
            2.ES6规定,一个数据结构只要具有Symbol.iterator属性,就可以认为是可遍历的的。该属性本身是一个函数,是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器对象。

        5.为对象添加Iterator接口,一个对象如果要具备可被for...of循环调用的Iterator接口,就必须在Symbol.iterator的属性上部署遍历器生成方法。

      let obj = {
            data:['hello','world'],
            [Symbol.iterator]() {
                const self = this;
                let index = 0;
                return {
                    next() {
                        if(index < self.data.length) {
                            return {
                                value: self.data[index++],
                                done:false
                            }
                        }else {
                            return { value: undefined, done: true }
                        }
                    }
                }
            }
        }

        为类似数组的对象调用数组的Symbol.iterator方法

     let iterable = {
            0: 'a',
            1: 'b',
            2;'c',
            length: 3,
            [Symbol.iterator]: Array.prototype[Symbol.iterator]
        }
        
        for(let item of oterable) {
            console.log(item) //a b c
        }

        6.调用iterator接口的场合
            1.解构赋值
            2.扩展运算符
            3.yield* 后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口
            4.任何接受数组作为参数的场合,都调用了遍历器接口
                for...of
                Array.from()
                Map(),Set()
                Promise.all()
                Promise.race()

    三.generator函数
        1.什么是generator?
            是ES6提供的一种异步编程解决方案
            从语法上:是一个状态机,封装了多个内部状态。
            从返回值:执行generator函数会返回一个遍历器对象。
            generator:状态机/遍历器对象生成函数
            返回的遍历器对象,可以依次遍历generator函数内部的每一个状态

            通过generator为对象加遍历接口

                function* objectEntries() {
                    let propKeys = Object.keys(this)
            
                    for(let propKey of propKeys) {
                        yield [propKey,this[propKey]]
                    }
                }
            
                let jane = { first: 'jane', last: 'Doe'};
                jane[Symbol.iterator] = objectEntries
                for(let [key,value] of jane) {
                    console.log(`${key} : ${value}`)
                }

        2.generetor为啥能实现异步?
            generator函数可以暂停执行和恢复执行,是它能封装异步任务的根本原因
        
        3.yield是干啥的?
            yield后面的表达式,只有当next方法将指针移到它时,才会执行
            yield是暂停标志,当函数遇到yield时,会暂停执行,并将yield后的值作为对象属性中的value返回
        
        4.yield与return的异同:
            相同点:都能返回紧跟在它之后表达式的值
            不同点:yield能记录位置,return不能,一个函数中可以有多个yield
        
        5.next方法的参数:
            yield本身无返回值,next方法可以带一个参数,作为上一次yield的表达式的返回值
            举个栗子:

            function* foo(x) {
                var y = 2 * (yield (x + 1));
                var z = yield (y / 3);
                return (x + y + z);
            }
            var a = foo(5);
            a.next() // Object{value:6, done:false}
            a.next() // Object{value:NaN, done:false}
            a.next() // Object{value:NaN, done:true}

            var b = foo(5);
            b.next() // { value:6, done:false }
            b.next(12) // { value:8, done:false }
            b.next(13) // { value:42, done:true }   //5+24+13


        6.内部可以使用try...catch捕获错误

            function* gen(x){
                try {
                    var y = yield x + 2;
                } catch (e){
                    console.log(e);
                }
                return y;
                }

                var g = gen(1);
                g.next();
                g.throw('出错了');

        7.generator与协程
            协程:多个线程互相协作,完成异步任务(单线程情况下,即多个函数可以并行执行)
                第一步,协程A开始执行。
                第二步,协程A执行到一半,进入暂停,执行权转移到协程B。
                第三步,(一段时间后)协程B交还执行权。
                第四步,协程A恢复执行。
            协程遇到yield命令就暂停,等到执行权返回,再从暂停的地方继续往后执行。        8.react-saga基础学习使用
            sagas:被实现为generator
                yield effect -> redux-saga middleware 解释执行指令 -> resolve/reject ->sagas恢复执行
                                        ⬇
                            sagas暂停,等待promise完成
    
    四:async await
        1.是什么?
            generator的升级版
        2.有什么优点?
            1.内置执行器
            2.语义化
            3.适用性广:await后面可以是promise对象或者原始类型的值
            4.返回值是promise


        3.怎么用?

   // 函数声明
            async function foo() {}

            // 函数表达式
            const foo = async function () {};

            // 对象的方法
            let obj = { async foo() {} };
            obj.foo().then(...)

            // Class 的方法
            class Storage {
            constructor() {
                this.cachePromise = caches.open('avatars');
            }

            async getAvatar(name) {
                const cache = await this.cachePromise;
                return cache.match(`/avatars/${name}.jpg`);
                }
            }

            const storage = new Storage();
            storage.getAvatar('jake').then(…);

            // 箭头函数
            const foo = async () => {};

        4.并发与继发

            //继发:耗时
            let foo = await getFoo();
            let bar = await getBar();

            //并发
            let [foo, bar] = await Promise.all([getFoo(), getBar()]);
        


        5.错误处理,使用try...catch捕获

            async function f() {
                return await 123;       //需要return
            }
            f().then(v => console.log(v))

            async function f() {
                await Promise.reject('出错了');   //不需要return也能捕获到错误
            }
            f()
            .then(v => console.log(v))
            .catch(e => console.log(e))

            async function main() {
                try {
                        const val1 = await 1
                        const val2 = await 1;
                        throw new Error('出错了');
                        const val3 = await 3;
                        console.log('Final: ', val3);
                    }
                    catch (err) {
                        console.error(err);
                    }
                    console.log('hello world')
                }
            main()  //Error:出错了
                    //hello world

 

五.虚拟DOM算法原理:

DOM树上的结构,属性信息都可以用JS对象来表示

  1. 状态变更-->重新渲染整个视图,用JS对象表示DOM信息和结构,当状态变更的时候,重新渲染js对象结构。用选渲染的对象树和旧的树进行对比,记录两棵树的差异,记录下来的不同就是我们需要对页面真正的DOM操作,然后把他们应用再真正的DOM树上,页面就变更了,这样,视图的结构确实重新渲染了,但是最后操作DOM的时候只变更有不同的地方,总结如下:
  •  1.1. 用JS对象结构表示DOM树的结构,然后用这个树构建一个真正的DOM树,插到文档当中
  •  1.2. 当状态变更时,重新构造一棵新的对象树,然后用新的树和旧的树进行比较,记录两棵树差异
  •  1.3. 把2所记录的差异应用到步骤1所构建的真正的DOM树,视图就更新了 
  1. diff算法:virtual DOM只会对同一个层级的元素进行对比
  •  2.1.实际操作中,diff会进行深度优先遍历,这样每个节点都会有一个唯一的标记,在进行深度优先遍历的时候,每遍历到一个节点就把该节点和新的树进行对比,如果有差异就记录到一个对象里面。
  •  2.2.列表对比算法重点:对节点进行删除,插入,移动的操作,列表对比时,给子节点加上唯一标识key,使用key进行对比,这样才能复用老的DOM树上的节点
  •  2.3.找到更改的的节点,进行DOM操作

MV*

  1. MVC
    //view model controller
    用户对view操作以后,view捕获到这个操作,会把处理的权利交给controller,controller会对来自view数据进行预处理,决定调用哪个model的接口,然后由model执行相关的业务逻辑。当model变更了以后,会通过观察者模式通知view,view通过观察者模式收到model变更的消息以后,会向model请求最新的数据,然后重新更新界面。JS知识点备忘
  •  1.1view是把控制权交给controller,controller执行应用程序的相关的应用逻辑
  •  1.2.controller操作model,model执行业务逻辑对数据进行处理,但不会直接操作view
  •  1.3.view和model的同步消息是通过观察者模式进行,而同步操作是由view自己请求model的数据然后对视图进行更新
  1. MVP
    //view presenter model
    用户对view的操作会从view移交到presenter,presenter会执行相关的业务逻辑,并且对model层进行操作,model执行完业务逻辑后,也是通过观察者模式把自己变更的消息传递出去。但是是传给presenter而不是view,presenter获取到model变更的消息以后,通过view提供的接口更新页面JS知识点备忘
  •  2.1.view不再负责同步的逻辑,而是由presenter负责,presenter中既有应用程序逻辑也有同步逻辑
  •  2.2.view需要提供操作界面的接口给presenter进行调用
  1. MVVM
    view view viewModel
    MVVM的调用关系和MVP一样。但是,在ViewModel当中会有一个叫Binder,或者是Data-binding engine的东西。以前全部由Presenter负责的View和Model之间数据同步操作交由给Binder处理。你只需要在View的模版语法当中,指令式地声明View上的显示的内容是和Model的哪一块数据绑定的。当ViewModel对进行Model更新的时候,Binder会自动把数据更新到View上去,当用户对View进行操作(例如表单输入),Binder也会自动把数据更新到Model上去。这种方式称为:Two-way data-binding,双向数据绑定。可以简单而不恰当地理解为一个模版引擎,但是会根据数据变更实时渲染。也就是说,MVVM把View和Model的同步逻辑自动化了。以前Presenter负责的View和Model同步不再手动地进行操作,而是交由框架所提供的Binder进行负责。只需要告诉Binder,View显示的数据对应的是Model哪一部分即可JS知识点备忘