创建具有不同范围的ES6 / ESNext原型函数(不是内联函数)

时间:2017-10-09 05:50:52

标签: javascript typescript ecmascript-6

好的说我们有这个:

class Car {
    constructor(name) {
        this.kind = 'Car';
        this.name = name;
    }

    printName() {
        console.log('this.name');
    }
}

我想要做的是定义printName,如下所示:

class Car {
    constructor(name) {
        this.kind = 'Car';
        this.name = name;
    }

    // we want to define printName using a different scope
    // this syntax is close, but is *not* quite correct
    printName: makePrintName(foo, bar, baz) 
}

其中makePrintName是一个仿函数,如下所示:

exports.makePrintName = function(foo, bar, baz){
   return function(){ ... }
};

这可能与ES6有关吗?我的编辑器和TypeScript不喜欢这个

注意:使用ES5,这很容易做到,看起来像这样:

var Car = function(){...};

Car.prototype.printName = makePrintName(foo, bar, baz);

使用类语法,目前最适合我的方法是:

const printName = makePrintName(foo,bar,baz);

class Car {
  constructor(){...}
  printName(){
    return printName.apply(this,arguments);
  }
}

但这并不理想。 如果您尝试使用类语法来执行ES5语法可以执行的操作,您将看到问题。因此,ES6类包装器是一个漏洞抽象。

要查看真实用例,请参阅:

https://github.com/sumanjs/suman/blob/master/lib/test-suite-helpers/make-test-suite.ts#L171

使用TestBlock.prototype.startSuite = ...的问题是,在这种情况下,我不能简单地在线返回该类:

https://github.com/sumanjs/suman/blob/master/lib/test-suite-helpers/make-test-suite.ts#L67

5 个答案:

答案 0 :(得分:9)

在JavaScript和TypeScript中执行此操作的首选方法可能因键入系统的限制而有所不同,但如果printName应该是原型方法而不是实例方法(前者对{{3}有用) }),没有那么多的选择。

可以通过访问器检索原型方法。在这种情况下,它应该优选地被记忆或缓存到变量。

const cachedPrintName = makePrintName(foo, bar, baz);

class Car {
    ...
    get printName(): () => void {
        return cachedPrintName;
    }
}

它可以被懒惰地评估:

let cachedPrintName;

class Car {
    ...
    get printName(): () => void {
        return cachedPrintName || cachedPrintName = makePrintName(foo, bar, baz);
    }
}

或者可以直接将其分配给班级prototype。在这种情况下,它应该另外输入为类属性,因为TypeScript会忽略prototype赋值:

class Car {
    ...
    printName(): () => void;
}

Car.prototype.printName = makePrintName(foo, bar, baz);

() => voidmakePrintName返回的函数的类型。

TypeScript的一种自然方式是不修改类原型,而是扩展原型链并通过several reasons引入新的或修改过的方法。这会在JavaScript中引入不必要的复杂性,但让TypeScript对类型感到满意:

function makePrintNameMixin(foo, bar, baz){
  return function (Class) {
    return class extends Class {
      printName() {...}
    }
  }
}

const Car = makePrintNameMixin(foo, bar, baz)(
  class Car {
    constructor() {...}
  }
);

此时无法无缝地使用TypeScript装饰器,因为类突变为mixin classes。该类还应补充接口以抑制类型错误:

interface Car {
  printName: () => void;
}

@makePrintNameMixin(foo, bar, baz)
class Car {
    constructor() {...}
}

答案 1 :(得分:4)

只需将:替换为=

即可
printName = makePrintName()

:用于表示法。

修改
如评论中所述,上述内容不会改变原型。相反,您可以使用ES5语法在类定义之外执行此操作:

// workaround: extracting function return type
const dummyPrintName = !true && makePrintName();
type PrintNameType = typeof dummyPrintName;

class Car {
    // ...
    printName: PrintNameType;
}
Car.prototype.printName = makePrintName();

Playground

答案 2 :(得分:2)

我还没有提到的另一个想法是使用method decorator。以下装饰器采用方法实现并根据需要将其放在原型上:

function setMethod<T extends Function>(value: T) {
    return function (target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<T>): void {
      descriptor.value = value;
      delete descriptor.get;
      delete descriptor.set;
    };
}

把它放在某个地方的图书馆里。以下是您如何使用它:

class Car {
    constructor(name) {
        this.kind = 'Car';
        this.name = name;
    }

    @setMethod(makePrintName(foo, bar, baz))
    printName() {} // dummy implementation
}

唯一的缺点是你必须在类中使用正确的签名放置方法的虚拟实现,因为装饰器需要装饰一些东西。但它在运行时的行为完全符合您的要求(它不是一个为每个实例计算新函数定义的实例方法,或者是每次使用时需要额外函数调用的访问器)。

这有帮助吗?

答案 3 :(得分:1)

你为什么不尝试这样的事呢

class Car {
    constructor(name) {
        this.kind = 'Car';
        this.name = name;
        this.printName = makePrintName(foo, bar, baz);

    }
}

答案 4 :(得分:0)

我没得到它还是.. ??

class关键字通常只是ES5中委托原型的语法糖。我刚用打字稿

试了一下
class Car {
private kind: string;
private name : string;
printName :Function;
    constructor(name) {
                this.kind = 'Car';
                this.name = name;
            }

   }
var makePrintName = function (foo, bar, baz) {
    return function () { console.log(foo, bar, baz); };
};
Car.prototype.printName = makePrintName('hello', 'world', 'did you get me');
var bmw = new Car('bmw');
bmw.printName();
console.log(bmw.hasOwnProperty('printName'));