Skip to content

链式调用和生成器函数 #49

Open
@Wscats

Description

@Wscats

链式调用

如果我们经常使用 jQuery ,我们写代码就会有时候这样写,这种就是我们最常用的链式调用

$(ele).show().find(child).hide()

实现这种链式调用的本质是,方法体内返回对象实例自身 this,让下一个对象的属性能够继续获取该对象的其他属性从而一直驱动着程序

var obj = {
    a: 1,
    func: function () {
        this.a += 1;
        return this
    }
}
obj.func().func();
console.log(Obj.a); // 3

但是如果链式调用中出现异步的函数的时候,我们就要用一个数组来进行集中式管理,类似一个队列的顺序规范,下面的 Queue 类就是一个重要的例子,关键点在于数组this.task = []还有next()的出队列的过程,让每一个函数在链式调用的时候进去数组this.task = []里面缓存起来,然后用用next()不断触发下一个函数的执行,这里面涉及数组的几个常用的方法,比如要置顶到队列前排,类似插队的草最就用unshift(),平时的按正常顺序进队列最后排的就用push(),而从头部出队列执行就用shift()

方法 作用
shift() 删除并返回数组的第一个元素
unshift() 向数组的开头添加一个或更多元素,并返回新的长度
push() 向数组的末尾添加一个或更多元素,并返回新的长度
class Queue {
    constructor() {
        this.task = [];
        setTimeout(() => {
            this.next();
        }, 0)
    }
    next() {
        // 先进先出 队列的过程
        let fn = this.task.shift(); // 删除并返回数组的第一个元素
        fn && fn();
    }
    // 置顶执行
    // 任务队列头部添加,然后执行头部
    first(callback, timer) {
        let fn = () => {
            setTimeout(() => {
                callback();
                this.next();
            }, timer * 1000)
        }
        this.task.unshift(fn); // 向数组的开头添加一个或更多元素,并返回新的长度
        return this;
    }
    // 延时执行
    // 任务队列尾部添加,然后执行头部
    time(callback, timer) {
        let fn = () => {
            setTimeout(() => {
                callback()
                this.next();
            }, timer * 1000)
        }
        this.task.push(fn); // 向数组的末尾添加一个或更多元素,并返回新的长度
        return this;
    }
    // 顺序执行
    // 和延时执行其实一样,只是延时会卡在哪里等待过完再往下走
    // 任务队列尾部添加,然后执行头部
    work(callback) {
        let fn = () => {
            callback();
            this.next();
        }
        this.task.push(fn);
        return this;
    }
}
new Queue()
    .first(() => {
        console.log(0)
    }, 2)
    .first(() => {
        console.log(1)
    }, 2)
    .work(() => {
        console.log(2)
    })
    .time(() => {
        console.log(3)
    }, 1)
// 1 0 2 3
// 两个置顶先执行,谁后置顶谁执行

生成器函数

function* 这种声明方式(function关键字后跟一个星号)会定义一个生成器函数(generator function),它返回一个 Generator 对象

function* fn() {
    console.log(1);
    //暂停!
    yield;
    //调用next方法继续执行
    console.log(2);
}
var iter = fn();
iter.next(); //1
iter.next(); //2

生成器函数在执行时能暂停,后面又能从暂停处继续执行,每 next 一次就执行每个 yield 间隔之间的代码,本质就是动态移动指针

  • 函数生成器特点是函数名前面有一个*
  • 通过调用函数生成一个控制器
  • 调用next()方法开始执行函数
  • 遇到yield函数将暂停
  • 再次调用next()继续执行函数

调用next()方法时,如果传入了参数,那么这个参数会作为上一条执行的yield语句的返回

function *gen(){
    yield 10;
    y=yield 'foo';
    yield y;
}

var gen_obj=gen();
console.log(gen_obj.next());// 执行 yield 10,返回 10
console.log(gen_obj.next());// 执行 yield 'foo',返回 'foo'
console.log(gen_obj.next(10));// 将 10 赋给上一条 yield 'foo' 的左值,即执行 y=10,返回 10
console.log(gen_obj.next());// 执行完毕,value 为 undefined,done 为 true

传递参数

function *createIterator() {
    let first = yield 1;
    let second = yield first + 2; // 4 + 2 
                                  // first =4 是next(4)将参数赋给上一条的
    yield second + 3;             // 5 + 3
}

let iterator = createIterator();
console.log(iterator.next());    // "{ value: 1, done: false }"
console.log(iterator.next(4));   // "{ value: 6, done: false }"
console.log(iterator.next(5));   // "{ value: 8, done: false }"
console.log(iterator.next());    // "{ value: undefined, done: true }"

注意显式返回和生成器函数不能当构造器使用

function* yieldAndReturn() {
  yield "Y";
  return "R";//显式返回处,可以观察到 done 也立即变为了 true
  yield "unreachable";// 不会被执行了
}
var gen = yieldAndReturn()
console.log(gen.next()); // { value: "Y", done: false }
console.log(gen.next()); // { value: "R", done: true }
console.log(gen.next()); // { value: undefined, done: true }
function* f() {}
var obj = new f; // throws "TypeError: f is not a constructor"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions