ES6中的class是如何实现的(附Babel编译的ES5代码详解)


Posted in Javascript onMay 17, 2019

序言

这篇文章主要讲解面试中的一个问题 - ES6中的class语法的实现?

ECMAScript 6 实现了class,class是一个语法糖,使得js的编码更清晰、更人性化、风格更接近面向对象的感觉;也使 IDE 、编译期类型检查器、代码风格检查器等工具更方便地检测代码语法,做静态分析。同样的,这给没有类就缺点什么的软件开发工程师一个更低的门槛去接触js。

ES6 class 的 ES5 代码实现

JavaScript语言的传统方法是通过构造函数定义并生成新对象,这种写法和传统的面向对象语言差异较大。所以,ES6引入了Class这个概念作为对象的模板。

constructor

效果:ES6创建一个class会默认添加constructor方法,并在new调用时自动调用该方法。

ES5:

function Person(name, age) {
 this.name = name;
 this.age = age;
}

Person.prototype.toString = function () {
 return '(' + this.name + ',' + this.age + ')';
}
var p = new Person('Mia', 18);
console.log(p);// Person { name: 'Mia', age: 18 }

ES6:

class Person {
 constructor(name, age) {
  this.name = name;
  this.age = age;
 }
 toString() {
  return '(' + this.name + ',' + this.age + ')';
 }
}
var p = new Person('Mia', 18);
console.log(p);// Person { name: 'Mia', age: 18 }

ES6的class中constructor是构造方法,对应的是ES5中的构造函数Person,this关键字则代表实例对象。

里面的class类可以看做是构造函数的另一种写法,由typeof Person === 'function'为true;Person === Person.prototype.constructor为true可以得出,类的数据类型就是函数,类本身指向构造函数。也可以说class的底层依然是function构造函数。

类的公共方法都定义在类的prototype属性上。可以使用Object.assign一次向类添加多个方法。

特别的:class的内部定义的方法都是不可枚举的(non-enumerable),这一点与ES5的行为不一致。

ES5:

Object.keys(Person.prototype); // ['toString']

ES6:

Object.keys(Person.prototype); // Person {}

不可枚举的代码实现会在后面将ES6代码用Babel转码之后解析。

new调用

效果:class类必须使用new调用,否则会报错。

ES5:

Person()// undefined

ES6:

Person() // TypeError: Class constructor Person cannot be invoked without 'new'

实例的属性

效果:实例的属性是显式定义在this对象上,否则都是定义在原型上。类的所有实例共享一个原型对象,与ES5行为一致。

ES5:

function Person() {
 this.grade = {
  count: 0
 };
}

ES6:

class Person {
 constructor() {
  this.grade = {
   count: 0
  };
 }
}

此外还要关注新提案,Babel已经支持实例属性和静态属性新的写法。

静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

注意:如果静态方法包含this关键字,指的是类。

ES5:

function Person() { }
Person.toSay = function () {
 return 'I love JavaScript.';
};
Person.toSay(); // I love JavaScript.

ES6:

class Person {
 static toSay() {
  return 'I love JavaScript.';
 }
}
Person.toSay(); // I love JavaScript.

getter 和 setter

ES6提供 get 和 set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为,和ES5行为一致。

ES5:

function Person(name) {}
Person.prototype = {
 get name() {
  return 'mia';
 },
 set name(newName) {
  console.log('new name:' + newName);
 }
}

ES6:

class Person {
 get name() {
  return 'mia';
 }
 set name(newName) {
  console.log('new name:' + newName);
 }
}

ES6 class 底层实现原理

下文主要用babel转码器分别对class中几个主要的方法进行转码,分析ES5的实现方式。

Babel是如何编译class的

将下面的代码使用babel转码器转换成ES5代码,按照代码结构和功能分块进行讲解。

class Person {
 constructor(name, age) {
  this.name = name;
  this.age = age;
 }
 toString() {
  return '(' + this.name + ',' + this.age + ')';
 }
}
var p = new Person('Mia', 18);

运行模式

"use strict";//class默认开启严格模式

私有函数:

JS开发者在变量名或函数名前缀加下划线,一般表示私有。

前缀加下划线表示私有仅仅是一个约定俗成的习惯,澄清意图,并没有做其他处理。由于ECMAScript草案中并没有定义私有变量的方法,所以在此限定之下仍可以在函数外或作用域外访问该函数或变量。

_instanceof和_classCallCheck的作用

检查声明的class类是否通过new的方式调用,否则会报错。

function _instanceof(left, right) {
 if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
  return right[Symbol.hasInstance](left);
 } else {
  return left instanceof right;
 }
}
function _classCallCheck(instance, Constructor) {
 if (!_instanceof(instance, Constructor)) {
  throw new TypeError("Cannot call a class as a function");
 }
}

_createClass和_defineProperties的作用

_createClass函数有三个参数,Constructor是传入构造函数Person,protoProps 是要添加到原型上的函数数组,staticProps 是要添加到构造函数本身的函数,即静态方法。这里的第二个和第三个参数是可以缺省的,会在_createClass 函数体内判断。

_createClass 函数的作用是收集公有函数和静态方法,将方法添加到构造函数或构造函数的原型中,并返回构造函数。

defineProperties 是将方法添加到构造函数或构造函数的原型中的主要逻辑,遍历函数数组,分别声明其描述符。若enumerable 没有被定义为true,则默认为fals,设置 configurable 为true。以上两个布尔值是为了限制 Object.keys() 之类的方法被遍历到。如果存在 value,就为 descriptor 添加 value 和 writable 属性,如果不存在,就直接使用 get 和 set 属性。

最后,使用 Object.defineProperty 方法为构造函数添加属性。

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);
 }
}
function _createClass(Constructor, protoProps, staticProps) {
 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
 if (staticProps) _defineProperties(Constructor, staticProps);
 return Constructor;
}

class类实现

var Person =
 /*#__PURE__*/
 function () {
  function Person(name, age) {
   _classCallCheck(this, Person);

   this.name = name;
   this.age = age;
  }

  _createClass(Person, [{
   key: "toString",
   value: function toString() {
    return '(' + this.name + ',' + this.age + ')';
   }
  }]);

  return Person;
 }();

var p = new Person('Mia', 18);

解析:

不使用new调用时,this指向window,所以instance instanceof Constructor为false,抛出异常。

通过调用_createClass函数,遍历函数数组。key为方法名,若有value说明是有具体的 function 声明,若无 value 说明使用了get 或 set 方法。

结尾

读到这相信大家对class的实现有了更深的理解。最近笔者一边在忙毕业设计,一边整理了这道阿里前端面试题的解析,评论区欢迎对class实现这一问题进行讨论。另外,class中的extend也是很有趣的实现,在下一篇文章会对class实现继承进行解析。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js判断undefined类型,undefined,null, 的区别详细解析
Dec 16 Javascript
JavaScript中循环遍历Array与Map的方法小结
Mar 12 Javascript
jquery动态遍历Json对象的属性和值的方法
Jul 27 Javascript
jQuery简单倒计时效果完整示例
Sep 20 Javascript
JavaScript实现多栏目切换效果
Dec 12 Javascript
JS数字千分位格式化实现方法总结
Dec 16 Javascript
详解JavaScript常量定义
Jan 03 Javascript
AngularJS ng-repeat指令中使用track by子语句解决重复数据遍历错误问题
Jan 21 Javascript
Angular2学习教程之TemplateRef和ViewContainerRef详解
May 25 Javascript
jquery DataTable实现前后台动态分页
Jun 17 jQuery
p5.js入门教程之键盘交互
Mar 19 Javascript
详解关于表格合并span-method方法的补充(表格数据由后台动态返回)
May 21 Javascript
微信小程序云开发实现数据添加、查询和分页
May 17 #Javascript
js常用正则表达式集锦
May 17 #Javascript
Angular请求防抖处理第一次请求失效问题
May 17 #Javascript
vue cli 3.0 搭建项目的图文教程
May 17 #Javascript
小程序云开发如何实现图片上传及发表文字
May 17 #Javascript
tsconfig.json配置详解
May 17 #Javascript
小程序云函数调用API接口的方法
May 17 #Javascript
You might like
PHP实现HTML生成PDF文件的方法
2014/11/07 PHP
laravel实现上传图片并在页面显示的例子
2019/10/14 PHP
Js获取事件对象代码
2010/08/05 Javascript
js常用代码段整理
2011/11/30 Javascript
js展开闭合效果演示代码
2013/07/24 Javascript
JavaScript禁止页面操作的示例代码
2013/12/17 Javascript
js实现iframe跨页面调用函数的方法
2014/12/13 Javascript
自定义Angular指令与jQuery实现的Bootstrap风格数据双向绑定的单选与多选下拉框
2015/12/12 Javascript
JS实现图片的不间断连续滚动的简单实例
2016/06/03 Javascript
各式各样的导航条效果css3结合jquery代码实现
2016/09/17 Javascript
移动适配的几种方案(三种方案)
2016/11/25 Javascript
快速实现JS图片懒加载(可视区域加载)示例代码
2017/01/04 Javascript
微信小程序 Buffer缓冲区的详解
2017/07/06 Javascript
基于js 字符串indexof与search方法的区别(详解)
2017/12/04 Javascript
JQuery animate动画应用示例
2019/05/14 jQuery
微信小程序如何调用图片接口API并居中显示
2019/06/29 Javascript
JavaScript中使用Spread运算符的八种方法总结
2020/06/18 Javascript
详谈vue中router-link和传统a链接的区别
2020/07/22 Javascript
python检查URL是否正常访问的小技巧
2017/02/25 Python
windows上安装Anaconda和python的教程详解
2017/03/28 Python
python使用Flask操作mysql实现登录功能
2018/05/14 Python
OPENCV去除小连通区域,去除孔洞的实例讲解
2018/06/21 Python
Python txt文件加入字典并查询的方法
2019/01/15 Python
Python qqbot 实现qq机器人的示例代码
2019/07/11 Python
python matlab库简单用法讲解
2020/12/31 Python
纯CSS实现菜单、导航栏的3D翻转动画效果
2014/04/23 HTML / CSS
印度最大的酒店品牌网络:OYO Rooms
2016/07/24 全球购物
Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?用contains来区分是否有重复的对象。还是都不用
2013/07/30 面试题
策划助理岗位职责
2013/11/18 职场文书
装潢设计专业推荐信模板
2013/11/26 职场文书
党员廉洁自律承诺书
2014/05/26 职场文书
篮球比赛拉拉队口号
2014/06/10 职场文书
公共机构节能宣传周活动总结
2014/07/09 职场文书
公安派出所所长四风问题个人对照检查材料
2014/10/04 职场文书
团员个人总结
2015/02/26 职场文书
Python 中 Shutil 模块详情
2021/11/11 Python