最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Create ES6ESNext prototype function with different scope (not an inline function) - Stack Overflow

programmeradmin4浏览0评论

Ok say we have this:

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

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

what I want to do is define printName, something like this:

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) 
}

where makePrintName is a functor, something like this:

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

is this possible with ES6? My editor and TypeScript is not liking this

NOTE: using ES5, this was easy to do, and looks like this:

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

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

Using class syntax, currently the best thing that is working for me, is this:

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

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

but that is not ideal. You will see the problem if you try to use class syntax to do what ES5 syntax can do. The ES6 class wrapper, is therefore a leaky abstraction.

To see the real-life use case, see:

.ts#L171

the problem with using TestBlock.prototype.startSuite = ..., is that in that case, I cannot simply return the class on line:

.ts#L67

Ok say we have this:

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

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

what I want to do is define printName, something like this:

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) 
}

where makePrintName is a functor, something like this:

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

is this possible with ES6? My editor and TypeScript is not liking this

NOTE: using ES5, this was easy to do, and looks like this:

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

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

Using class syntax, currently the best thing that is working for me, is this:

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

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

but that is not ideal. You will see the problem if you try to use class syntax to do what ES5 syntax can do. The ES6 class wrapper, is therefore a leaky abstraction.

To see the real-life use case, see:

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

the problem with using TestBlock.prototype.startSuite = ..., is that in that case, I cannot simply return the class on line:

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

Share Improve this question edited Oct 18, 2017 at 19:56 Alexander Mills asked Oct 9, 2017 at 5:50 Alexander MillsAlexander Mills 100k165 gold badges532 silver badges909 bronze badges 6
  • 2 "note that with ES5, this was easy to do" You can do exactly the same thing: ES6 - declare a prototype method on a class with an import statement – Felix Kling Commented Oct 9, 2017 at 16:12
  • yes, but this is a perfect example of ES6 being less dynamic and more rigid than ES5..the technique with the import statement is ES5 syntax not ES6 syntax. – Alexander Mills Commented Oct 9, 2017 at 17:54
  • 2 Uh? Everything that is valid in ES5 is also valid in ES6. If you mean syntax that was introduced in ES6, sure. But there is no way to write a JavaScript program without using syntax that was introduced before ES6... so that argument is kind of silly. – Felix Kling Commented Oct 9, 2017 at 17:56
  • dude I don't know why we are arguing about this....it is a limitation of ES6 classes, plain and simple. If you are on some JS mittee, please fix this. – Alexander Mills Commented Oct 9, 2017 at 17:56
  • "the problem with using TestBlock.prototype.startSuite = ..., is that in that case, I cannot simply return the class on line" But you are not required to put the return statement there. You can do return TestBlock; after the declaration. – Felix Kling Commented Oct 18, 2017 at 20:07
 |  Show 1 more ment

5 Answers 5

Reset to default 10

The preferable ways to do this in JavaScript and TypeScript may differ due to limitations in typing system, but if printName is supposed to be a prototype method and not instance method (the former is beneficial for several reasons), there are not so many options.

Prototype method can be retrieved via accessor. In this case it should be preferably memoized or cached to a variable.

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

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

And it can be lazily evaluated:

let cachedPrintName;

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

Or it can be assigned to class prototype directly. In this case it should be additionally typed as class property because TypeScript ignores prototype assignments:

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

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

Where () => void is the type of a function that makePrintName returns.

A way that is natural to TypeScript is to not modify class prototypes but extend prototype chain and introduce new or modified methods via mixin classes. This introduces unnecessary plexity in JavaScript yet keeps TypeScript happy about types:

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

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

TypeScript decorator cannot be seamlessly used at this point because class mutation is not supported at this moment. The class should be additionally supplemented with interface to suppress type errors:

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

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

Just replace : with =

printName = makePrintName()

: goes for type notation.

Edit
As noted in ments the above will not change the prototype. Instead, you can do this outside the class definition, using ES5 syntax:

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

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

Playground

Another idea I haven't seen mentioned yet is to use a method decorator. The following decorator takes a method implementation and puts it on the prototype, as desired:

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;
    };
}

Put it in a library somewhere. Here's how you'd use it:

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

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

The only downside is that you have to put a dummy implementation of the method with the right signature in the class, since the decorator needs something to decorate. But it behaves exactly as you want at runtime (it isn't an instance method which costs a new function definition for each instance, or an accessor which costs an extra function call at each use).

Does that help?

Why don't you try something like this

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

    }
}

am I dont getting it or ..??

class keyword normally is just a syntax sugar of the delegate prototype in ES5. I just tried it in typescript

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'));
发布评论

评论列表(0)

  1. 暂无评论