详解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中使用&quot;with&quot;语句中跨frame的变量引用问题
Mar 08 Javascript
Jquery iframe内部出滚动条
Feb 11 Javascript
jquery导航制件jquery鼠标经过变色效果示例
Dec 05 Javascript
jQuery实现的tab标签切换效果示例
Sep 05 Javascript
yarn与npm的命令行小结
Oct 20 Javascript
JavaScript实现事件的中断传播和行为阻止方法示例
Jan 20 Javascript
详解浏览器渲染页面过程
Feb 09 Javascript
bootstrap模态框远程示例代码分享
May 22 Javascript
浅谈JS对html标签的属性的干预以及对CSS样式表属性的干预
Jun 25 Javascript
JavaScript实现二叉树定义、遍历及查找的方法详解
Dec 20 Javascript
微信小程序视图容器(swiper)组件创建轮播图
Jun 19 Javascript
js获取url页面id,也就是最后的数字文件名
Sep 25 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
盘点被央视点名过的日本动画电影 一部比一部强
2020/03/08 日漫
php中的时间处理
2006/10/09 PHP
php轻松实现文件上传功能
2016/03/03 PHP
PHP中soap用法示例【SoapServer服务端与SoapClient客户端编写】
2018/12/25 PHP
DWR实现模拟Google搜索效果实现原理及代码
2013/01/30 Javascript
用js代码和插件实现wordpress雪花飘落效果的四种方法
2014/12/15 Javascript
浅析javascript 定时器
2014/12/23 Javascript
javascript实时显示北京时间的方法
2015/03/12 Javascript
JavaScript中使用Object.prototype.toString判断是否为数组
2015/04/01 Javascript
理解javascript封装
2016/02/23 Javascript
jquery validate表单验证插件
2016/09/06 Javascript
jQuery实现动态生成表格并为行绑定单击变色动作的方法
2017/04/17 jQuery
node.js 中间件express-session使用详解
2017/05/20 Javascript
Angular2安装angular-cli
2017/05/21 Javascript
JavaScript中错误正确处理方式小结你用对了吗
2017/10/10 Javascript
vue+vuecli+webpack中使用mockjs模拟后端数据的示例
2017/10/24 Javascript
Vue 项目代理设置的优化
2018/04/17 Javascript
JS选取DOM元素常见操作方法实例分析
2018/12/10 Javascript
基于vue开发微信小程序mpvue-docs跳转页面功能
2019/04/10 Javascript
JS实现的对象去重功能示例
2019/06/04 Javascript
Vue代码整洁之去重方法整理
2019/08/06 Javascript
解决VUE项目localhost端口服务器拒绝连接,只能用127.0.0.1的问题
2020/08/14 Javascript
用python实现对比两张图片的不同
2018/02/05 Python
详解python中的装饰器
2018/07/10 Python
10分钟教你用Python实现微信自动回复功能
2018/11/28 Python
使用Python向C语言的链接库传递数组、结构体、指针类型的数据
2019/01/29 Python
Python docx库用法示例分析
2019/02/16 Python
Python小白必备的8个最常用的内置函数(推荐)
2019/04/03 Python
win7上tensorflow2.2.0安装成功 引用DLL load failed时找不到指定模块 tensorflow has no attribute xxx 解决方法
2020/05/20 Python
django为Form生成的label标签添加class方式
2020/05/20 Python
荷兰最大的鞋子、服装和运动折扣店:Bristol
2021/01/07 全球购物
个人简历中自我评价
2014/02/11 职场文书
新教师个人工作总结
2015/02/06 职场文书
心灵捕手观后感
2015/06/02 职场文书
繁星春水读书笔记
2015/06/30 职场文书
一文弄懂MySQL索引创建原则
2022/02/28 MySQL