如何在内部生成器函数中访问 super class 方法?

How to access super class method inside of internal generator function?

看看这个 类,Base 和 Derived,很简单 类 有 "name" 作为 属性:

class Base {
    constructor(name) {
        this.name = name;
    }
    printName() {
        console.log("Base: " + this.name);
    }
}

class Derieved extends Base {
    constructor(name) {
        super(name);
    }
    // Override
    printName() {
        // IIFE.
        (function() {
            super.printName();  // Can't use super here
        })();

        console.log("Derived: " + this.name);
    }
}

var obj = new Derieved('John');
obj.printName();

我想从 Derieved::printName 调用 Base::printName。但是由于某些原因,我必须在 Derieved::printName.

的内部函数内部调用

但是 运行 上面的代码,它失败了:

SyntaxError: 'super' keyword unexpected here

如果我将父方法的引用保存到变量,看起来它可以调用但不能访问任何属性,它说未定义。

TypeError: Cannot read property 'name' of undefined

我刚刚写的内部函数只是普通函数,但实际上它是生成器函数,所以我不能使用箭头函数:

get(match: T, options: IQueryOptions|void): Promise<Array<Object>|Error> {
    const superGet = super.get;

    return new Promise((resolve, reject) => {
        co(function*() {
            try {
                    // I need to invoke parent's get method here!!
                const accounts = yield superGet(match, options);

                ... // do something with accounts

                resolve(accounts);
            }
            catch(err) {
                ...
            }
        });
    });
}

有办法吗?为什么我不能将父方法的引用保存到变量中?

箭头函数来拯救!

class Base {
    constructor(name) {
        this.name = name;
    }
    printName() {
        console.log("Base: " + this.name);
    }
}

class Derieved extends Base {
    constructor(name) {
        super(name);
    }
    // Override
    printName() {
        // IIFE
        (() => {
            super.printName();  // Can't use super here
        })();

        console.log("Derived: " + this.name);
    }
}

var obj = new Derieved('John');
obj.printName();

基本上,箭头函数维护 thissuper 上下文,这与文字 function 关键字

不同

super 只能从子方法访问,不能从该方法内部调用的函数范围访问。

生成器函数仍然是函数并且支持绑定。这将导致无法通过其签名将绑定函数识别为生成器函数,但只要协程库支持通用迭代器(co 支持),就可以了。

所以基本上是

get(...) {
    const superGet = super.get;

    return new Promise((resolve, reject) => {
        co(function*() {
              ...
              const accounts = yield superGet.call(this, match, options);
              ...
        }.bind(this));
    });
}

甚至更好:

get(...) {
    const superGet = super.get.bind(this);

    return new Promise((resolve, reject) => {
        co(function*() {
              ...
              const accounts = yield superGet(match, options);
              ...
        });
    });
}

这里有几处可以改进。第一个是它使用 promise 构造函数反模式。 co 已经 returns 一个承诺,没有必要用 new Promise.

包装它

还有一点是为了无缝继承,将生成器方法和promisified方法分开是有好处的。 co 支持委派 yields,它使这更容易:

class Base {
  get(...args) {
    return co(this._get.bind(this, ...args));
  }

  * _get(...) { ... }
}

class Child extends Base {
  * _get(...) {
    ...
    const accounts = yield* super._get(match, options);
    ...
  }
}

TypeScript 和 Babel 都支持 ES2017 async..await,并且能够在 ES6 目标输出中回退到类似于 co 的生成器协程。这使得 co 在 transpiled JS 项目中有效无用,上面的代码变为:

class Child extends Base {
  async get(...) {
    ...
    const accounts = await super.get(match, options);
    ...
  }
}

显然您无论如何都为类型注释使用了一些编译器。在这种情况下,解决方案是只删除 co 库和生成器,并使用适当的现代 async/await 语法代替,TypeScript、Babel 和本机 nodejs 支持:

async get(match: T, options: IQueryOptions|void): Promise<Array<Object>|Error> {
    try {
        const accounts = await super.get(match, options);
        … // do something
        return accounts;
    }
    catch(err) {
        …
    }
}

super 在这里开箱即用。