Почему TypeScript упаковывает класс в IIFE?
Вот класс TypeScript:
class Greeter {
public static what(): string {
return "Greater";
}
public subject: string;
constructor(subject: string) {
this.subject = subject;
}
public greet(): string {
return "Hello, " + this.subject;
}
}
Он переносится в IIFE, когда TS нацеливается на ES5:
var Greeter = /** @class */ (function () {
function Greeter(subject) {
this.subject = subject;
}
Greeter.what = function () {
return "Greater";
};
Greeter.prototype.greet = function () {
return "Hello, " + this.subject;
};
return Greeter;
}());
Тем не менее, он обычно работает таким же образом, когда он представлен как функция конструктора. Который, конечно, выглядит более JavaScriptish и рукописный :)
function Greeter(subject) {
this.subject = subject;
}
Greeter.what = function () {
return "Greater";
};
Greeter.prototype.greet = function () {
return "Hello, " + this.subject;
};
Использование:
Оба блока кода работают одинаково:
Greater.what(); // -> "Greater"
var greater = new Greater("World!");
greater.greet(); // -> "Hello, World!
Какова выгода или мотивы, чтобы упаковать его в IIFE?
Я сделал наивный тест:
console.time("Greeter");
for(let i = 0; i < 100000000; i++) {
new Greeter("world" + i);
}
console.timeEnd("Greeter");
Он показал практически одинаковую скорость реализации. Конечно, мы не можем ожидать никакой разницы, потому что IIFE решается только один раз.
Я думал, что, возможно, это из-за закрытия, но IIFE не принимает аргументы. Это не должно быть закрытием.
Ответы
Ответ 1
TypeScript будет передавать аргументы IIFE в тех случаях, когда существует наследование между классами. Например, закрытие ниже используется, когда Greeter
расширяет класс BaseGreeter
:
var Greeter = /** @class */ (function (_super) {
// __extends is added by the TS transpiler to simulate inheritance
__extends(Greeter, _super);
function Greeter(subject) {
var _this = _super.call(this) || this;
_this.subject = subject;
return _this;
}
Greeter.What = function () {
return "Greater";
};
Greeter.prototype.greet = function () {
return "Hello, " + this.subject;
};
return Greeter;
}(BaseGreeter));
Ответ 2
Это сделано для того, чтобы сохранить поведение нативного класса в подобных случаях, когда кто-то пытается использовать класс Greeter
до того, как он определит:
// this is javascript code, not TypeScript
console.log(Greeter.What());
class Greeter {
}
Greeter.What = function What() {
return "Greater";
}
При реализации собственного класса это должно вывести ReferenceError: Greeter is not defined
.
При переносе и переносе в IIFE результат достаточно близок: TypeError: Cannot read property 'What' of undefined
.
Без IIFE, развернутая функция поднимается, и имя Greeter
находится в области видимости, прежде чем оно определено, поэтому возникает другая ошибка TypeError: Greeter.What is not a function
Обратите внимание, что IIFE не используется, чтобы скрыть свойства частного экземпляра или класса, потому что это все равно не нужно. При передаче свойства экземпляра назначаются как свойства для this
внутри конструктора, а статические свойства назначаются как свойства объекта Greeter
- переменные не создаются.