从编译后的js代码看typescript中的面向对象
2020/3/18 11:01:32
本文主要是介绍从编译后的js代码看typescript中的面向对象,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
众所周知,ts目前是不能直接编译成机器码运行的
需要转换成js代码后运行
由于js的动态特性,所以我也很好奇编译后的代码是怎么样的呢
废话不多说,直接切入正题
注:(ts版本为:3.1,编译的js版本为:ES5)
封装
public/private/protected
首先写一个基类
class Person { public name: string; private _age: number; protected sex: string; constructor(name: string, age: number, sex: string) { this.name = name; this.sex = sex; this._age = age; } public say():void { console.log(`my name is ${this.name}`) } } 复制代码
编译后:
var Person = /** @class */ (function () { function Person(name, age, sex) { this.name = name; this.sex = sex; this._age = age; } Person.prototype.say = function () { console.log("my name is " + this.name); }; return Person; }()); 复制代码
可以看到编译后的代码还少了很多
我本以为编译后的代码应该更复杂些,
比如用闭包实现private等等
后来一想,真没必要,ts是js的超集
只要保证在ts内,代码的安全性能得到保证就行
毕竟,我们不会在编译后的js内再进行编码
let p: Person = new Person('小明', 20, '男'); // error:属性“_age”为私有属性,只能在类“Person”中访问。ts(2341) p._age = 21; 复制代码
注意,构造函数也可以进行描述,private后将不能实例化对象,protected后只能由继承的类调用其构造函数
存取器与只读
我们再来看看其他修饰器编译之后的代码
class Person { public readonly age: number; private _height: number; get height(): number { return this._height; } constructor( age: number, height: number) { this.age = age; this._height = height; } } 复制代码
编译后:
var Person = /** @class */ (function () { function Person(age, height) { this.age = age; this._height = height; } Object.defineProperty(Person.prototype, "height", { get: function () { return this._height; }, enumerable: true, configurable: true }); return Person; }()); 复制代码
这里只有get ts能推断出属性是只读
我们代码主动设置属性readonly也是只读
但要注意他们编译出来的代码不同
let p: Person = new Person(20, 170); // error:Cannot assign to 'age' because it is a read-only property.ts(2540) p.age = 22; // error:Cannot assign to 'height' because it is a read-only property.ts(2540) p.height = 180; 复制代码
继承
下面我们来写一个子类
class Person { public name: string; private _age: number; protected sex: string; constructor(name: string, age: number, sex: string) { this.name = name; this.sex = sex; this._age = age; } public say() { console.log(`my name is ${this.name}`) } } class Boy extends Person { constructor(name: string, age: number) { super(name, age, '男'); } play() { } } 复制代码
编译后:
"use strict"; 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 __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var Person = /** @class */ (function () { function Person(name, age, sex) { this.name = name; this.sex = sex; this._age = age; } Person.prototype.say = function () { console.log("my name is " + this.name); }; return Person; }()); var Boy = /** @class */ (function (_super) { __extends(Boy, _super); function Boy(name, age) { return _super.call(this, name, age, '男') || this; } Boy.prototype.play = function () { }; return Boy; }(Person)); 复制代码
可以看到编译成ES5的js代码class的实现还是特别规范的
使用的组合寄生继承方实现式,
没有变量提升,静态属性也继承了
extend与implements
要注意ts和别的强类型语言不同点
类既能被继承,也能被当做接口去实现
而接口,只能去实现
如:
class Person { public name: string; constructor(name: string) { this.name = name; } public say() { console.log(`my name is ${this.name}`) } } interface IStudy { read(): void; write(): void; } class Boy extends Person implements IStudy { constructor(name: string) { super(name); } read(): void { console.log('boy read'); } write(): void { } } class Girl implements Person, IStudy { public name: string; constructor(name: string) { this.name = name; } read(): void { console.log('girl read'); } write(): void { } say(): void { } } 复制代码
Boy是继承自Person
而Girl是实现了Person
编译代码为:
var Person = /** @class */ (function () { function Person(name) { this.name = name; } Person.prototype.say = function () { console.log("my name is " + this.name); }; return Person; }()); exports.Person = Person; var Boy = /** @class */ (function (_super) { __extends(Boy, _super); function Boy(name) { return _super.call(this, name) || this; } Boy.prototype.read = function () { console.log('boy read'); }; Boy.prototype.write = function () { }; return Boy; }(Person)); var Girl = /** @class */ (function () { function Girl(name) { this.name = name; } Girl.prototype.read = function () { console.log('girl read'); }; Girl.prototype.write = function () { }; Girl.prototype.say = function () { }; return Girl; }()); 复制代码
这里编译后的js代码也直观的显示了继承和实现的区别
Boy继承自Person,所以原型链上能找到父类的方法属性
Girl实现了Person,所以只是自身拥有和Person一样结构的属性和方法
let s1: Boy = new Boy('小明'); let s2: Girl = new Girl('小红'); console.log(s1 instanceof Person); // true console.log(s2 instanceof Person); // false 复制代码
类型兼容
相较其他强类型语言,更有意思的是
ts中的类型兼容,更像是填鸭大法
只要两个类型拥有相同的结构,就可以兼容
比如接着上面的代码:
let boy: Boy = new Boy('小明'); let girl: Girl = new Girl('小红'); boy = girl;// ok let tmp: Girl = new Boy('小黑'); // ok 复制代码
联合类型
甚至还可以:
function factory(): IStudy & Person { // return new Boy('小绿'); // ok return new Girl('小紫'); // ok } 复制代码
由于js中弱类型的特性,广泛的使用了匿名对象,动态属性等等
所以ts中对类型的兼容的判定都是基于"结构",而不是它的名义类型
多态
多态是弱类型语言与生俱来的,或者说不应该有这个概念
而ts中也是与生俱来
let s1: IStudy = new Boy('小明'); let s2: IStudy = new Girl('小红'); s1.read();// boy read s2.read();// girl read 复制代码
因为多态的定义本来就是:
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
所以重写,实现,或是重载都能表现
总结
js本来就有面向对象的实现,ts只是更好的表现出来
ts对类型的兼容是基于“结构”的兼容
所以ts中的"类",既能被继承(extends),也能被实现(implements)
ts是js的超集,很多特征在编译后的js代码内并不能体现,但是能保证编译后js运行的安全性
这篇关于从编译后的js代码看typescript中的面向对象的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-30React Native常用组件-点击组件
- 2024-05-30uniapp+vue3+uv-ui手机端后台OA管理模板
- 2024-05-29Python网络爬虫的时候json=就是让你少写个json.dumps()
- 2024-05-27React Native常用组件-展示组件
- 2024-05-27React Native常用组件-列表组件
- 2024-05-09vue3开发前端表单缓存自定义指令,移动端h5必备插件
- 2024-05-09React Hooks在class组件中的使用方式
- 2024-03-30[OIDC in Action] 2. 基于OIDC(OpenID Connect)的SSO(纯JS客户端)
- 2024-03-29terraform jsonencode
- 2024-03-13vuex-persist