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 的所有成员方法,但是至少有两个问题:

  1. 原型对象上 还放着变量成员,变量成员不应该被共享

  2. 第一级的原型对象上面应该只存放子类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 继承篇的更多相关文章

  1. c++学习笔记之继承篇

    title: c++学习笔记之继承篇 date: 2017-03-26 16:36:33 tags: [c++,继承,public,virtual,private,protected] categor ...

  2. typescript 的 polyfill 学习

    我们知道typescript 是ES 超集.这意味着,不仅仅ES 的各种语法特性都会包括,还能保证通过typescript的编译服务可以很方便的转成ES向下兼容的版本,这得意于typescript强大 ...

  3. Java菜鸟学习笔记(23)--继承篇(二):继承与组合

    组合是什么 1.继承和组合都是一种随思想渗透而下的编码方式,其根本目的都是为了复用类,减少重复代码 2.要实现一个类的复用,可以分为组合语法和继承语法 3.组合就是通过将一个对象置于一个新类中,将其作 ...

  4. 8-C++远征之继承篇-学习笔记

    C++远征之继承篇 开篇介绍 整个C++远征计划: 起航->离港->封装->继承 为什么要用继承? 为什么要有继承? 如何来定义基类 <----> 派生类? 基类到派生类 ...

  5. TypeScript 入门教程学习笔记

    TypeScript 入门教程学习笔记 1. 数据类型定义 类型 实例 说明 Number let num: number = 1; 基本类型 String let myName: string = ...

  6. openresty 学习笔记番外篇:python的一些扩展库

    openresty 学习笔记番外篇:python的一些扩展库 要写一个可以使用的python程序还需要比如日志输出,读取配置文件,作为守护进程运行等 读取配置文件 使用自带的ConfigParser模 ...

  7. Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点

    Spring源码学习笔记12--总结篇,IOC,Bean的生命周期,三大扩展点 参考了Spring 官网文档 https://docs.spring.io/spring-framework/docs/ ...

  8. JS对象继承篇

    JS对象继承篇 ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的 原型链 其基本思路是利用原型让一个引用类型继承另一个引用类型的属性和方法 function Person() ...

  9. 从零开始学习jQuery (一) 入门篇

    本系列文章导航 从零开始学习jQuery (一) 入门篇 一.摘要 本系列文章将带您进入jQuery的精彩世界, 其中有很多作者具体的使用经验和解决方案,  即使你会使用jQuery也能在阅读中发现些 ...

随机推荐

  1. 如何设置Cookie 的值为中文的内容

    默认情况下,cookie的值是不允许中文内容的.可以借助于java.net.URLEncoder先对中文字符串进行编码,将编码后的结果设为cookie值.当程序要读取cookie值时,先读取,然后使用 ...

  2. 201521123064 《Java程序设计》第8周学习总结

    1. 本章学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 集合内容见:<Java程序设计>第7周学习总结 1.2 选做:收集你认为有用的代码片段 ① Jav ...

  3. Java 第七周总结

    1. 本周学习总结 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 我们知道ArrayList是允许重复的,有序的元素的集合,但当我们想用它来放 ...

  4. 201521123100 《Java程序设计》第5周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 1.接口不是类,不能使用new进行实例化 2.使用instanceof运算符,可 ...

  5. 201521123034《Java程序设计》第十四周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自 ...

  6. 201521123001《Java程序设计》第10周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 finally 题目4-2 1.1 截图你的提交结果(出现学 ...

  7. Spring第六篇【Spring AOP模块】

    前言 Spring的第五篇也算是AOP编程的开山篇了,主要讲解了代理模式-..本博文主要讲解Spring的AOP模块:注解方式和XML方式实现AOP编程.切入点表达式.. AOP的概述 Aop: as ...

  8. java web项目修改favicon.ico图标的方式

    1.修改整个项目的tomcat图标 找到tomcat的根目录(tomcat-webapps-ROOT目录),然后将修改的favicon.ico图标覆盖掉本地的图标,然后再重启项目,刷新,清除浏览器缓存 ...

  9. webstorm配置scss环境

    1.下载 Ruby  (安装过程中记得勾选添加到环境变量,安装结束最后可能会弹出一个cmd弹框,可以忽略) 2. cmd安装sass gem install sass 3. cmd检查是否安装 sas ...

  10. php字符的替换,截取,指定查找

    <?php/** * Created by 郭鹏. * User: msi * Date: 2017/9/27 * Time: 14:17 *///随机数生成器echo rand();echo ...