typescript 的 polyfill 学习1-Class 继承篇
Class 继承
js 是多范式的编程语言,同样也是支持面向对象编程的,类 是面向对象中是很重要的概念。
区别于传统的java,c#基于模板的类,js是基于原型的。
类继承一般是通过原型链的方式来实现,在es3时代,可以使用Base.js这个库来进行类编程。
而ES6通过关键字class来定义类,这种语法糖让写法更加清晰,更像传统的类编程,也因此屏蔽了原型链的细节,会减少初学者的困惑,不过也因为这样就失去了理解js本身的语法特性的机会。
下面用ts写的两个类,我们看看转成es5后,js是怎么模拟这个 语法糖的。
代码使用amd进行模块封装:
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": false,
"module": "amd",
"target": "es5",
"jsx": "react"
},
"include": [
"./src/**/*"
]
}
ts代码:
//基类
export class A {
public Pro1:string = "pro1";//公共成员
private fPro1:string = "fPro1";//私有成员
//私有方法
private fMethod1():string{
return "123";
}
//保护方法(可继承的方法)
protected pMethod1():string{
return "456";
}
//公共方法
public Method1(){
}
}
//子类B
export class B extends A {
public Pro2:string ="Pro2";
private fPro2:string ="fPro2";
constructor(para:string){
super(); //构造器重载
this.fPro2 = para ;
}
public Method2(){
}
//方法重载
protected pMethod1():string{
return super.pMethod1();
}
private fMethod2():string{
return "123";
}
}
我们定义了基类A,并且定义了 私有变量,保护变量,公共变量,私有方法,保护方法,公共方法
定义了继承类B,同样定义了自己的成员,并且构造器重载 和 保护方法重载
父类的实现
var A = /** @class */
(function() {
function A() {
this.Pro1 = "pro1";
this.fPro1 = "fPro1";
}
A.prototype.fMethod1 = function() {
return "123";
}
;
A.prototype.pMethod1 = function() {
return "456";
}
;
A.prototype.Method1 = function() {}
;
return A;
}()); //闭包作用域
可以看到,通过闭包的的作用域里面新建构造函数给变量A赋值一个构造函数
构造函数里面对类成员变量进行初始化,并且在 原型对象(显示原型)定义类方法。里面js里面没有访问修饰符,我们看到私有方法和公有方法的定义并没有区别。
子类的实现
var B = /** @class */
(function(_super) {
__extends(B, _super);
function B(para) {
var _this = _super.call(this) || this;
_this.Pro2 = "Pro2";
_this.fPro2 = "fPro2";
_this.fPro2 = para;
return _this;
}
B.prototype.Method2 = function() {}
;
B.prototype.pMethod1 = function() {
return _super.prototype.pMethod1.call(this);
}
;
B.prototype.fMethod2 = function() {
return "123";
}
;
return B;
}(A));
exports.B = B;
子类定义的闭包函数传入父类(基类)A做为参数_super,同样也是返回构造函数B.
跟父类不同的是,首先通过调用 __extends函数,对B 和 _super 做了处理。根据js
函数提升的特性,此时B 这个构造函数已经被定义,所以__extends函数的调用应该是在最后面。
在讲解__extends函数之前,我们先看看 重载的定义。
首先是构造器的重载。 构造函数通过调用 A.call(this),父类被调用,父类的构造函数被执行。
这里我们顺便复习一下new 操作符的执行原理:
var obj = {};//我们创建了一个空对象obj
obj.__proto__ = Base.prototype;//我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象
Base.call(obj);//我们将Base函数对象的this指针替换成obj,然后再调用Base函数,于是我们就给obj对象赋值了一个id成员变量,这个成员变量的值是”base”,关于call函数的用法。
这样我们就知道,当我们new B()的时候,就开始执行B构造函数里面的代码.
在这里,__extends做了什么处理呢,我们来分析一下:
__extends 实现
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) { //d:父类 b:子类
extendStatics(d, b);
function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype,
new __());
}
;
})();
同样的,函数都是在闭包里面定义的。__extends(B, _super);得知,我们传入父类和子类函数。
首先通过 extendStatics 函数的处理。extendStatics函数这边先不介绍,最后再说。
因为:
new B().__ proto__ === B.prototype
通过上面构造函数的代码里面知道,类成员都是在构造函数里面定义的,其中
方法成员全部定义在prototype里面给所有成员共享。
对于类实例来说,也就是new B() 出来的对象来说,方法成员要全部存放在__ proto__里面的,但是成员方法的来源并不一样,有的是来自于子类,有的来自于
父类,甚至来自与父父类...
如果我们通过原型链来实现的话,很自然的我们希望,每一级的父类对应原型链上的每一级。
也就是说我们希望:
new B().__ proto__ 存放B定义的所有方法成员
new B().__ proto__.__ proto__ 存放A定义的所有方法成员
.......
左边是继承链,右边是继承链
我们可能会这么做, B.prototype = new A(), 这个时候new B()的原型对象上的确存在了A和B 的所有成员方法,但是至少有两个问题:
原型对象上 还放着变量成员,变量成员不应该被共享
第一级的原型对象上面应该只存放子类B的成员方法
我们看看他们是怎么解决的,既然直接实例A不行,那就是new 一个空的对象。
d.prototype = new __();
为了保证该空对象有原型功能,我们这么做。
function __() {
this.constructor = d;
}
然后
__.prototype = b.prototype
这样就保证了 new B().__ proto__.__ proto__ 存放A定义的所以方法成员
很完美,我们看看整体代码:
////d:父类 A b:子类 B
function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype,
new __());
静态成员
我们知道,传统面向对象编程语言 静态成员可以通过 static 指定,静态成员跟方法成员一样,可以被共享,typescript也是有static 关键字的, 最终也是定义在 原型对象上面。
extendStatics(d, b); // 通过命名我们也知道,这个是为了解决静态成员的
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];
}
;
这个比较简单 ,通过代码我们得知,这个其实就是Object.setPrototypeOf函数及其等价的代码。
Object.setPrototypeOf 定义:
将一个指定的对象的原型设置为另一个对象或者null(既对象的[[Prototype]]内部属性).
B.__ proto__ = A , 为什么需要等价代码呢,是因为 proto 并不是标准(虽然每个浏览器都支持了)。
好了,我们看一下一个子类实例的效果:

typescript 的 polyfill 学习1-Class 继承篇的更多相关文章
- c++学习笔记之继承篇
title: c++学习笔记之继承篇 date: 2017-03-26 16:36:33 tags: [c++,继承,public,virtual,private,protected] categor ...
- typescript 的 polyfill 学习
我们知道typescript 是ES 超集.这意味着,不仅仅ES 的各种语法特性都会包括,还能保证通过typescript的编译服务可以很方便的转成ES向下兼容的版本,这得意于typescript强大 ...
- Java菜鸟学习笔记(23)--继承篇(二):继承与组合
组合是什么 1.继承和组合都是一种随思想渗透而下的编码方式,其根本目的都是为了复用类,减少重复代码 2.要实现一个类的复用,可以分为组合语法和继承语法 3.组合就是通过将一个对象置于一个新类中,将其作 ...
- 8-C++远征之继承篇-学习笔记
C++远征之继承篇 开篇介绍 整个C++远征计划: 起航->离港->封装->继承 为什么要用继承? 为什么要有继承? 如何来定义基类 <----> 派生类? 基类到派生类 ...
- TypeScript 入门教程学习笔记
TypeScript 入门教程学习笔记 1. 数据类型定义 类型 实例 说明 Number let num: number = 1; 基本类型 String let myName: string = ...
- openresty 学习笔记番外篇:python的一些扩展库
openresty 学习笔记番外篇:python的一些扩展库 要写一个可以使用的python程序还需要比如日志输出,读取配置文件,作为守护进程运行等 读取配置文件 使用自带的ConfigParser模 ...
- Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点
Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...
- JS对象继承篇
JS对象继承篇 ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的 原型链 其基本思路是利用原型让一个引用类型继承另一个引用类型的属性和方法 function Person() ...
- 从零开始学习jQuery (一) 入门篇
本系列文章导航 从零开始学习jQuery (一) 入门篇 一.摘要 本系列文章将带您进入jQuery的精彩世界, 其中有很多作者具体的使用经验和解决方案, 即使你会使用jQuery也能在阅读中发现些 ...
随机推荐
- 团队作业10——复审与事后分析(Beta版本)
Deadline: 2017-6-13 22:00PM,以博客发表日期为准 评分基准: 按时交 - 有分,检查的项目内容为后文的两个方面 Beta阶段项目复审(单独一篇博客) 事后诸葛亮分析报告(单独 ...
- 【Beta】Daily Scrum Meeting——Day7
站立式会议照片 1.本次会议为第七次Meeting会议: 2.本次会议在中午12点,在陆大楼召开,本次会议为30分钟讨论今天要完成的任务以及接下来的任务安排. 燃尽图 每个人的工作分配 成 员 昨天已 ...
- 201521123022 《Java程序设计》 第五周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 2. 书面作业 1.代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过? ...
- Python IDLE快捷键一览
编辑状态时:Ctrl + [ .Ctrl + ] 缩进代码Alt+3 Alt+4 注释.取消注释代码行Alt+5 Alt+6 切换缩进方式 空格<=>TabAlt+/ 单词完成,只要文中出 ...
- 201521123037 《Java程序设计》第12周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. I/O流.本质上是一个数据序列:最基本的可处理数据单位为byte. 1.1 分类:输入流(读数据),输出流(写 ...
- 万物皆对象的JavaScript
写在前面: 经过前段时间对于H5语言的学习,开始慢慢适应现在的生活节奏和代码语言.从一开始的毫无头绪到现在可以大概地写出部分的网站静态页面,用了两个周的时间.作为一个之前从未接触过计算机语言的初学者来 ...
- shell脚本之算术运算和逻辑运算
目录 算术运算 赋值运算 逻辑运算 短路运算和异或 条件测试 数值测试 字符串测试 文件及其属性测试 存在性测试 存在性及类别测试 文件权限测试 文件特殊权限测试 文件大小测试 文件是否打开 双目测试 ...
- Spring第二篇【Core模块之快速入门、bean创建细节、创建对象】
前言 上篇Spring博文主要引出了为啥我们需要使用Spring框架,以及大致了解了Spring是分为六大模块的-.本博文主要讲解Spring的core模块! 搭建配置环境 引入jar包 本博文主要是 ...
- [13] static 和 final
不论是类.属性,还是方法的声明中,都有一个可设置的"修饰符",它可以实现一些高级特性. 1.static static被称之为静态的,并不是指不可以修改的意思,而是说它的内存空间是 ...
- python实例编写(4)--js,滚动条,cookie,验证码,获取特定属性的元素,实现原理
一.调用js 执行方法:execute_script(script,*args) 场景一:在页面上直接执行调用js 场景二:在定位的某个元素上执行调用js 如:掩藏文字(提示插件 tooltip设置淡 ...