详解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 相关文章推荐
Javascript执行效率全面总结
Nov 04 Javascript
jquery属性选择器not has怎么写 行悬停高亮显示
Nov 13 Javascript
js中function()使用方法
Dec 24 Javascript
jQuery实现仿QQ在线客服效果的滚动层代码
Oct 15 Javascript
jQuery添加删除DOM元素方法详解
Jan 18 Javascript
全面理解JavaScript中的继承(必看)
Jun 16 Javascript
JS控制HTML元素的显示和隐藏的两种方法
Sep 27 Javascript
完全深入学习Bootstrap表单
Nov 28 Javascript
js将字符串中的每一个单词的首字母变为大写其余均为小写
Jan 05 Javascript
jQuery实现鼠标经过显示动画边框特效
Mar 24 jQuery
基于vue2实现左滑删除功能
Nov 28 Javascript
Vue.js 父子组件通信的十种方式
Oct 30 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
mac下使用brew配置环境的步骤分享
2011/05/23 PHP
PHP常用字符串操作函数实例总结(trim、nl2br、addcslashes、uudecode、md5等)
2016/01/09 PHP
php数值计算num类简单操作示例
2020/05/15 PHP
增强用户体验友好性之jquery easyui window 窗口关闭时的提示
2012/06/22 Javascript
基于jQuery中对数组进行操作的方法
2013/04/16 Javascript
Javascript/Jquery——简单定时器的多种实现方法
2013/07/03 Javascript
jQuery实现锚点scoll效果实例分析
2015/03/10 Javascript
easyUI实现(alert)提示框自动关闭的实例代码
2016/11/07 Javascript
jquery实现多次上传同一张图片
2017/01/09 Javascript
Javascript 使用ajax与C#获取文件大小实例详解
2017/01/13 Javascript
JS拉起或下载app的实现代码
2017/02/22 Javascript
深入理解Vue-cli搭建项目后的目录结构探秘
2017/07/13 Javascript
详解ES6之async+await 同步/异步方案
2017/09/19 Javascript
XMLHttpRequest对象_Ajax异步请求重点(推荐)
2017/09/28 Javascript
Bootstrap模态对话框中显示动态内容的方法
2018/08/10 Javascript
Vue Autocomplete 自动完成功能简单示例
2019/05/25 Javascript
layui table 列宽百分比显示的实现方法
2019/09/28 Javascript
JavaScript实现Tab选项卡切换
2020/02/13 Javascript
js实现无缝轮播图
2020/03/09 Javascript
linux服务器快速卸载安装node环境(简单上手)
2021/02/22 Javascript
Python  连接字符串(join %)
2008/09/06 Python
Python的ORM框架中SQLAlchemy库的查询操作的教程
2015/04/25 Python
Caffe均值文件mean.binaryproto转mean.npy的方法
2018/07/09 Python
Python docx库用法示例分析
2019/02/16 Python
PyTorch中Tensor的维度变换实现
2019/08/18 Python
keras处理欠拟合和过拟合的实例讲解
2020/05/25 Python
CSS3等相关属性制作分页导航实现代码
2012/12/24 HTML / CSS
大学生先进事迹材料
2014/02/16 职场文书
取保候审保证书
2014/04/30 职场文书
大学同学会活动方案
2014/08/20 职场文书
2014离婚协议书范文两篇
2014/09/15 职场文书
2015年挂职锻炼工作总结
2014/12/12 职场文书
自愿离婚协议书2015
2015/01/26 职场文书
2015年电工工作总结
2015/04/10 职场文书
MySQL sql_mode的使用详解
2021/05/08 MySQL
Python可变集合和不可变集合的构造方法大全
2021/12/06 Python