详解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 相关文章推荐
从新浪弄下来的全屏广告代码 与使用说明
Mar 15 Javascript
js文字横向滚动特效
Nov 11 Javascript
五种js判断是否为整数类型方式
Dec 03 Javascript
jquery判断复选框是否选中进行答题提示特效
Dec 10 Javascript
JavaScript仿网易选项卡制作代码
Oct 06 Javascript
bootstrap table插件的分页与checkbox使用详解
Jul 23 Javascript
解决vue select当前value没有更新到vue对象属性的问题
Aug 30 Javascript
详解在不使用ssr的情况下解决Vue单页面SEO问题
Nov 08 Javascript
一文快速了解JQuery中的AJAX
May 31 jQuery
怎么使用javascript深度拷贝一个数组
Jun 06 Javascript
vue-loader中引入模板预处理器的实现
Sep 04 Javascript
JavaScript通如何过RGraph实现动态仪表盘
Oct 15 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无限极分类原理
2019/03/14 PHP
深入聊聊Array的sort方法的使用技巧.详细点评protype.js中的sortBy方法
2007/04/12 Javascript
jQuery DOM操作小结与实例
2010/01/07 Javascript
用JavaScript对JSON进行模式匹配 (Part 2 - 实现)
2010/07/17 Javascript
jQuery学习笔记之Helloworld
2010/12/22 Javascript
JS中类或对象的定义说明
2014/03/10 Javascript
html文档中的location对象属性理解及常见的用法
2014/08/13 Javascript
javascript里使用php代码实例
2014/12/13 Javascript
js jquery获取当前元素的兄弟级 上一个 下一个元素
2015/09/01 Javascript
javascript基于prototype实现类似OOP继承的方法
2015/12/16 Javascript
vue从使用到源码实现教程详解
2016/09/19 Javascript
Boostrap栅格系统与自己额外定义的媒体查询的冲突问题
2017/02/19 Javascript
node-sass安装失败的原因与解决方法
2017/09/04 Javascript
支付宝小程序tabbar底部导航
2018/11/06 Javascript
JavaScript碎片—函数闭包(模拟面向对象)
2019/03/13 Javascript
vue实现PC端分辨率适配操作
2020/08/03 Javascript
jquery插件实现轮播图效果
2020/10/19 jQuery
antd Select下拉菜单动态添加option里的内容操作
2020/11/02 Javascript
[02:49]2018DOTA2亚洲邀请赛主赛事决赛日战况回顾 Mineski鏖战5局夺得辉耀
2018/04/10 DOTA
python通过socket查询whois的方法
2015/07/18 Python
python实现自动获取IP并发送到邮箱
2018/12/26 Python
django用户登录验证的完整示例代码
2019/07/21 Python
Python实现大数据收集至excel的思路详解
2020/01/03 Python
解析PyCharm Python运行权限问题
2020/01/08 Python
解决Keras中Embedding层masking与Concatenate层不可调和的问题
2020/06/18 Python
详解使用HTML5 Canvas创建动态粒子网格动画
2016/12/14 HTML / CSS
html5中audio支持音频格式的解决方法
2018/08/24 HTML / CSS
护士自荐信怎么写
2013/10/18 职场文书
自荐信要包含哪些内容
2013/11/06 职场文书
医学生自荐信
2013/12/03 职场文书
优秀毕业自我鉴定
2014/02/15 职场文书
社区爱国卫生月活动总结
2014/06/30 职场文书
2015会计试用期工作总结
2014/12/12 职场文书
工作简报怎么写
2015/07/21 职场文书
python解决12306登录验证码的实现
2021/04/18 Python
使用opencv-python如何打开USB或者笔记本前置摄像头
2022/06/21 Python