最近在学习TypeScript,也不知道看的是不是官方文档。本文会根据文档给出的例子和说明展开。
文档中的示例如下:
class MsgError extends Error {
constructor(m: string) {
super(m);
// Set the prototype explicitly.
Object.setPrototypeOf(this, MsgError.prototype);
}
sayHello() {
return "hello " + this.message;
}
}
现在我们尝试把这段代码编译成es5的js代码。
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
// 继承静态属性和方法,如果Object.setPrototypeOf和__proto__都不存在,会把基类上的属性复制到派生类上
extendStatics(d, b);
/*
将构造函数原型的constructor属性设置为构造函数本身。通过检查对象实例的constructor属性,
我们可以确定对象实例是由哪个构造函数创建的。比如instanceof运算符,它会沿着对象实例的原型
链向上查找,判断对象实例的原型链上是否存在指定构造函数的原型。
本例中: MsgError.prototype.constructor === MsgError
*/
function __() { this.constructor = d; }
/*
如果 b 是 null,将派生类的原型对象设置为一个空对象,且原型指向null
否则,d.prototype.__proto__ = b.prototype
ps: (__.prototype = b.prototype, new __())使用了逗号运算符,它可以在一行代码中执行多
个表达式,并返回最后一个表达式的值。
*/
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var MsgError = /** @class */ (function (_super) {
__extends(MsgError, _super);
function MsgError(m) {
var _this = _super.call(this, m) || this;
// Set the prototype explicitly.
Object.setPrototypeOf(_this, MsgError.prototype);
return _this;
}
MsgError.prototype.sayHello = function () {
return "hello " + this.message;
};
return MsgError;
}(Error));
接下来我们按文档说明逐行解析:
翻译:返回对象的构造函数隐式地将this的值替换为super(...)的任何调用者。生成的构造函数代码必须捕获super(...)的任何潜在返回值并将其替换为this。
解释:这在示例中体现为"var _this = _super.call(this, m) || this;"。
翻译:因此,Error、Array和其他JavaScript的原生构造函数的子类可能不再按预期工作。这是因为Error、Array等的构造函数使用es6的new.target来调整原型链。但是,在es5中调用构造函数时,没有办法确保new.target的值。默认情况下,其他的下级编译器具有相同的限制。
如果不设置"Object.setPrototypeOf(this, MsgError.prototype);",你可能会发现:
- 这些子类的构造函数返回的对象的方法可能是undefined,所以调用sayHello会报错。
- instanceof会在子类的实例与子类之间断开。(new MsgError()) instanceof MsgError将会返回false。
解释:在es5中,原生的构造函数的原型属性是不可配置的。以new关键字调用Error的构造函数时,虽然更改了this的指向,但内部的new.target始终等于Error,因此返回的实例对象_this的__proto__属性指向的是Error的原型。
翻译:您可以在任何super(...)调用后立即手动调整原型。MsgError的任何子类也必须手动设置原型。对于不支持Object.setPrototypeOf的运行时,你可以使用__proto__替代。
解释:在示例中通过"Object.setPrototypeOf(_this, MsgError.prototype);"手动调整原型。对于所有直接或间接继承于Error的类都是不可配置的。如果MsgError的子类创建了实例且没有手动调整原型,它的__proto__属性指向的是MsgError的原型。