详解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 打印页面代码
Mar 24 Javascript
理解Javascript_07_理解instanceof实现原理
Oct 15 Javascript
动态添加删除表格行的js实现代码
Feb 28 Javascript
jQuery-1.9.1源码分析系列(十)事件系统之事件体系结构
Nov 19 Javascript
jQuery解析json格式数据简单实例
Jan 22 Javascript
JQuery点击行tr实现checkBox选中的简单实例
May 26 Javascript
购物车前端开发(jQuery和bootstrap3)
Aug 27 Javascript
js实现移动端微信页面禁止字体放大
Feb 16 Javascript
js a标签点击事件
Mar 30 Javascript
JavaScript中各数制转换全面总结
Aug 21 Javascript
详解React中传入组件的props改变时更新组件的几种实现方法
Sep 13 Javascript
详解Vue之父子组件传值
Apr 01 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
法压式咖啡之制作法
2021/03/03 冲泡冲煮
一首老MP3,致敬WAR3经典
2021/03/08 魔兽争霸
php 生成随机验证码图片代码
2010/02/08 PHP
PHP 5.3新特性命名空间规则解析及高级功能
2010/03/11 PHP
curl不使用文件存取cookie php使用curl获取cookie示例
2014/01/26 PHP
PHP判断文章里是否有图片的简单方法
2014/07/26 PHP
PHP实现简单搜歌的方法
2015/07/28 PHP
thinkphp3.x中display方法及show方法的用法实例
2016/05/19 PHP
利用PHP获取访客IP、地区位置、浏览器及来源页面等信息
2017/06/27 PHP
从零开始学习jQuery (六) jquery中的AJAX使用
2011/02/23 Javascript
jquery getScript动态加载JS方法改进详解
2012/11/15 Javascript
jQuery基本过滤选择器使用介绍
2013/04/18 Javascript
jquery的ajax跨域请求原理和示例
2014/05/08 Javascript
基于jQuery实现返回顶部实例代码
2016/01/01 Javascript
jQuery表格插件datatables用法详解
2020/11/23 Javascript
Bootstrap简单表单显示学习笔记
2016/11/15 Javascript
Vue系列:通过vue-router如何传递参数示例
2017/01/16 Javascript
bootstrap手风琴折叠示例代码分享
2017/05/22 Javascript
JavaScript基础心法 数据类型
2018/03/05 Javascript
vue.js在标签属性中插入变量参数的方法
2018/03/06 Javascript
js统计页面上每个标签的数量实例代码
2018/05/29 Javascript
vue项目中监听手机物理返回键的实现
2020/01/18 Javascript
python实用代码片段收集贴
2015/06/03 Python
Django REST framework 分页的实现代码
2019/06/19 Python
python实现爬取百度图片的方法示例
2019/07/06 Python
Ubuntu下Python+Flask分分钟搭建自己的服务器教程
2019/11/19 Python
Python三维绘图之Matplotlib库的使用方法
2020/09/20 Python
Python通过Schema实现数据验证方式
2020/11/12 Python
Python对excel的基本操作方法
2021/02/18 Python
Canvas 文本填充线性渐变的使用详解
2020/06/22 HTML / CSS
中文专业毕业生自荐信
2013/10/28 职场文书
2014年体育部工作总结
2014/11/13 职场文书
2016年春节问候语
2015/11/11 职场文书
医生行业员工的辞职信
2019/06/24 职场文书
python中super()函数的理解与基本使用
2021/08/30 Python
Python序列化模块JSON与Pickle
2022/06/05 Python