TypeScript高级用法的知识点汇总

作为一门强大的静态类型检查工具,如今在许多中大型应用程序以及流行的JS库中均能看到TypeScript的身影。JS作为一门弱类型语言,在我们写代码的过程中稍不留神便会修改掉变量的类型,从而导致一些出乎意料的运行时错误。然而TypeScript在编译过程中便能帮我们解决这个难题,不仅在JS中引入了强类型检查,并且编译后的JS代码能够运行在任何浏览器环境,Node环境和任何支持ECMAScript 3(或更高版本)的JS引擎中。最近公司刚好准备使用TypeScript来对现有系统进行重构,以前使用TypeScript的机会也不多,特别是一些有用的高级用法,所以借着这次机会,重新巩固夯实一下这方面的知识点,如果有错误的地方,还请指出。

1、类继承

在ES5中,我们一般通过函数或者基于原型的继承来封装一些组件公共的部分方便复用,然而在TypeScript中,我们可以像类似Java语言中以面向对象的方式使用类继承来创建可复用的组件。我们可以通过class关键字来创建类,并基于它使用new操作符来实例化一个对象。为了将多个类的公共部分进行抽象,我们可以创建一个父类并让子类通过extends关键字来继承父类,从而减少一些冗余代码的编写增加代码的可复用性和可维护性。示例如下:

class Parent { readonly x: number; constructor() { this.x = 1; } print() { console.log(this.x); } } class Child extends Parent { readonly y: number; constructor() { // 注意此处必须优先调用super()方法 super(); this.y = 2; } print() { // 通过super调用父类原型上的方法,但是方法中的this指向的是子类的实例 super.print(); console.log(this.y); } } const child = new Child(); console.log(child.print()) // -> 1 2

在上述示例中,Child子类中对父类的print方法进行重写,同时在内部使用super.print()来调用父类的公共逻辑,从而实现逻辑复用。class关键字作为构造函数的语法糖,在经过TypeScript编译后,最终会被转换为兼容性好的浏览器可识别的ES5代码。class在面向对象的编程范式中非常常见,因此为了弄清楚其背后的实现机制,我们不妨多花点时间来看下经过编译转换之后的代码是什么样子的(当然这部分已经比较熟悉的同学可以直接跳过)。

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 Parent = /** @class */ (function () { function Parent() { this.x = 1; } Parent.prototype.print = function () { console.log(this.x); }; return Parent; }()); var Child = /** @class */ (function (_super) { __extends(Child, _super); function Child() { var _this = // 注意此处必须优先调用super()方法 _super.call(this) || this; _this.y = 2; return _this; } Child.prototype.print = function () { // 通过super调用父类原型上的方法,但是方法中的this指向的是子类的实例 _super.prototype.print.call(this); console.log(this.y); }; return Child; }(Parent)); var child = new Child(); console.log(child.print()); // -> 1 2

以上就是转换后的完整代码,为了方便对比,这里将原来的注释信息保留,仔细研究这段代码我们会发现以下几个要点:

1) 子类Child的构造函数中super()方法被转换成了var _this = _super.call(this) || this,这里的_super指的就是父类Parent,因此这句代码的含义就是调用父类构造函数并将this绑定到子类的实例上,这样的话子类实例便可拥有父类的x属性。因此为了实现属性继承,我们必须在子类构造函数中调用super()方法,如果不调用会编译不通过。

2) 子类Child的print方法中super.print()方法被转换成了_super.prototype.print.call(this) ,这句代码的含义就是调用父类原型上的print方法并将方法中的this指向子类实例,由于在上一步操作中我们已经继承到父类的x属性,因此这里我们将直接打印出子类实例的x属性的值。

3) extends关键字最终被转换为__extends(Child, _super)方法,其中_super指的是父类Parent,为了方便查看,这里将_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 __()); }; })();

在以上代码中,主要可以分为两个部分来进行理解,第一部分为extendStatics(d, b)方法,第二部分为该方法后面的两行代码。

第一部分:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/a5cd6f57eef1c0c30548e3ad454fcdf6.html