【注:如下TypeScript简称为TS】javascript
咱们从一段实现类继承的代码开始(出自TS):java
var Point3D = (function (_super) {
__extends(Point3D, _super);
function Point3D(x, y, z) {
_super.call(this, x, y);
this.z = z;
} P
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 [Immediately-Invoked Function Expression])git
__extends实现以下(这段代码也是TS里实现类继承的简化版本):typescript
var __extends = this.__extends || function (Child, Parent) {
for (var p in Parent) if (Parent.hasOwnProperty(p)) Child[p] = Parent[p];
function __() { this.constructor = Child; }
__.prototype = Parent.prototype;
Child.prototype = new __();
};
复制代码
for (var p in Parent) if (Parent.hasOwnProperty(p)) Child[p] = Parent[p];
这里的静态属性是指直接用hasOwnProperty可以访问到的属性,而并不是原型链上的属性。Child.prototype.__proto__ = Parent.prototype
,而后为啥一句话就能搞定,可是TS要写3句话,那是由于__proto__这个属性有兼容性问题,有些浏览器叫法不一样,或者有些根本不让你去访问到,但确确实实在其内部存在这么一个东西,接下去咱们先来把Child.prototype.__proto__ = Parent.prototype
这个解释通,而后再来讲下剩下的3行代码首先先来名词解释一下浏览器
__proto__
在JS里,全部实例对象都有一个__proto__(抛开浏览器兼容性,只说概念),做用是:若是访问某个对象的属性obj.abc没有的话,那就会去obj.__proto__
.abc去找,若是仍是没有找到,那就去obj.__proto__
.__proto__
.abc找,以此类推,一直到__proto__为null结束,若是还没找到,那就真的没有了。这个就是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
复制代码
prototype
__proto__
是给实例用的,而prototype
是给函数用的,每一个JS里的function都有个prototype
(这个貌似没有兼容性问题,比较统一),而后prototype
有一个成员叫constructor
,这个constructor
又反过来指向函数本身。代码以下:ui
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
复制代码
this
在函数里指向了当前这个被新建立出来的对象实例,通常咱们会在构造函数里用this
来改一下这个实例的初始值等。this
function Foo() {
this.bar = 123;
}
// call with the new operator
var newFoo = new Foo();
console.log(newFoo.bar); // 123
复制代码
我以为能够这么认为,用了new关键字来调用函数后,不但建立了一个新对象,并且在函数最后强制return了里面的this,从而可以赋值给左边那个变量(newFoo)spa
prototype
和__proto__
作了些啥调用了new以后,会把这个函数的prototype
赋值给这个新对象实例的__proto__
属性。举例:prototype
function Foo() { }
var foo = new Foo();
console.log(foo.__proto__ === Foo.prototype); // True!
复制代码
1 function __() { this.constructor = Child; }
2 __.prototype = Parent.prototype;
3 Child.prototype = new __();
复制代码
咱们倒过来看,
第3行 Child.prototype = new __()
意思实际上是: Child.prototype = {__proto__ : __.prototype}
(上面第4点)
而后结合第2行代码 __.prototype = Parent.prototype
, 其实就变成了 Child.prototype = {__proto__ : Parent.prototype}
到这里,其实咱们已经完成目标Child.prototype.__proto__ = Parent.prototype
而后为了和普通函数拥有相同的定义,参考第1点,咱们须要再复原一下Child.prototype.constructor
,这也就是第一行function __() {this.constructor = Child; }
作的事情,最后就变成了Child.prototype = {__proto__ : Parent.prototype , constructor : Child}
(上面第2点)
而后疑问来了,咱们为啥要复原这个constructor
,其实没有这个constructor
,咱们大部分继承功能都能使用,具体能够参考一下这里
大概理解为,这个玩意只有在你本身手动调用的时候才有用,好比有一个var ball = new Football();
,你用着用着,发现忘了这个ball是Football仍是Basketball构造出来的,你确实又想要一个和ball同类型的,那你能够这样来 var ball2 = ball.constructor();
,这样ball2和ball就同样了,不过好像用不太到就是。。。若是有小伙伴知道更多的理解,麻烦评论给我哈。。。。
这篇文章是看了TypeScript Deep Dive关于TS类转成JS实现那部分以后总结的,若有不对的地方还但愿你们评论给我。