详解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 相关文章推荐
页面右下角弹出提示框示例代码js版
Aug 02 Javascript
从js向Action传中文参数出现乱码问题的解决方法
Dec 29 Javascript
利用js实现在浏览器状态栏显示访问者在本页停留的时间
Dec 29 Javascript
js使用栈来实现10进制转8进制与取除数及余数
Jun 11 Javascript
js中for in语句的用法讲解
Apr 24 Javascript
javascript实现淘宝幻灯片广告展示效果
Apr 27 Javascript
浅谈JS中逗号运算符的用法
Jun 12 Javascript
Node.js学习之TCP/IP数据通讯(实例讲解)
Oct 11 Javascript
JavaScript使用享元模式实现文件上传优化操作示例
Aug 07 Javascript
Vue源码解析之Template转化为AST的实现方法
Dec 14 Javascript
vue实现一个获取按键展示快捷键效果的Input组件
Jan 13 Vue.js
AJAX实现省市县三级联动效果
Oct 16 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汉字转拼音的示例
2014/02/27 PHP
php文件上传你必须知道的几点
2015/10/20 PHP
PHP编程计算日期间隔天数的方法
2017/04/26 PHP
Expandable &quot;Detail&quot; Table Rows
2007/08/29 Javascript
jQuery $.each的用法说明
2010/03/22 Javascript
兼容IE和Firefox的javascript获取iframe文档内容的函数
2011/08/15 Javascript
js前台分页显示后端JAVA数据响应
2013/03/18 Javascript
javascript-表格排序(降序/反序)实现介绍(附图)
2013/05/30 Javascript
js实现键盘控制DIV移动的方法
2015/01/10 Javascript
ECMAScript6函数剩余参数(Rest Parameters)
2015/06/12 Javascript
jQuery实现瀑布流布局详解(PC和移动端)
2020/09/01 Javascript
js实现瀑布流的三种方式比较
2020/06/28 Javascript
vue Element-ui input 远程搜索与修改建议显示模版的示例代码
2017/10/19 Javascript
Angular CLI在Angular项目中如何使用scss详解
2018/04/10 Javascript
vue element项目引入icon图标的方法
2018/06/06 Javascript
在vue2.0中引用element-ui组件库的方法
2018/06/21 Javascript
js点击事件的执行过程实例分析【冒泡与捕获】
2020/04/11 Javascript
JavaScript进阶(二)词法作用域与作用域链实例分析
2020/05/09 Javascript
Python用模块pytz来转换时区
2016/08/19 Python
Python中的左斜杠、右斜杠(正斜杠和反斜杠)
2016/08/30 Python
Python OpenCV处理图像之图像直方图和反向投影
2018/07/10 Python
Python画图实现同一结点多个柱状图的示例
2019/07/07 Python
用Python解数独的方法示例
2019/10/24 Python
python批量处理txt文件的实例代码
2020/01/13 Python
python3.7通过thrift操作hbase的示例代码
2020/01/14 Python
PYQT5 vscode联合操作qtdesigner的方法
2020/03/24 Python
amazeui时间组件的实现示例
2020/08/18 HTML / CSS
Moss Bros官网:英国排名第一的西装店
2020/02/26 全球购物
某公司部分笔试题
2013/11/05 面试题
Java语言程序设计测试题判断题部分
2013/01/06 面试题
Java中采用什么结构来捕获、处理异常?各子句的顺序、功能如何
2013/10/07 面试题
竞争上岗演讲稿
2014/01/05 职场文书
公安局副政委班子个人对照检查材料
2014/10/04 职场文书
综合办公室主任岗位职责
2015/04/01 职场文书
幼儿园亲子活动通知
2015/04/24 职场文书
情侣之间的道歉短信
2015/05/12 职场文书