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. Alpha阶段-个人总结

    一.五个问题 1.第三章中提到了"质量"和"按时交付"的问题,我想问,世事难料,当两者不能兼得的时候,我是保证质量却无法按时交付,还是水两下保证按时交付呢? 2 ...

  2. 分而治之(Work Breakdown Structure, WBS)

    不知道大家有没有和我一样的情况,就是想写一篇博客,不知道从何写起,如何组织语言,如何安排这篇博客的要交待的事情的前因后果:如果在写作过程中被打断,又不知道如何重新拾起键盘,从哪里写起."就如 ...

  3. 团队作业1——团队展示&博客作业查重系统

    团队展示: 1.队名:六个核桃 2.队员学号: 王婧(201421123065).柯怡芳(201421123067组长).陈艺菡(201421123068). 钱惠(201421123071).尼玛( ...

  4. 201521123066 《Java程序设计》第四周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 1.多态性: (1)概念:相同的方法名,不同的实现方法 (2)instanceof运算符:判 ...

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

    1. 本周学习总结 初学面向对象,会学习到很多碎片化的概念与知识.尝试学会使用思维导图将这些碎片化的概念.知识组织起来.请使用纸笔或者下面的工具画出本周学习到的知识点.截图或者拍照上传. 2. 书面作 ...

  6. Java课程设计-计算器

    1.团队课程设计博客链接 http://www.cnblogs.com/yuanj/p/7072137.html 2.个人负责模块或任务说明 监听器的设置 3.自己的代码提交记录截图 //注册各个组件 ...

  7. 201521123048 《java程序设计》 第10周学习总结

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

  8. lintcode.67 二叉树中序遍历

    二叉树的中序遍历    描述 笔记 数据 评测 给出一棵二叉树,返回其中序遍历 您在真实的面试中是否遇到过这个题? Yes 样例 给出二叉树 {1,#,2,3}, 1 \ 2 / 3 返回 [1,3, ...

  9. JS中基本的一些兼容问题 可能解释的不会太清楚

    做兼容注意: 一如果两个都是属性,用逻辑||做兼容 二如果有一个是方法 用三目运算符做兼容 三多个属性或方法封装函数做兼容 一:谷歌浏览器和火狐浏览器鼠标滚动条兼容 1.document.docume ...

  10. angular 学习笔记

    每天进步一点点,学习笔记 笔记来自  angular权威指南 如果想要屏蔽浏览器对表单的默认验证行为,可以在表单元素上添加 novalidate 标记. 而按钮标签则完全忽略 hr e f 属性,并不 ...