为什么 TypeScript 在 IIFE 中打包 class?
Why does TypeScript pack a class in an IIFE?
这是一个 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;
}
}
当 TS 以 ES5 为目标时,它被转译为 IIFE:
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;
}());
但是,当它作为构造函数出现时,它通常以相同的方式工作。当然,它看起来更像 JavaScript 和手写 :)
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 不接受参数。不能是闭包。
这样做是为了在像这样的边缘情况下保留本机 class 行为,在这种情况下,有人试图在 class Greeter
定义之前使用它:
// this is javascript code, not TypeScript
console.log(Greeter.What());
class Greeter {
}
Greeter.What = function What() {
return "Greater";
}
使用本机 class 实现,这应该打印 ReferenceError: Greeter is not defined
.
在 IIFE 中转译和包装时,结果非常接近:TypeError: Cannot read property 'What' of undefined
。
没有 IIFE,展开的函数是 hoisted 并且名称 Greeter
在定义之前就在范围内,因此会产生不同的错误:TypeError: Greeter.What is not a function
请注意,IIFE 不用于隐藏私有实例或 class 属性,因为无论如何都没有必要。转译时,实例属性被分配为构造函数内 this
的属性,静态属性被分配为 Greeter
对象的属性 - 没有创建变量。
在 classes 之间存在继承的情况下,TypeScript 会将参数传递给 IIFE。例如,当 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));
这是一个 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;
}
}
当 TS 以 ES5 为目标时,它被转译为 IIFE:
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;
}());
但是,当它作为构造函数出现时,它通常以相同的方式工作。当然,它看起来更像 JavaScript 和手写 :)
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 不接受参数。不能是闭包。
这样做是为了在像这样的边缘情况下保留本机 class 行为,在这种情况下,有人试图在 class Greeter
定义之前使用它:
// this is javascript code, not TypeScript
console.log(Greeter.What());
class Greeter {
}
Greeter.What = function What() {
return "Greater";
}
使用本机 class 实现,这应该打印 ReferenceError: Greeter is not defined
.
在 IIFE 中转译和包装时,结果非常接近:TypeError: Cannot read property 'What' of undefined
。
没有 IIFE,展开的函数是 hoisted 并且名称 Greeter
在定义之前就在范围内,因此会产生不同的错误:TypeError: Greeter.What is not a function
请注意,IIFE 不用于隐藏私有实例或 class 属性,因为无论如何都没有必要。转译时,实例属性被分配为构造函数内 this
的属性,静态属性被分配为 Greeter
对象的属性 - 没有创建变量。
在 classes 之间存在继承的情况下,TypeScript 会将参数传递给 IIFE。例如,当 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));