详解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验证Checkbox是否选中的代码 推荐
Sep 04 Javascript
jQuery获取iframe的document对象的方法
Oct 10 Javascript
PHP中使用微秒计算脚本执行时间例子
Nov 19 Javascript
13 款最热门的 jQuery 图像 360 度旋转插件推荐
Dec 09 Javascript
jQuery获取页面元素绝对与相对位置的方法
Jun 10 Javascript
Bootstrap常用组件学习(整理)
Mar 24 Javascript
使用JavaScriptCore实现OC和JS交互详解
Mar 28 Javascript
解决vue+element 键盘回车事件导致页面刷新的问题
Aug 25 Javascript
vue router 源码概览案例分析
Oct 09 Javascript
手挽手带你学React之React-router4.x的使用
Feb 14 Javascript
Vue infinite update loop的问题解决
Apr 23 Javascript
使用layui 的layedit定义自己的toolbar方法
Sep 18 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/01 无线电
用PHP连接MySQL代码的参数说明
2008/06/07 PHP
PHP获取当前页面URL函数实例
2014/10/22 PHP
浅谈PHP中output_buffering
2015/07/13 PHP
PHP实现图片的等比缩放和Logo水印功能示例
2017/05/04 PHP
jQuery maxlength文本字数限制插件
2010/04/16 Javascript
php上传图片并给图片打上透明水印的代码
2010/06/07 Javascript
克隆javascript对象的三个方法小结
2011/01/12 Javascript
ichart.js绘制虚线、平均分虚线效果的实现代码
2016/05/05 Javascript
详解堆的javascript实现方法
2016/11/29 Javascript
探讨跨域请求资源的几种方式(总结)
2016/12/02 Javascript
jQuery使用bind函数实现绑定多个事件的方法
2017/10/11 jQuery
Angular2学习笔记之数据绑定的示例代码
2018/01/03 Javascript
mongodb初始化并使用node.js实现mongodb操作封装方法
2019/04/02 Javascript
微信小程序实现多图上传
2020/06/19 Javascript
Vue.js中Line第三方登录api的实现代码
2020/06/29 Javascript
python模拟新浪微博登陆功能(新浪微博爬虫)
2013/12/24 Python
一些Python中的二维数组的操作方法
2015/05/02 Python
构建Python包的五个简单准则简介
2015/06/15 Python
请不要重复犯我在学习Python和Linux系统上的错误
2016/12/12 Python
详解Django中间件执行顺序
2018/07/16 Python
Sanic框架基于类的视图用法示例
2018/07/18 Python
Python读取excel指定列生成指定sql脚本的方法
2018/11/28 Python
python可视化实现代码
2019/01/15 Python
使用python批量修改XML文件中图像的depth值
2020/07/22 Python
FitFlop澳大利亚官网:英国符合人体工学的鞋类品牌
2017/06/05 全球购物
计算机应用专业推荐信
2013/11/13 职场文书
高中毕业自我鉴定
2013/12/13 职场文书
职工趣味运动会方案
2014/02/10 职场文书
《最可爱的人》教学反思
2014/02/14 职场文书
应急管理培训方案
2014/06/12 职场文书
财务科长个人对照检查材料
2014/09/18 职场文书
党员批评与自我批评发言稿
2014/10/14 职场文书
优秀团员主要事迹材料
2015/11/05 职场文书
小学音乐课教学反思
2016/02/18 职场文书
纯html+css实现Element loading效果
2021/08/02 HTML / CSS