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 相关文章推荐
dwr spring的集成实现代码
Mar 22 Javascript
JS将所有对象s的属性复制给对象r(原生js+jquery)
Jan 25 Javascript
由浅入深剖析Angular表单验证
Jul 14 Javascript
jQuery实现带遮罩层效果的blockUI弹出层示例【附demo源码下载】
Sep 14 Javascript
10分钟掌握XML、JSON及其解析
Dec 06 Javascript
js 性能优化之快速响应的用户界面
Feb 15 Javascript
javascript ES6 新增了let命令使用介绍
Jul 07 Javascript
Vue filter介绍及其使用详解
Oct 21 Javascript
微信小程序实现点击按钮移动view标签的位置功能示例【附demo源码下载】
Dec 06 Javascript
JavaScript实现全选取消效果
Dec 14 Javascript
Vue 动态设置路由参数的案例分析
Apr 24 Javascript
微信小程序获取公众号文章列表及显示文章的示例代码
Mar 10 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
杏林同学录(九)
2006/10/09 PHP
PHP 查找字符串常用函数介绍
2012/06/07 PHP
PHP中Cookie的使用详解(简单易懂)
2017/04/28 PHP
PHPstorm激活码2020年5月13日亲测有效
2020/09/17 PHP
javascript与CSS复习(《精通javascript》)
2010/06/29 Javascript
JavaScript 原型继承
2011/12/26 Javascript
用javascript将数据导入Excel示例代码
2014/09/10 Javascript
JavaScript中诡异的delete操作符
2015/03/12 Javascript
JavaScript获取网页中第一个图片id的方法
2015/04/03 Javascript
jQuery插件Tooltipster实现漂亮的工具提示
2015/04/12 Javascript
jQuery实现鼠标经过购物车出现下拉框代码(推荐)
2016/07/21 Javascript
JS实现中文汉字按拼音排序的方法
2017/10/09 Javascript
vue嵌套路由与404重定向实现方法分析
2018/05/04 Javascript
Vue引入sass并配置全局变量的方法
2018/06/27 Javascript
vue中使用codemirror的实例详解
2018/11/01 Javascript
Jquery+AJAX实现无刷新上传并重命名文件操作示例【PHP后台接收】
2020/05/29 jQuery
小程序表单认证布局及验证详解
2020/06/19 Javascript
如何在postman中添加cookie信息步骤解析
2020/06/30 Javascript
Javascript实现贪吃蛇小游戏(含详细注释)
2020/10/23 Javascript
详解React中共享组件逻辑的三种方式
2021/02/02 Javascript
[51:27]LGD vs Liquid 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
Python 文件操作实现代码
2009/10/07 Python
Python使用Matplotlib模块时坐标轴标题中文及各种特殊符号显示方法
2018/05/04 Python
python 快速把超大txt文件转存为csv的实例
2018/10/26 Python
Django对数据库进行添加与更新的例子
2019/07/12 Python
Python自动化测试笔试面试题精选
2020/03/12 Python
Python3.7将普通图片(png)转换为SVG图片格式(网站logo图标)动起来
2020/04/21 Python
keras模型保存为tensorflow的二进制模型方式
2020/05/25 Python
美国著名童装品牌:OshKosh B’gosh
2016/08/05 全球购物
央视元宵晚会主持串词
2014/03/25 职场文书
跳槽求职信范文
2014/05/26 职场文书
公司年底活动方案
2014/08/17 职场文书
乡镇党的群众路线教育实践活动个人整改方案
2014/10/31 职场文书
信贷客户经理岗位职责
2015/04/09 职场文书
上帝也疯狂观后感
2015/06/09 职场文书
《我在为谁工作》:工作的质量往往决定生活的质量
2019/12/27 职场文书