了解 typescript 生成的 __extends 函数?
Understanding the __extends function generated by typescript?
我正在玩 Typescript 并试图理解编译器生成的已编译 Javascript 代码
打字稿代码:
class A { }
class B extends A { }
生成的 Javascript 代码:
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var A = /** @class */ (function () {
function A() {
}
return A;
}());
var B = /** @class */ (function (_super) {
__extends(B, _super);
function B() {
return _super !== null && _super.apply(this, arguments) || this;
}
return B;
}(A));
根据 Mozilla docs 的 Javascript 继承是这样的:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
Typescript生成的代码中我不理解的部分是这样的
1。这条线的目的是什么?貌似是把A的所有key都复制到B里了?这是对静态属性的某种破解吗?
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
2。这是在做什么?
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
我不明白这部分:(__.prototype = b.prototype, new __())
为什么函数 B() return 这个?
return _super !== null && _super.apply(this, arguments) || this;
如果有人能逐行向我解释一下,我将不胜感激。
我自己对此很好奇,无法快速找到答案,所以这是我的细分:
它的作用
__extends 是在面向对象语言中模拟单个 class 继承的函数,return 是派生函数的新构造函数可以创建从基础对象继承的对象。
注一:
我自己并没有真正意识到这一点,但是如果你做了类似下面的事情,其中所有的值都是真实的,那么变量被设置为被测试的最后一项的值,除非一个是假的,在这种情况下变量设置为假:
// value1 is a function with the definition function() {}
var value1 = true && true && function() {};
// value2 is false
var value2 = true && false && function() {};
// value3 is true
var value3 = true && function() {} && true;
我提到这个是因为这是我看到这个 javascript 时最让我困惑的事情,它在 __extends[=186 中使用了几次=] 函数定义.
注二:
参数d(可能代表derived)和b(可能代表base)都是构造函数,不是实例对象。
注3:
prototype
是函数的属性,是'constructor'函数使用的原型对象(即使用new <function name>()
创建的对象)。
当您使用 new
运算符构造新对象时,新对象的内部 [[PROTOTYPE]]
又名 __proto__
被设置为函数的原型 属性。
function Person() {
}
// Construct new object
var p = new Person();
// true
console.log(p.__proto__ === Person.prototype);
// true
console.log(Person.prototype.__proto__ === Object.prototype);
这不是副本。它是对象。
当您创建像
这样的文字对象时
var o = {};
// true
console.log(o.__proto__ === Object.prototype);
新对象的__proto__
设置为Object.prototype
(内置对象构造函数)
您可以将对象的 __prototype__
设置为另一个对象,但是使用 Object.create
。
当在当前对象上找不到 属性 或方法时,将检查对象的 [[PROTOTYPE]]
。如果未找到,则检查该对象的原型。因此它会检查原型,直到到达最终的原型对象 Object.prototype
。请记住,没有什么是副本。
注4
在 Javascript 中模拟继承时,设置了 'constructor' 函数的原型。
function Girl() {
}
Girl.prototype = Object.create(Person.prototype);
// true
console.log(Girl.prototype.__proto__ === Person.prototype);
// true
console.log(Girl.constructor === Function);
// Best practices say reset the constructor to be itself
Girl.constructor = Girl;
// points to Girl function
console.log(Girl.constructor);
注意我们如何将构造函数指向 Girl,因为 Person 的构造函数指向内置 Function
。
您可以在以下位置查看上面的代码:http://jsbin.com/dutojo/1/edit?js,console
原文:
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
细分:
var __extends = (this && this.__extends) || (function () {
// gobbledygook
})();
记住上面的 注 1,第一部分(和结尾)是创建一个名为 __extends 的变量有意持有一个函数来设置派生的原型class。
(this && this.__extends)
正在做我的 注释 1 解释的事情。如果 this 为真且 this.__extends 为真则变量 __extends已经存在,因此设置为其自身的现有实例。如果不是,则设置为 || 之后的内容这是一个 iife(立即调用的函数表达式)。
现在是官方文档,它是 __extends:
的实际定义
var extendStatics = Object.setPrototypeOf ||
名为 extendStatics 的变量被设置为脚本在 运行 中的环境的内置 Object.setPrototypeOf 函数(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf)
或
它创建自己的版本
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
在 注释 3 中,我讨论了 __proto__
又名 [[PROTOTYPE]]
以及如何设置它。代码
{ __proto__: [] } instanceof Array
是一个测试,用于确定当前环境是否允许设置此 属性,方法是将文字对象的 __proto__
集与带有 Array 内置函数的文字数组进行比较。
回到我上面的 注 1 并记住 javascript instanceof 运算符 return 是真还是假(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) 如果环境评估一个对象,其原型 属性 设置为内置数组,则 extendsStatics 设置为
function (d, b) { d.__proto__ = b; })
如果环境不以这种方式评估它,则将 extendStatics 设置为:
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }
之所以这样做是因为 __proto__
从来没有成为官方 ECMAScript 标准的一部分,直到 ECMAScript 2015(并且根据 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto)只是为了向后兼容)。如果支持,则使用 __proto__
函数,否则它使用 'roll your own' 版本',该版本为用户定义的属性从对象 b 复制到 d。
现在定义了 extendStatics 函数变量,一个函数调用 extendStatics 中的任何内容(以及其他一些东西)是return编辑。请注意,参数 'd' 是子 class(继承的),'b' 是超 class(继承自的):
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
分解调用 extendStatics,第一个参数对象 (d) 的原型设置为 (b)(回忆上面的 注 3):
extendStatics(d, b);
在下一行中,声明了一个名为“__”的构造函数,该函数将其构造函数指定为派生的 (d) 构造函数:
function __() { this.constructor = d; }
如果基础 (b) constructor
函数恰好为空,这将确保派生将保留其自己的 prototype
.
来自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor,Object.prototype.constructor(所有对象都是javascript中的对象):
Returns a reference to the Object constructor function that created
the instance object. Note that the value of this property is a
reference to the function itself, not a string containing the
function's name.
和
All objects will have a constructor property. Objects created without
the explicit use of a constructor function (i.e. the object and array
literals) will have a constructor property that points to the
Fundamental Object constructor type for that object.
因此,如果 constructor
函数“__”按原样新建,它将创建一个派生对象。
最后是这一行:
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
如果基础 constructor
函数恰好为 null
,则将派生 (d) 的 prototype
设置为新的空对象
// b is null here so creates {}
Object.create(b)
或
将 __ constructor
函数的 prototype
设置为基础 class prototype
然后调用 __() 其具有设置派生函数的效果 constructor
是派生函数。
(__.prototype = b.prototype, new __()
所以基本上最终函数 returned 创建了一个派生的构造函数,它的原型继承自基本构造函数。
为什么函数 B() return 是这样的?
return _super !== null && _super.apply(this, arguments) || this;
根据:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
The apply() method calls a function with a given this value, and
arguments provided as an array (or an array-like object).
记住 B 是一个构造函数,这就是 returnB 定义中的内容。
如果你有一个 Person class(构造函数),它在构造函数中接受了一个名字参数,你就可以调用一个派生的 Girl class(构造函数),将女孩的名字作为参数.
// Base constructor function
function Person(n) {
// Set parameter n to the name property of a Person
this.name = n;
}
function Girl() {
// Call the Person function with same arguments passed to new Girl
Person.apply(this, arguments);
// Set it so all Girl objects created inherit properties and methods from Person
Girl.prototype = Object.create(Person.prototype);
// Make sure the constructor is not set to Person
Girl.prototype.constructor = Girl;
}
var p = new Person("Sally");
var g = new Girl("Trudy");
console.log(p.name);
console.log(g.name);
这有助于理解 TypeScript class 扩展器中真正发生的事情。事实上,下面的代码包含完全相同的逻辑,因为人们甚至可以将它用作原始代码的替代品,而不需要所有使其极其难以阅读的特殊技巧。
- 前半部分只尝试为'setPrototypeOf'
寻找浏览器兼容版本
- 后半部分从基类实现继承class
尝试用以下代码替换 TypeScript __extends 函数:
// refactored version of __extends for better readability
if(!(this && this.__extends)) // skip if already exists
{
var __extends = function(derived_cl, base_cl) // main function
{
// find browser compatible substitute for implementing 'setPrototypeOf'
if(Object.setPrototypeOf) // first try
Object.setPrototypeOf(derived_cl, base_cl);
else if ({ __proto__: [] } instanceof Array) // second try
derived_cl.__proto__ = base_cl;
else // third try
for (var p in base_cl)
if (base_cl.hasOwnProperty(p)) derived_cl[p] = derived_cl[p];
// construct the derived class
if(base_cl === null)
Object.create(base_cl) // create empty base class if null
else
{
var deriver = function(){} // prepare derived object
deriver.constructor = derived_cl; // get constructor from derived class
deriver.prototype = base_cl.prototype; // get prototype from base class
derived_cl.prototype = new deriver(); // construct the derived class
}
}
}
在接下来的版本中,所有使其通用的东西都被去掉了,例如浏览器兼容性处理和 'null' 衍生产品。我不鼓励任何人将以下代码作为永久替代品,但下面的版本确实展示了 class 继承如何与 TypeScript 一起工作的基本本质。
// Barebone version of __extends for best comprehension
var __extends = function(derived_cl,base_cl)
{
Object.setPrototypeOf(derived_cl,base_cl);
var deriver = function(){} // prepare derived object
deriver.constructor = derived_cl; // get constructor from derived class
deriver.prototype = base_cl.prototype; // get prototype from base class
derived_cl.prototype = new deriver(); // construct derived class
}
尝试以下工作示例:
var __extends = function(derived_cl,base_cl)
{
Object.setPrototypeOf(derived_cl,base_cl);
var deriver = function(){} // prepare derived object
deriver.constructor = derived_cl; // get constructor from derived class
deriver.prototype = base_cl.prototype; // get prototype from base class
derived_cl.prototype = new deriver(); // construct derived class
}
// define the base class, and another class that is derived from base
var Base = function()
{
this.method1 = function() { return "replace the batteries" }
this.method2 = function() { return "recharge the batteries" }
}
var Derived = function(_super) {
function Derived() {
__extends(this, _super); _super.apply(this, arguments);
this.method3 = function() { return "reverse the batteries" }
this.method4 = function() { return "read the damn manual" }
}
return Derived
}(Base)
// Let's do some testing: create the objects and call their methods
var oBase = new Base(); // create the base object
var oDerived = new Derived(); // create the derived object
console.log(oDerived.method2()); // result: 'recharge the batteries'
console.log(oDerived.method4()); // result: 'read the damn manual'
console.log(oBase.method1()) ; // result: 'replace the batteries'
try{ console.log(oBase.method3()) }
catch(e) {console.log(e.message)}; // result: 'oBase.method3 is not a function'
最后,当你厌倦了学习 TypeScript 的混淆继承机制时,我发现 __extend 功能甚至不是必需的,只需 让原生 JavaScript 函数 'apply' 完成工作 ,它通过原型链接准确地实现了我们的目标继承机制。
尝试最后一个例子...忘记其他一切,还是我错过了什么?
// Short, readable, explainable, understandable, ...
// probably a 'best practice' for JavaScript inheritance !
var Base = function()
{
this.method1 = function() { return "replace the batteries" }
this.method2 = function() { return "recharge the batteries" }
}
var Derived = function(){
Base.apply(this, arguments); // Here we inherit all methods from Base!
this.method3 = function() { return "reverse the batteries" }
this.method4 = function() { return "read the damn manual" }
}
var oDerived = new Derived(); // create the derived object
console.log(oDerived.method2()); // result: 'recharge the batteries'
我正在玩 Typescript 并试图理解编译器生成的已编译 Javascript 代码
打字稿代码:
class A { }
class B extends A { }
生成的 Javascript 代码:
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var A = /** @class */ (function () {
function A() {
}
return A;
}());
var B = /** @class */ (function (_super) {
__extends(B, _super);
function B() {
return _super !== null && _super.apply(this, arguments) || this;
}
return B;
}(A));
根据 Mozilla docs 的 Javascript 继承是这样的:
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
Typescript生成的代码中我不理解的部分是这样的
1。这条线的目的是什么?貌似是把A的所有key都复制到B里了?这是对静态属性的某种破解吗?
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
2。这是在做什么?
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
我不明白这部分:(__.prototype = b.prototype, new __())
为什么函数 B() return 这个?
return _super !== null && _super.apply(this, arguments) || this;
如果有人能逐行向我解释一下,我将不胜感激。
我自己对此很好奇,无法快速找到答案,所以这是我的细分:
它的作用
__extends 是在面向对象语言中模拟单个 class 继承的函数,return 是派生函数的新构造函数可以创建从基础对象继承的对象。
注一:
我自己并没有真正意识到这一点,但是如果你做了类似下面的事情,其中所有的值都是真实的,那么变量被设置为被测试的最后一项的值,除非一个是假的,在这种情况下变量设置为假:
// value1 is a function with the definition function() {}
var value1 = true && true && function() {};
// value2 is false
var value2 = true && false && function() {};
// value3 is true
var value3 = true && function() {} && true;
我提到这个是因为这是我看到这个 javascript 时最让我困惑的事情,它在 __extends[=186 中使用了几次=] 函数定义.
注二: 参数d(可能代表derived)和b(可能代表base)都是构造函数,不是实例对象。
注3:
prototype
是函数的属性,是'constructor'函数使用的原型对象(即使用new <function name>()
创建的对象)。
当您使用 new
运算符构造新对象时,新对象的内部 [[PROTOTYPE]]
又名 __proto__
被设置为函数的原型 属性。
function Person() {
}
// Construct new object
var p = new Person();
// true
console.log(p.__proto__ === Person.prototype);
// true
console.log(Person.prototype.__proto__ === Object.prototype);
这不是副本。它是对象。
当您创建像
这样的文字对象时var o = {};
// true
console.log(o.__proto__ === Object.prototype);
新对象的__proto__
设置为Object.prototype
(内置对象构造函数)
您可以将对象的 __prototype__
设置为另一个对象,但是使用 Object.create
。
当在当前对象上找不到 属性 或方法时,将检查对象的 [[PROTOTYPE]]
。如果未找到,则检查该对象的原型。因此它会检查原型,直到到达最终的原型对象 Object.prototype
。请记住,没有什么是副本。
注4 在 Javascript 中模拟继承时,设置了 'constructor' 函数的原型。
function Girl() {
}
Girl.prototype = Object.create(Person.prototype);
// true
console.log(Girl.prototype.__proto__ === Person.prototype);
// true
console.log(Girl.constructor === Function);
// Best practices say reset the constructor to be itself
Girl.constructor = Girl;
// points to Girl function
console.log(Girl.constructor);
注意我们如何将构造函数指向 Girl,因为 Person 的构造函数指向内置 Function
。
您可以在以下位置查看上面的代码:http://jsbin.com/dutojo/1/edit?js,console
原文:
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
细分:
var __extends = (this && this.__extends) || (function () {
// gobbledygook
})();
记住上面的 注 1,第一部分(和结尾)是创建一个名为 __extends 的变量有意持有一个函数来设置派生的原型class。
(this && this.__extends)
正在做我的 注释 1 解释的事情。如果 this 为真且 this.__extends 为真则变量 __extends已经存在,因此设置为其自身的现有实例。如果不是,则设置为 || 之后的内容这是一个 iife(立即调用的函数表达式)。
现在是官方文档,它是 __extends:
的实际定义var extendStatics = Object.setPrototypeOf ||
名为 extendStatics 的变量被设置为脚本在 运行 中的环境的内置 Object.setPrototypeOf 函数(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf)
或
它创建自己的版本
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
在 注释 3 中,我讨论了 __proto__
又名 [[PROTOTYPE]]
以及如何设置它。代码
{ __proto__: [] } instanceof Array
是一个测试,用于确定当前环境是否允许设置此 属性,方法是将文字对象的 __proto__
集与带有 Array 内置函数的文字数组进行比较。
回到我上面的 注 1 并记住 javascript instanceof 运算符 return 是真还是假(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) 如果环境评估一个对象,其原型 属性 设置为内置数组,则 extendsStatics 设置为
function (d, b) { d.__proto__ = b; })
如果环境不以这种方式评估它,则将 extendStatics 设置为:
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }
之所以这样做是因为 __proto__
从来没有成为官方 ECMAScript 标准的一部分,直到 ECMAScript 2015(并且根据 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto)只是为了向后兼容)。如果支持,则使用 __proto__
函数,否则它使用 'roll your own' 版本',该版本为用户定义的属性从对象 b 复制到 d。
现在定义了 extendStatics 函数变量,一个函数调用 extendStatics 中的任何内容(以及其他一些东西)是return编辑。请注意,参数 'd' 是子 class(继承的),'b' 是超 class(继承自的):
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
分解调用 extendStatics,第一个参数对象 (d) 的原型设置为 (b)(回忆上面的 注 3):
extendStatics(d, b);
在下一行中,声明了一个名为“__”的构造函数,该函数将其构造函数指定为派生的 (d) 构造函数:
function __() { this.constructor = d; }
如果基础 (b) constructor
函数恰好为空,这将确保派生将保留其自己的 prototype
.
来自https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor,Object.prototype.constructor(所有对象都是javascript中的对象):
Returns a reference to the Object constructor function that created the instance object. Note that the value of this property is a reference to the function itself, not a string containing the function's name.
和
All objects will have a constructor property. Objects created without the explicit use of a constructor function (i.e. the object and array literals) will have a constructor property that points to the Fundamental Object constructor type for that object.
因此,如果 constructor
函数“__”按原样新建,它将创建一个派生对象。
最后是这一行:
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
如果基础 constructor
函数恰好为 null
prototype
设置为新的空对象
// b is null here so creates {}
Object.create(b)
或
将 __ constructor
函数的 prototype
设置为基础 class prototype
然后调用 __() 其具有设置派生函数的效果 constructor
是派生函数。
(__.prototype = b.prototype, new __()
所以基本上最终函数 returned 创建了一个派生的构造函数,它的原型继承自基本构造函数。
为什么函数 B() return 是这样的?
return _super !== null && _super.apply(this, arguments) || this;
根据:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
The apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object).
记住 B 是一个构造函数,这就是 returnB 定义中的内容。
如果你有一个 Person class(构造函数),它在构造函数中接受了一个名字参数,你就可以调用一个派生的 Girl class(构造函数),将女孩的名字作为参数.
// Base constructor function
function Person(n) {
// Set parameter n to the name property of a Person
this.name = n;
}
function Girl() {
// Call the Person function with same arguments passed to new Girl
Person.apply(this, arguments);
// Set it so all Girl objects created inherit properties and methods from Person
Girl.prototype = Object.create(Person.prototype);
// Make sure the constructor is not set to Person
Girl.prototype.constructor = Girl;
}
var p = new Person("Sally");
var g = new Girl("Trudy");
console.log(p.name);
console.log(g.name);
这有助于理解 TypeScript class 扩展器中真正发生的事情。事实上,下面的代码包含完全相同的逻辑,因为人们甚至可以将它用作原始代码的替代品,而不需要所有使其极其难以阅读的特殊技巧。
- 前半部分只尝试为'setPrototypeOf' 寻找浏览器兼容版本
- 后半部分从基类实现继承class
尝试用以下代码替换 TypeScript __extends 函数:
// refactored version of __extends for better readability
if(!(this && this.__extends)) // skip if already exists
{
var __extends = function(derived_cl, base_cl) // main function
{
// find browser compatible substitute for implementing 'setPrototypeOf'
if(Object.setPrototypeOf) // first try
Object.setPrototypeOf(derived_cl, base_cl);
else if ({ __proto__: [] } instanceof Array) // second try
derived_cl.__proto__ = base_cl;
else // third try
for (var p in base_cl)
if (base_cl.hasOwnProperty(p)) derived_cl[p] = derived_cl[p];
// construct the derived class
if(base_cl === null)
Object.create(base_cl) // create empty base class if null
else
{
var deriver = function(){} // prepare derived object
deriver.constructor = derived_cl; // get constructor from derived class
deriver.prototype = base_cl.prototype; // get prototype from base class
derived_cl.prototype = new deriver(); // construct the derived class
}
}
}
在接下来的版本中,所有使其通用的东西都被去掉了,例如浏览器兼容性处理和 'null' 衍生产品。我不鼓励任何人将以下代码作为永久替代品,但下面的版本确实展示了 class 继承如何与 TypeScript 一起工作的基本本质。
// Barebone version of __extends for best comprehension
var __extends = function(derived_cl,base_cl)
{
Object.setPrototypeOf(derived_cl,base_cl);
var deriver = function(){} // prepare derived object
deriver.constructor = derived_cl; // get constructor from derived class
deriver.prototype = base_cl.prototype; // get prototype from base class
derived_cl.prototype = new deriver(); // construct derived class
}
尝试以下工作示例:
var __extends = function(derived_cl,base_cl)
{
Object.setPrototypeOf(derived_cl,base_cl);
var deriver = function(){} // prepare derived object
deriver.constructor = derived_cl; // get constructor from derived class
deriver.prototype = base_cl.prototype; // get prototype from base class
derived_cl.prototype = new deriver(); // construct derived class
}
// define the base class, and another class that is derived from base
var Base = function()
{
this.method1 = function() { return "replace the batteries" }
this.method2 = function() { return "recharge the batteries" }
}
var Derived = function(_super) {
function Derived() {
__extends(this, _super); _super.apply(this, arguments);
this.method3 = function() { return "reverse the batteries" }
this.method4 = function() { return "read the damn manual" }
}
return Derived
}(Base)
// Let's do some testing: create the objects and call their methods
var oBase = new Base(); // create the base object
var oDerived = new Derived(); // create the derived object
console.log(oDerived.method2()); // result: 'recharge the batteries'
console.log(oDerived.method4()); // result: 'read the damn manual'
console.log(oBase.method1()) ; // result: 'replace the batteries'
try{ console.log(oBase.method3()) }
catch(e) {console.log(e.message)}; // result: 'oBase.method3 is not a function'
最后,当你厌倦了学习 TypeScript 的混淆继承机制时,我发现 __extend 功能甚至不是必需的,只需 让原生 JavaScript 函数 'apply' 完成工作 ,它通过原型链接准确地实现了我们的目标继承机制。
尝试最后一个例子...忘记其他一切,还是我错过了什么?
// Short, readable, explainable, understandable, ...
// probably a 'best practice' for JavaScript inheritance !
var Base = function()
{
this.method1 = function() { return "replace the batteries" }
this.method2 = function() { return "recharge the batteries" }
}
var Derived = function(){
Base.apply(this, arguments); // Here we inherit all methods from Base!
this.method3 = function() { return "reverse the batteries" }
this.method4 = function() { return "read the damn manual" }
}
var oDerived = new Derived(); // create the derived object
console.log(oDerived.method2()); // result: 'recharge the batteries'