详解ES6中class的实现原理


Posted in Javascript onOctober 03, 2020

一、在ES6以前实现类和继承

实现类的代码如下:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.speakSomething = function () {
  console.log("I can speek chinese");
};

实现继承的代码如下:一般使用原型链继承和call继承混合的形式

function Person(name) {
  this.name = name;
}

Person.prototype.showName = function () {
  return `名字是:${this.name}`;
};

function Student(name, skill) {
  Person.call(this, name);//继承属性
  this.skill = skill;
}

Student.prototype = new Person();//继承方法

二、ES6使用class定义类

class Parent {
  constructor(name,age){
    this.name = name;
    this.age = age;
  }
  speakSomething(){
    console.log("I can speek chinese");
  }
}

经过babel转码之后

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var Parent = function () {
  function Parent(name, age) {
    _classCallCheck(this, Parent);

    this.name = name;
    this.age = age;
  }

  _createClass(Parent, [{
    key: "speakSomething",
    value: function speakSomething() {
      console.log("I can speek chinese");
    }
  }]);

  return Parent;
}();

可以看到ES6类的底层还是通过构造函数去创建的。

通过ES6创建的类,是不允许你直接调用的。在ES5中,构造函数是可以直接运行的,比如Parent()。但是在ES6就不行。我们可以看到转码的构造函数中有_classCallCheck(this, Parent)语句,这句话是防止你通过构造函数直接运行的。你直接在ES6运行Parent(),这是不允许的,ES6中抛出Class constructor Parent cannot be invoked without 'new'错误。转码后的会抛出Cannot call a class as a function.能够规范化类的使用方式。

转码中_createClass方法,它调用Object.defineProperty方法去给新创建的Parent添加各种属性。defineProperties(Constructor.prototype, protoProps)是给原型添加属性。如果你有静态属性,会直接添加到构造函数defineProperties(Constructor, staticProps)上。

三、ES6实现继承

我们给Parent添加静态属性,原型属性,内部属性。

class Parent {
  static height = 12
  constructor(name,age){
    this.name = name;
    this.age = age;
  }
  speakSomething(){
    console.log("I can speek chinese");
  }
}
Parent.prototype.color = 'yellow'


//定义子类,继承父类
class Child extends Parent {
  static width = 18
  constructor(name,age){
    super(name,age);
  }
  coding(){
    console.log("I can code JS");
  }
}

经过babel转码之后

"use strict";
 
var _createClass = function () {
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }
 
  return function (Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
}();
 
function _possibleConstructorReturn(self, call) {
  if (!self) {
    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  }
  return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
 
function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
 
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}
 
var Parent = function () {
  function Parent(name, age) {
    _classCallCheck(this, Parent);
 
    this.name = name;
    this.age = age;
  }
 
  _createClass(Parent, [{
    key: "speakSomething",
    value: function speakSomething() {
      console.log("I can speek chinese");
    }
  }]);
 
  return Parent;
}();
 
Parent.height = 12;
 
Parent.prototype.color = 'yellow';
 
//定义子类,继承父类
 
var Child = function (_Parent) {
  _inherits(Child, _Parent);
 
  function Child(name, age) {
    _classCallCheck(this, Child);
 
    return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, name, age));
  }
 
  _createClass(Child, [{
    key: "coding",
    value: function coding() {
      console.log("I can code JS");
    }
  }]);
 
  return Child;
}(Parent);
 
Child.width = 18;

构造类的方法都没变,只是添加了_inherits核心方法来实现继承。具体步骤如下:

首先是判断父类的类型,然后:

subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });

这段代码翻译下来就是

function F(){}
F.prototype = superClass.prototype
subClass.prototype = new F()
subClass.prototype.constructor = subClass

接下来就是subClass.__proto__ = superClass

_inherits核心思想就是下面两句: 

subClass.prototype.__proto__ = superClass.prototype
subClass.__proto__ = superClass

如下图所示:

详解ES6中class的实现原理

首先 subClass.prototype.__proto__ = superClass.prototype保证了子类的实例instanceof父类是true,子类的实例可以访问到父类的属性,包括内部属性,以及原型属性。

其次,subClass.__proto__ = superClass,保证了静态属性也能访问到,也就是这个例子中的Child.height。

以上就是详解ES6中class的实现原理的详细内容,更多关于ES6中class的实现原理的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
jQuery 无刷新分页实例代码
Nov 12 Javascript
jquery统计输入文字的个数并对其进行判断
Jan 07 Javascript
JavaScript设计模式之外观模式介绍
Dec 28 Javascript
原生js实现的贪吃蛇网页版游戏完整实例
May 18 Javascript
Jsonp 关键字详解及json和jsonp的区别,ajax和jsonp的区别
Dec 30 Javascript
浅析jQuery Ajax通用js封装
Jun 22 Javascript
js仿支付宝多方框输入支付密码效果
Sep 27 Javascript
JavaScript基于扩展String实现替换字符串中index处字符的方法
Jun 13 Javascript
JavaScript正则表达式简单实用实例
Jun 23 Javascript
Vue2.x Todo之自定义指令实现自动聚焦的方法
Jan 08 Javascript
jquery的$().each和$.each的区别
Jan 18 jQuery
JavaScript面向对象核心知识与概念归纳整理
May 09 Javascript
在vue中使用Echarts画曲线图的示例
Oct 03 #Javascript
vue 虚拟DOM的原理
Oct 03 #Javascript
vue使用video插件vue-video-player的示例
Oct 03 #Javascript
区分vue-router的hash和history模式
Oct 03 #Javascript
Vue双向数据绑定(MVVM)的原理
Oct 03 #Javascript
Chrome插件开发系列一:弹窗终结者开发实战
Oct 02 #Javascript
js通过canvas生成图片缩略图
Oct 02 #Javascript
You might like
php url地址栏传中文乱码解决方法集合
2010/06/25 PHP
php函数间的参数传递(值传递/引用传递)
2013/09/23 PHP
php导出excel格式数据问题
2014/03/11 PHP
php对关联数组循环遍历的实现方法
2015/03/13 PHP
PHP计算加权平均数的方法
2015/07/16 PHP
Nginx环境下PHP flush失效的解决方法
2016/10/19 PHP
jQuery EasyUI API 中文文档 - NumberSpinner数值微调器使用介绍
2011/10/21 Javascript
window.open以post方式将内容提交到新窗口
2012/12/26 Javascript
jquery动态加载select下拉框示例代码
2013/12/10 Javascript
js单词形式的运算符
2014/05/06 Javascript
用Jquery选择器计算table中的某一列某一行的合计
2014/08/13 Javascript
JavaScript地理位置信息API
2016/06/11 Javascript
jQuery实现选项卡功能(两种方法)
2017/03/08 Javascript
MUI 上拉刷新/下拉加载功能实例代码
2017/04/13 Javascript
浅谈Angular文字折叠展开组件的原理分析
2017/11/24 Javascript
Angular2学习笔记之数据绑定的示例代码
2018/01/03 Javascript
用Node写一条配置环境的指令
2019/11/14 Javascript
Vue组件间的通信pubsub-js实现步骤解析
2020/03/11 Javascript
[04:40]2016国际邀请赛中国区预选赛全程TOP10镜头集锦
2016/07/01 DOTA
在Python中使用pngquant压缩png图片的教程
2015/04/09 Python
Python中处理字符串之islower()方法的使用简介
2015/05/19 Python
Python对文件操作知识汇总
2016/05/15 Python
Python数据操作方法封装类实例
2017/06/23 Python
Python中list查询及所需时间计算操作示例
2018/06/21 Python
在mac下查找python包存放路径site-packages的实现方法
2018/11/06 Python
python使用mitmproxy抓取浏览器请求的方法
2019/07/02 Python
python pycharm的安装及其使用
2019/10/11 Python
python 图像的离散傅立叶变换实例
2020/01/02 Python
对Tensorflow中Device实例的生成和管理详解
2020/02/04 Python
浅析Python 序列化与反序列化
2020/08/05 Python
html5中嵌入视频自动播放的问题解决
2020/05/25 HTML / CSS
Nicole Miller官方网站:纽约女装品牌
2019/09/14 全球购物
事业单位个人应聘自荐信
2013/09/21 职场文书
机电专业大学生求职信
2013/10/04 职场文书
教师节活动总结
2014/08/29 职场文书
python-for x in range的用法(注意要点、细节)
2021/05/10 Python