详解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知识点二 jquery下对数组的操作
Jan 15 Javascript
javascript实现文字图片上下滚动的具体实例
Jun 28 Javascript
使用CSS和jQuery模拟select并附提交后取得数据的代码
Oct 18 Javascript
一个简单的动态加载js和css的jquery代码
Sep 01 Javascript
jquery表单对象属性过滤选择器实例分析
May 18 Javascript
js为什么不能正确处理小数运算?
Dec 29 Javascript
JS获取鼠标选中的文字
Aug 10 Javascript
js监听键盘事件的方法_原生和jquery的区别详解
Oct 10 Javascript
js date 格式化
Feb 15 Javascript
Vue2.0学习系列之项目上线的方法步骤(图文)
Sep 25 Javascript
node crawler如何添加promise支持
Feb 01 Javascript
ES6函数和数组用法实例分析
May 23 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中使用CURL伪造来路抓取页面或文件
2011/05/04 PHP
自己在做项目过程中学到的PHP知识收集
2012/08/20 PHP
PHP实现数组array转换成xml的方法
2016/07/19 PHP
判断脚本加载是否完成的方法
2009/05/26 Javascript
jQuery Ajax方法调用 Asp.Net WebService 的详细实例代码
2011/04/27 Javascript
chrome下img加载对height()的影响示例探讨
2014/05/26 Javascript
jquery+html5烂漫爱心表白动画代码分享
2015/08/24 Javascript
JS获取子窗口中返回的数据实现方法
2016/05/28 Javascript
基于JS如何实现给字符加千分符(65,541,694,158)
2016/08/03 Javascript
js实现贪吃蛇小游戏(容易理解)
2017/01/22 Javascript
jQuery插件FusionCharts绘制的3D环饼图效果示例【附demo源码】
2017/04/02 jQuery
jQuery实现动态删除LI的方法
2017/05/30 jQuery
实例讲解DataTables固定表格宽度(设置横向滚动条)
2017/07/11 Javascript
详解vue组件基础
2018/05/04 Javascript
socket io与vue-cli的结合使用的示例代码
2018/11/01 Javascript
微信小程序实现同一页面取值的方法分析
2019/04/30 Javascript
vue+elementui 对话框取消 表单验证重置示例
2019/10/29 Javascript
微信小程序后端无法保持session的原因及解决办法问题
2020/03/20 Javascript
基于vue3.0.1beta搭建仿京东的电商H5项目
2020/05/06 Javascript
vue实现匀速轮播效果
2020/06/29 Javascript
Python文件和目录操作详解
2015/02/08 Python
Python基础中所出现的异常报错总结
2016/11/19 Python
python中map()函数的使用方法示例
2017/09/29 Python
python机器学习之神经网络(二)
2017/12/20 Python
基于python框架Scrapy爬取自己的博客内容过程详解
2019/08/05 Python
python中使用while循环的实例
2019/08/05 Python
浅析Python 条件控制语句
2020/07/15 Python
英国男士时尚购物网站:Stuarts London
2017/10/22 全球购物
美国波道夫·古德曼百货官网:Bergdorf Goodman
2017/11/07 全球购物
什么是属性访问器
2015/10/26 面试题
升旗仪式主持词
2014/03/19 职场文书
《郑和远航》教学反思
2014/04/16 职场文书
社区四风存在问题及整改措施
2014/10/26 职场文书
违纪学生保证书
2015/02/27 职场文书
2015年上半年物业工作总结
2015/03/30 职场文书
聘任协议书(挂靠)
2015/09/21 职场文书