本文讨论Typescript中的Class同ES5构造函数的对应关系,涉及TypeScript的诸多语法、构造函数、面向对象以及原型对象等相关知识点细节,本文只简单对比并不进行深入展开。

TypeScript 是JavaScript的超集,包含ES5、ES6、ES7+...和扩展。

我们知道在TS中,Class并不是什么新鲜的东西,它的本质其实就是ES那套构造函数·原型对象·实例对象的老古董。在下面的篇幅中,我会简单的对比Class的ES5写法,从最简单到复杂。

最简单的类

最简单的类,除名字外其它一无所有。

/* 文件名:01.ts_class_and_es5.ts */
class Class_test{}

我们通过tsc 01.ts_class_and_es5.ts命令对该文件进行编译,得到的是下面的JavaScript代码。

/* 文件名:01.ts_class_and_es5.js */
var Class_test = /** @class */ (function () {
function Class_test() {}
return Class_test;
}());

从编译后的结果可以看到对应的代码就是个空构造函数,精巧的地方在于这个函数被放到一个自调用函数中并返回,这里使用了一个同名的变量来接收内部的构造函数,多么经典的闭包应用啊。

+ 属性

我们尝试在上面代码的基础上,在这个类中加入属性的概念。

class Class_test {
name:string;
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
}

我们为Class_test类新添加了属性的概念( 分别是 nameage ) , 这里其实如果不想写constructor构造函数的话其实可以给 nameage 设置一个初始值也是没问题的。

var Class_test = /** @class */ (function () {
function Class_test(name, age) {
this.name = name;
this.age = age;
}
return Class_test;
}());

可以发现写法跟ES5构造函数的写法一致,注意Class成员的几个修饰符:public protected private 在ES5代码的结构中没有意义,这里不做额外说明。

+ 方法(静态)

我们在上面代码的基础上再加上方法、静态属性、静态方法。

/* 设计类 */
class Class_test {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
getInfo():void{
console.log(`Name === ${this.name} Age === ${this.age}`);
}
/* 静态属性:构造函数(类)自己的属性,访问示例:类名.静态属性; */
/* 静态方法:构造函数(类)自己的方法,访问示例:类名.静态方法名称(); */
static className: string = "Class_test";
static getClassName():void{
console.log("ClassName === "+this.className); //this=>Class_Test
}
} /* 实例化对象 */
let c:Class_test = new Class_test("文顶顶",18);
console.log(c.name,c.age);
c.getInfo();
console.log(Class_test.className);
Class_test.getClassName();

我们为代码添加了静态属性 className、静态方法 getClassName() 、实例方法 getInfo(),通过查看对应的ES5代码,我们会发现这也没什么特别的。

var Class_test = /** @class */ (function () {
function Class_test(name, age) {
this.name = name;
this.age = age;
}
Class_test.prototype.getInfo = function () {
console.log("Name === " + this.name + " Age === " + this.age);
};
Class_test.getClassName = function () {
console.log("ClassName === " + this.className); //this=>Class_Test
};
/* 静态属性:构造函数(类)自己的属性,访问示例:类名.静态属性; */
/* 静态方法:构造函数(类)自己的方法,访问示例:类名.静态方法名称(); */
Class_test.className = "Class_test";
return Class_test;
}());
var c = new Class_test("文顶顶", 18);
console.log(c.name, c.age); /* 文顶顶 18 */
c.getInfo(); /* Name === 文顶顶 Age === 18 */
console.log(Class_test.className); /* Class_test */
Class_test.getClassName(); /* ClassName === Class_test */

关键在于搞清楚成员原型成员静态成员实例成员构造函数原型对象以及实例化的关系。

+ 继承

  /* 设计类(父类) */
class Class_super {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
getInfo():void{
console.log(`Name === ${this.name} Age === ${this.age}`);
}
static className: string = "Class_test";
static getClassName():void{
console.log("ClassName === "+this.className); //this=>Class_Test
}
} /* 设计类(子类) */
class Class_child extends Class_super{
money:number;
constructor(name: string, age: number,money:number) {
super(name,age);
this.money = money;
}
getMoney():void{
console.log(`Money === ${this.money}`)
}
static child_static_func():void{
console.log("测试该方法能否被Class_super方法调用?No")
}
} /* 实例化(对象) */
let child:Class_child = new Class_child("zs",19,100);
console.log(child,child.name,child.age,child.money);
child.getInfo();
child.getMoney();
console.log(Class_child.className);
Class_child.getClassName();
Class_child.child_static_func();
// Class_super.child_static_func(); 错误的示范

如果我们实现了类的继承,那么问题就会变得复杂得多,至少看起来如此。

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 Class_super = /** @class */ (function () {
function Class_super(name, age) {
this.name = name;
this.age = age;
}
Class_super.prototype.getInfo = function () {
console.log("Name === " + this.name + " Age === " + this.age);
};
Class_super.getClassName = function () {
console.log("ClassName === " + this.className); //this=>Class_Test
};
Class_super.className = "Class_test";
return Class_super;
}());
/* 设计类(子类) */
var Class_child = /** @class */ (function (_super) {
__extends(Class_child, _super);
function Class_child(name, age, money) {
var _this = _super.call(this, name, age) || this;
_this.money = money;
return _this;
}
Class_child.prototype.getMoney = function () {
console.log("Money === " + this.money);
};
Class_child.child_static_func = function () {
console.log("测试该方法能否被Class_super方法调用?No");
};
return Class_child;
}(Class_super));
/* 实例化(对象) */
var child = new Class_child("zs", 19, 100);
console.log(child, child.name, child.age, child.money);
/* Class_child { name: 'zs', age: 19, money: 100 } 'zs' 19 100 */
child.getInfo(); /* Name === zs Age === 19 */
child.getMoney(); /* Money === 100 */
console.log(Class_child.className); /* Class_test */
Class_child.getClassName(); /* ClassName === Class_test */
Class_child.child_static_func(); /* 测试该方法能否被Class_super方法调用?No */
// Class_super.child_static_func(); 错误的示范

蒽,是的。看上去,TS ( ES6 ) 中感觉Class继承的实现稍显复杂。接下来,我们试着挑出关键的部分,并把比较重要(表面感觉不太看得懂)的那部分代码加上些注释。

关键 ①

var _this = _super.call(this, name, age) || this;

这行代码的目的是获取父类的实例成员,具体采用的技术手段是通过借用构造函数调用( 在子构造函数中以call方法来调用父构造函数,并绑定this)的方式来获取。此处,拿到的是name age两个属性(当我们通过子类来实例化创建对象的时候,得到的实例化对象中因此拥有了nameage 属性)。

关键 ②

__extends(Class_child, _super);

这行代码是一个函数调用,其中__extends是函数名称,而Class_child, _super是传递给函数的具体实参。

该函数的作用是,通过特定的方式来获取父类(构造函数)的静态成员(包括静态属性以及静态方法)并让实例对象可以通过原型链来访问父类原型对象上面的成员(属性和方法)。

/* @Description: __extends主要用于处理(静态成员)的继承
* @return: 函数形态 function f(_child,_super){} */
var __extends = (this && this.__extends) || (function() {
/* @Description: extendStatics用于扩展静态成员(静态属性和静态方法)
* param __child 子类(Class_child)
* param __super 父类(Class_super)*/
var extendStatics = function(__child, __super) {
/* 1.确定函数 */
/* extendStatics变量的值总是为一个函数,该函数的目的是_super的属性和方法(静态)*/
/* 核心策略 */
/* ① 尝试直接使用 Object.setPrototypeOf函数 类似于__child.__proto__ = __super */
/* ② 直接通过__child.__proto__ = __super来设置 */
/* ③ 通过遍历的方式来拷贝_super对象的实例成员 */
extendStatics = Object.setPrototypeOf
||({ __proto__: [] } instanceof Array
&& function(__child, __super) {
__child.__proto__ = __super;
})
||function(d, b) {
for (var p in b) {
if (b.hasOwnProperty(p)) d[p] = b[p]; }
};
/* 2.调用函数(完成继承) */
return extendStatics(__child, __super);
};
return function(_child, _super) {
/* [1] 完成对父类(构造函数)静态成员(属性+方法)的拷贝工作 */
extendStatics(_child, _super); /* [2] 完成对父类(构造函数)原型成员(主要是方法)的原型链继承 */
/* 内部的this取决于__()函数如何调用,使用new调用则this指向的是内部新创建的实例对象 */
function __() { this.constructor = _child; } /* 如果_spuer为null,那么就创建一个新对象*/
/* 设置该对象的原型对象为_super[let o = {}; o.__prototype = _super] */
/* 并把最终结果赋值给_child.prototype, 否则就间接通过__()这个函数来设置原型对象*/
/* 并创建实例化对象,并设置 _child.prototype = new __() */
_child.prototype =
_super === null ?
Object.create(_super) :
(__.prototype = _super.prototype, new __());
};
})();

我在代码中穿插并加上了一些说明性的注释 ,看上去似乎比较复杂,稍微总结下:

__extends() 函数做了两件事情:
[1] 完成对父类(构造函数)静态成员(属性+方法)的拷贝工作
[2] 完成对父类(构造函数)原型成员(主要是方法)的原型链继承
如何做的?
[1] child.__proto__ = super
[2] child.prototype = super.prototype; + 解决共享问题(中间加了间隔层)

+ Interface

接口( interface )是 typescript 中的重要概念,而且接口也能继承且可以约束类,因此这里简单的比较下,下面给出示例代码以及对应的JavaScript代码。

/* 1.定义接口(父) */
interface Test_Class_Super_Interface {
name: string;
age: number;
getName: () => void;
getAge: () => void;
} /* 2.定义接口(子) */
interface Test_Class_Child_Interface extends Test_Class_Super_Interface{
id:number;
getId:()=>void;
} /* 3.设计类:该类实现指定的接口 */
class Test_Class implements Test_Class_Child_Interface {
id: number = 0;
name: string;
age: number;
getName(): void {
console.log("getName() " + this.name)
};
getAge(): void {
console.log("getAge() " + this.age)
};
getId():void{
console.log(this.id);
}
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
/* 4.创建实例化对象 */
let instance: Test_Class = new Test_Class("ls", 18);
console.log(instance);

上面代码设计了两个接口,分别是Test_Class_Super_InterfaceTest_Class_Child_Interface,它们是继承关系,Test_Class 类实现了Test_Class_Child_Interface接口。

var Test_Class = /** @class */ (function() {
function Test_Class(name, age) {
this.id = 0;
this.name = name;
this.age = age;
}
Test_Class.prototype.getName = function() {
console.log("getName() " + this.name);
};;
Test_Class.prototype.getAge = function() {
console.log("getAge() " + this.age);
};;
Test_Class.prototype.getId = function() {
console.log(this.id);
};
return Test_Class;
}());
var instance = new Test_Class("ls", 18);
console.log(instance);

我们发现转换后 JavaScript版本的代码里面好像没有任何与接口相关的信息,接口是 typescript 中特有的类型, 在编译后会自动消失。

javaScript系列 [43]-TS、Class and ES5的更多相关文章

  1. 深入理解JavaScript系列(43):设计模式之状态模式

    介绍 状态模式(State)允许一个对象在其内部状态改变的时候改变它的行为,对象看起来似乎修改了它的类. 正文 举个例子,就比如我们平时在下载东西,通常就会有好几个状态,比如准备状态(ReadySta ...

  2. 深入理解JavaScript系列

    转自http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html 深入理解JavaScript系列(1):编写高质量JavaScript代码 ...

  3. 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点

    深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点 2011-12-28 23:00 by 汤姆大叔, 139489 阅读, 119 评论, 收藏, 编辑 才华横溢的 ...

  4. 深入理解JavaScript系列(转自汤姆大叔)

    深入理解JavaScript系列文章,包括了原创,翻译,转载,整理等各类型文章,如果对你有用,请推荐支持一把,给大叔写作的动力. 深入理解JavaScript系列(1):编写高质量JavaScript ...

  5. [转]深入理解JavaScript系列

    文章转自:汤姆大叔-深入理解JavaScript系列文章 深入理解JavaScript系列文章,包括了原创,翻译,转载,整理等各类型文章,如果对你有用,请推荐支持一把,给大叔写作的动力. 深入理解Ja ...

  6. javascript系列之this

    原文:javascript系列之this 引言 在这篇文章里我们将会讨论与执行上下文直接相关的更多细节.讨论的主题就是this关键字.实践证明,这个主题是足够难的并且在不同的执行上下文中判定this的 ...

  7. javascript系列之变量对象

    原文:javascript系列之变量对象 引言 一般在编程的时候,我们会定义函数和变量来成功的构造我们的系统.但是解析器该如何找到这些数据(函数,变量)呢?当我们引用需要的对象时,又发生了什么了? 很 ...

  8. JavaScript 系列博客(三)

    JavaScript 系列博客(三) 前言 本篇介绍 JavaScript 中的函数知识. 函数的三种声明方法 function 命令 可以类比为 python 中的 def 关键词. functio ...

  9. JavaScript 系列博客(一)

    JavaScript 系列博客(一) 前言 本系列博客为记录学习 JavaScript 的学习笔记,会从基础开始慢慢探索 js.今天的学习笔记主要为 js 引入.定义变量以及 JavaScript 中 ...

随机推荐

  1. Linux学习 - 网络命令

    一.write 1 功能 给指定在线用户发信息,以Ctrl + D保存结束 2 语法 write  <用户名>  [信息] 二.wall(write all) 1 功能 给所有在线用户发送 ...

  2. entfrm开源免费模块化无代码开发平台,开放生态为您创造更多的价值

    entfrm开发平台6大特性,赋能快速开发,为您创造更多的价值: 1. 模块化 丰富的模块稳定的框架 后台极易上手 目前已包括系统管理.任务调度.运维监控.开发工具.消息系统.工作流引擎.内容管理等模 ...

  3. 【Linux】【Services】【Docker】应用

    1. Docker应用: 镜像:包含了启动Docker容器所需要的文件系统层级及其内容:基于UnionFS采用分层结构实现: bootfs,rootfs registry:保存docker镜像及镜像层 ...

  4. C#获取Windows10屏幕的缩放比例

    现在1920x1080以上分辨率的高分屏电脑渐渐普及了.我们会在Windows的显示设置里看到缩放比例的设置.在Windows桌面客户端的开发中,有时会想要精确计算窗口的面积或位置.然而在默认情况下, ...

  5. 粒子群算法-PSO

    粒子群优化算法 1. 背景知识 1995年美国社会心理学家Kennedy和电气工程师Eberhart共同提出粒子群优化算法(Particle Swarm Optimization, PSO).PSO算 ...

  6. 筛选Table.SelectRows-文本与数值(Power Query 之 M 语言)

    数据源: 包含文本与数值的任意数据 目标: 对文本和数值进行筛选 M公式: = Table.SelectRows( 表, 筛选条件) 筛选条件: 等于:each [指定列] = "指定值&q ...

  7. Landsat 现有 Analysis Ready Data (ARD) 数据介绍

    Global Web-Enabled Landsat Data (GWELD)[1] NASA 原先的 Web-Enabled Landsat Data Conterminous U.S. Seaso ...

  8. CF1036A Function Height 题解

    Content 给定一个坐标系,在它的 \(x\) 轴上有 \(2n+1\) 个点 \(P_0,P_1,P_2,...,P_{2n}\),其中对于 \(0\leqslant i\leqslant 2n ...

  9. CF1547A Shortest Path with Obstacle 题解

    Content 给定两个在二维平面上的网格 \(A(x_A,y_A)\) 和 \(B(x_B,y_B)\),另外,还有一个不可通过的网格 \(F(x_F,y_F)\).你需要求出在不经过 \(F\) ...

  10. TCP 长连接保活机制&HTTP长连接设置

    TCP KeepAlive Wireshark抓包分析机制 -------------------------------- 如上图所示,TCP保活报文总是成对出现,包括TCP保活探测报文和TCP保活 ...