问题
Here is a TypeScript class:
class Greeter {
public static what(): string {
return "Greater";
}
public subject: string;
constructor(subject: string) {
this.subject = subject;
}
public greet(): string {
return "Hello, " + this.subject;
}
}
It is transpiled to IIFE when TS targets 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;
}());
However, it generally works in the same way when it is presented as a constructor function. Which, of course, looks more JavaScriptish and handwritten :)
function Greeter(subject) {
this.subject = subject;
}
Greeter.what = function () {
return "Greater";
};
Greeter.prototype.greet = function () {
return "Hello, " + this.subject;
};
Usage:
Both blocks of code work in the same way:
Greater.what(); // -> "Greater"
var greater = new Greater("World!");
greater.greet(); // -> "Hello, World!
What is the benefit or motives to pack it in IIFE?
I made a naive benchmark:
console.time("Greeter");
for(let i = 0; i < 100000000; i++) {
new Greeter("world" + i);
}
console.timeEnd("Greeter");
It showed virtually the same instantiation speed. Of course, we cannot expect any difference, because the IIFE is resolved only once.
I was thinking that maybe it is because of closure, but the IIFE doesn't take arguments. It must not be a closure.
回答1:
TypeScript will pass arguments to the IIFE in cases where there is inheritance between classes. For example, the closure below is used when Greeter
extends a BaseGreeter
class:
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:
It's done to preserve native class behavior in edge cases like this, where someone tries to use class Greeter
before it's defined:
// this is javascript code, not TypeScript
console.log(Greeter.What());
class Greeter {
}
Greeter.What = function What() {
return "Greater";
}
With native class implementation, this should print ReferenceError: Greeter is not defined
.
When transpiled and wrapped in IIFE, the result is close enough: TypeError: Cannot read property 'What' of undefined
.
Without IIFE, unwrapped function is hoisted and name Greeter
is in scope before it's defined, so the different error is produced: TypeError: Greeter.What is not a function
Note that IIFE is not used to hide private instance or class properties because it's not necessary anyway. When transpiled, instance properties are assigned as properties for this
inside the constructor, and static properties are assigned as properties of Greeter
object - no variables are created.
来源:https://stackoverflow.com/questions/56086411/why-does-typescript-pack-a-class-in-an-iife