详解Typescript中继承的实现

来源:互联网 发布:泛微java怎么样 编辑:程序博客网 时间:2024/06/03 21:17

学习javascript也有一段时间了,打算从今天开始记录自己的一些理解。

此篇文章主要是记录自己对Typescript在编译成es5的代码时如何实现继承的理解。

首先是测试代码


class Sup {    public static staticProp = "静态属性";    public instanceProp = "实例属性";    public foo(): string {        return "原型方法";    }}class Sub extends Sup {    public constructor() {        super();    }}let subObj = new Sub();console.log(Sub.staticProp);console.log(subObj.instanceProp);console.log(subObj.foo()); 

执行结果如下,可以看到,子类成功继承了父类的静态成员和实例成员,其中父类的foo方法在编译成es5代码后是绑定到Sup构造函数的prototype属性上的。

编译出来es5代码如下

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 Sup = (function () {    function Sup() {        this.instanceProp = "实例属性";    }    Sup.prototype.foo = function () {        return "原型方法";    };    return Sup;}());Sup.staticProp = "静态属性";var Sub = (function (_super) {    __extends(Sub, _super);    function Sub() {        return _super.call(this) || this;    }    return Sub;}(Sup));var subObj = new Sub();console.log(Sub.staticProp);console.log(subObj.instanceProp);console.log(subObj.foo());

可以看到继承的实现是通过__extends函数和在子类构造函数里以call的方式调用父类构造函数来实现的,接下来来看看这个__extends函数,这个函数与老版本的编译器编译出来的是有所区别的,下面还是以新版本的编译结果作为说明对象。为了方便注释,对这个函数的书写格式进行了一些调整。

// 首先来看下这个(this && this.__extends) || ...的写法// 在js中,布尔运算可以用于任何类型,运算的结果取决于参与运算的变量被转成布尔型后的对应布尔值,// 当然这句话不说布尔运行的结果一定是布尔值,下面举一个例子// 分别对null和{}调用Boolean()转型函数,结果是false和true// var test = null || {}; test为{},var test = {} && null ; test为null// 如果这段代码是在全局作用域的环境下执行的,this为全局对象,浏览器下即为window对象。// 全局作用域下定义的函数是作为全局对象的方法被创建的,// __extends函数在全局作用域中被第一次声明后,this.__extends即指向__extends函数。// 在存在多个写有继承的TS文件的时候,每个对应的js文件都会有这串代码,// 如果__extends已经作为全局变量的方法被定义过了。// 那么就不再为__extends变量重新指定新的函数对象。// 反之如果__extends函数尚未存在,就将||符号右边的值赋值给__extends变量,// (function(){})();的写法被称为IIFE(立即执行函数表达式)。var __extends = (this && this.__extends) || (function () {        // 从这个函数的名字就可以看到它是实现静态的属性的继承的        // 静态属性就是直接绑定在构造函数这个函数对象上的属性        var extendStatics =            // Object.setPrototypeOf能够接受两个参数,用法如下            // var obj = {},proto = {x:10};            // Object.setPrototypeOf(obj, proto);            // console.log(obj.x); // 10            // 上面代码将proto对象设为obj对象的原型,所以从obj对象可以读取proto对象的属性。            // 具体可以参考阮一峰的ECMAScript 6 入门(http://es6.ruanyifeng.com/#docs/object)            Object.setPrototypeOf ||            // 如果浏览器不支持上面的Object.setPrototypeOf方法,则会跑到这里            // {__proto__: []} instanceof Array用来判断浏览器是否支持__proto__属性,不支持则返回最后一项            ({__proto__: []} instanceof Array && function (d, b) {                // 效果和上面用Object.setPrototypeOf方法是一样的,这边更粗暴一点                d.__proto__ = b;            }) ||            function (d, b) {                // 老版本的编译器对于静态属性的继承是用这一块代码实现的,                // 稍后将贴出老版本编译器生成的代码                // 这块代码是将Sup函数对象的实例方法或者属性浅拷贝给Sub函数对象                // (用hasOwnProperty排除了apply等从原型链上继承过来的方法)                for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];            };        // 这个是真正的__extends函数本体        // 参数d为Sub,b为Sup        return function (d, b) {            // 这个最终返回的匿名函数作为闭包能够访问到外层函数作用域的extendStatics函数            // 闭包的定义:有权访问另一个函数作用域中的变量的函数            // 注意,是这个返回的匿名函数被称为闭包            // 之所以采用这样的方式,一旦extendStatics通过第一次的布尔运算被确定下来,            // 以后每次调用__extends函数的时候就可以用这个确定下来的extendStatics函数了            extendStatics(d, b);            function __() {                // 因为下面要用这个构造函数的实例来重写Sub函数的原型对象,                // 若不重新将原型对象的constructor属性指向Sub函数,                // 则将直接指向Object构造函数,就不能通过constructor确定subObj对象的类型了。                this.constructor = d;            }            // 当让Sub继承null即"class Sub extends null"的时候,            // 会调用这个Object.create函数,            // Object.create(null)会返回一个没有任何属性和方法的对象            // 和用new Object()或对象字面量{}创建对象不同,            // 后者会从Object原型上继承属性和方法。            // "class Sub extends null"即表明Sub.prototype就是它所在原型链的开端            // 接下来讨论extend后面跟的是个的类的时候            // 将__函数的prototype指向Sup.prototype,然后将__函数的实例作为Sub的prototype            // 本质上对Sup.prototype的一个浅拷贝,            // 如果直接将Sup.prototype赋给Sub.prototype也可以实现原型继承            // 但后者会导致在Sub.prototype上挂在属性和方法时影响到Sup。            d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());        };    })();

上面介绍了__extends函数对静态成员和原型上定义的成员的继承的实现。实例属性的继承就比较简单了,只用call的方式调用父类构造函数,此时Sup函数中的this指向新创建的Sub的实例。Sup函数对this添加的成员也成功添加到Sub的实例上了。

最后上一下老版本的ts编译器生成的__extends函数代码。

var __extends = (this && this.__extends) || function (d, b) {    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];    function __() { this.constructor = d; }    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());};
原创粉丝点击