TypeScript入门教程 之 classes-emit
What's up with the IIFE
为该类生成的js可能是:
function Point(x, y) { this.x = x; this.y = y; } Point.prototype.add = function (point) { return new Point(this.x + point.x, this.y + point.y); };
它包装在立即调用的函数表达式(IIFE)中的原因,即
(function () { // BODY return Point; })();
与继承有关。它允许TypeScript将基类捕获为变量,_super
例如
var Point3D = (function (_super) { __extends(Point3D, _super); function Point3D(x, y, z) { _super.call(this, x, y); this.z = z; } Point3D.prototype.add = function (point) { var point2D = _super.prototype.add.call(this, point); return new Point3D(point2D.x, point2D.y, this.z + point.z); }; return Point3D; })(Point);
请注意,IIFE允许TypeScript轻松捕获变量中的基类Point
,_super
并且该基类在类主体中始终使用。
__extends
您会注意到,一旦继承了TypeScript类,它也会生成以下函数:
var __extends = this.__extends || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } __.prototype = b.prototype; d.prototype = new __(); };
这里d
是指派生类,b
是指基类。此功能有两件事:
- 将基类的静态成员复制到子类,即
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
- 设置子类函数的原型,以选择性地在父类上查找成员,
proto
即有效d.prototype.__proto__ = b.prototype
人们很少难于理解1,但许多人却与2纠缠不清。
d.prototype.__proto__ = b.prototype
在为许多人辅导之后,我发现以下解释是最简单的。首先,我们将解释from的代码如何__extends
等效于simple d.prototype.__proto__ = b.prototype
,然后解释为什么这一行本身很重要。
要了解所有这些,您需要了解以下内容:
__proto__
prototype
- 效果
new
上this
调用的函数内 - 效果
new
上prototype
和__proto__
JavaScript中的所有对象都包含一个__proto__
成员。在较旧的浏览器中通常无法访问该成员(有时文档将此神奇属性称为[[prototype]]
)。
它有一个目标:如果在查找(例如obj.property
)期间未在对象上找到属性,则将其查找到obj.__proto__.property
。如果仍未找到它,则obj.__proto__.__proto__.property
直到:找到它或最新的值.__proto__
本身为null。这解释了为什么据说JavaScript支持开箱即用的原型继承。
在下面的示例中显示了该示例,您可以在chrome控制台或Node.js中运行它:
var foo = {} // setup on foo as well as foo.__proto__ foo.bar = 123; foo.__proto__.bar = 456; console.log(foo.bar); // 123 delete foo.bar; // remove from object console.log(foo.bar); // 456 delete foo.__proto__.bar; // remove from foo.__proto__ console.log(foo.bar); // undefined
很酷,所以您了解__proto__
。另一个有用的事实是,function
JavaScript 中的所有都具有一个称为的属性prototype
,并且其成员constructor
指向该函数。
如下所示:
function Foo() { } console.log(Foo.prototype); // {} i.e. it exists and is not undefined console.log(Foo.prototype.constructor === Foo); // Has a member called `constructor` pointing back to the function
现在让我们看一下on 在被调用函数内部的效果new
this
。基本上this
,在调用的函数内部将指向将从函数返回的新创建的对象。
很容易看出您是否this
对函数内部的属性进行了突变:
function Foo() { this.bar = 123; } // call with the new operator var newFoo = new Foo(); console.log(newFoo.bar); // 123
现在,你需要知道的唯一的另一件事是调用new
一个函数分配prototype
的函数来的__proto__
是从函数调用返回的新创建的对象的。
这是您可以运行以完全理解它的代码:
function Foo() { } var foo = new Foo(); console.log(foo.__proto__ === Foo.prototype); // True!
现在直接看下面的内容__extends
。我可以自由地为这些行编号:
1 function __() { this.constructor = d; } 2 __.prototype = b.prototype; 3 d.prototype = new __();
以相反的方式读取d.prototype = new __()
第3行上的此函数意味着d.prototype = {__proto__ : __.prototype}
(由于new
on prototype
和的影响__proto__
),将其与__.prototype = b.prototype;
您获得的上一行(即第2行)结合起来d.prototype = {__proto__ : b.prototype}
。
但是,等等,我们只想d.prototype.__proto__
改变原型并保留旧的d.prototype.constructor
。这是第一行(即function __() { this.constructor = d; }
)的含义所在的位置。在这里,我们将有效地使用它d.prototype = {__proto__ : __.prototype, constructor : d}
(因为new
on this
在被调用函数内部的影响)。所以,既然我们恢复d.prototype.constructor
,我们已经真正变异的唯一的事情就是__proto__
因此d.prototype.__proto__ = b.prototype
。
d.prototype.__proto__ = b.prototype
意义
重要的是,它允许您将成员函数添加到子类并从基类继承其他成员函数。下面的简单示例说明了这一点:
function Animal() { } Animal.prototype.walk = function () { console.log('walk') }; function Bird() { } Bird.prototype.__proto__ = Animal.prototype; Bird.prototype.fly = function () { console.log('fly') }; var bird = new Bird(); bird.walk(); bird.fly();
基本上bird.fly
从被查找bird.__proto__.fly
(记住,new
使得bird.__proto__
点Bird.prototype
)和bird.walk
(一种遗传性构件)将被从查找bird.__proto__.__proto__.walk
(如bird.__proto__ == Bird.prototype
和bird.__proto__.__proto__
== Animal.prototype
)。
翻译来源:https://gitee.com/yunwisdoms/typescript-book/blob/master/docs/classes-emit.md