Создайте прототип функции ES6/ESNext с разным объемом (не встроенной функцией)

Хорошо, скажем, что у нас есть это:

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

Ответ 1

Еще одна идея, о которой я еще не упоминал, заключается в использовании . Следующий декоратор выполняет реализацию метода и помещает его в прототип по желанию:

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
}

Единственным недостатком является то, что вы должны поместить фиктивную реализацию метода с правильной подписью в класс, поскольку декоратору нужно что-то украсить. Но он работает точно так же, как вы хотите во время выполнения (это не метод экземпляра, который стоит за новое определение функции для каждого экземпляра, или аксессуар, который требует дополнительного вызова функции при каждом использовании).

Помогает ли это?

Ответ 2

Предпочтительные способы сделать это в JavaScript и TypeScript могут отличаться из-за ограничений в системе ввода, но если printName предполагается прототипом, а не методом экземпляра (первый полезен для несколько причин), не так много вариантов.

Метод прототипа может быть извлечен через accessor. В этом случае он должен быть предпочтительно замещен или кэширован переменной.

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

Где () => void - это тип функции, возвращаемой makePrintName.

Естественным способом TypeScript является не модифицировать прототипы классов, а расширять цепочку прототипов и вводить новые или модифицированные методы через классы не поддерживается в данный момент. Класс должен дополнительно дополняться интерфейсом для подавления ошибок типа:

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

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

Ответ 3

Просто замените : на =

printName = makePrintName()

: используется для обозначения типа.

Edit
Как отмечено в комментариях, это не изменит прототип. Вместо этого вы можете сделать это за пределами определения класса, используя синтаксис ES5:

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

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

Игровая площадка

Ответ 4

Почему бы вам не попробовать что-то вроде этого

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

    }
}

Ответ 5

am Я не получаю его или..??

class keyword обычно является синтаксическим сахаром прототипа делегата в ES5. Я просто попробовал это в 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'));