TypeScript中Class继承的JS实现
新建 index.ts 文件,实现 SubType 继承 SuperType。
//index.ts class SuperType { name: String; constructor(name:String){ this.name = name; } getName():String{ return this.name } } class SubType extends SuperType { constructor(name:String){ super(name); } }
将 index.ts 编译为 index.js
//index.js 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 (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var SuperType = (function () { function SuperType(name) { this.name = name; } SuperType.prototype.getName = function () { return this.name; }; return SuperType; }()); var SubType = (function (_super) { __extends(SubType, _super); function SubType(name) { return _super.call(this, name) || this; } return SubType; }(SuperType));
我们首先分析编译后的SuperType, 可以看出SuperType就是一个自执行函数,执行过程中生成构造函数并将原型方法绑定在构造函数上,最终返回构造函数。
/*创建自执行函数*/ var SuperType = (function () { /*构造函数*/ function SuperType(name) { this.name = name; } /*绑定原型方法*/ SuperType.prototype.getName = function () { return this.name; }; /*返回构造函数*/ return SuperType; }());
然后SubType与SuperType有两点不同,多了两个地方。
/*自执行函数,参数_super值为SuperType*/ var SubType = (function (_super) { /*继承函数*/ __extends(SubType, _super); function SubType(name) { /*将父类的实例属性和方法绑定到子类的实例*/ return _super.call(this, name) || this; } return SubType; }(SuperType));
先看 _super.call(this, name);
,这里实际是执行SuperType构造函数并将构造函数的this指向当前创建的SubType实例。
要明白这一点首先要知道在new一个实例的时候发生了什么。
/*当我们new一个实例时*/ var o = new SubType(); /*我们可以理解为*/ /* 1.创建一个空的对象 2.将对象的__proto__指向类的prototype 3.调用构造函数并将构造函数的this指向对象,以此将构造函数上的属性绑定在对象上。 */ var o = {}; o.__proto__ = SubType.prototype; SubType().call(o,name)
因为在执行 SubType().call(o,name)
时 子类构造函数 SubType() 中的 this 已经指向实例 o ,所以当调用 子类中的 _super.call(this, name)
其 this 即指向实例 o,因此便可将父类构造函数 SuperType()中的实例属性和方法继承到了在子类的实例上。
接下来来看 SubType() 下的 __extends()
方法。
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 (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })();
首先判断__extends()
方法是否已经被声明。
/*若在当前环境下已经被声明,则返回this.__extends,否则返回(function(){})(); */ var __extends = (this && this.__extends) || (function () { ... })();
然后看返回的自执行函数 (function(){})()
var __extends = (function () { var extendStatics = function (d, b){...} //返回一个匿名函数 return function (d, b) { //调用闭包中extendStatics()方法 extendStatics(d, b); //定义一个构造函数__() function __() { this.constructor = d; } //判断b是否为空,是的话则 d.prototype = Object.create(b) //否则 d.prototype = (__.prototype = b.prototype, new __()) d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })();
重点看d.prototype = (__.prototype = b.prototype, new __());
d.prototype = (__.prototype = b.prototype, new __()) //其步骤为 //1.将__.prototype指向 b即父类的 prototype __.prototype = b.prototype; //2.实例化一个__() let _o = new __(); //3.将 d即子类的的 prototype 指向 new __() d.prototype = _o; //相当于做了 d.prototype.__proto__ = b.prototype; d.prototype.constructor = d;
以此实现了子类对父类原型方法的继承。
下面来看闭包中extendStatics = function (d, b){...}
的定义
var extendStatics = function (d, b) { //当浏览器支持Object.setPrototypeOf时返回 extendStatics = Object.setPrototypeOf || //当浏览器支持__proto__时返回 ({ __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 extendStatics(d, b);
这一段代码做了兼容性处理,实际在做的就是 d.__proto__ = b
即实现了子类对父类静态方法的继承。
总结
typescript 的继承,实际上相对用javascript实现了
function SubType(){ SuperType().call(this); } SubType.prototype.constructor = SubType; SubType.prototype.__proto__ = SuperType.prototype; SubType.__proto__ = SuperType;
了解了typescript的继承原理后,我们在使用typescript继承时就要注意一些问题,比如深浅拷贝。
PS
在看代码时碰到的小点,在此记录。{ __proto__: [] } instanceof Array
是如何工作的?
个人理解为当浏览器不支持__proto__
时,{ __proto__: [] }
相当于为对象{}
定义了一个值为[]
的__proto__
属性;但当浏览器支持__proto__
时,相当于将{}
的原型指向了一个Array实例。