前言

在了解 Babel 是如何编译 class 前,我们先看看 ES6 的 class 和 ES5 的构造函数是如何对应的。毕竟,ES6 的 class 可以看作一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

constructor

ES6 中:

class Person {constructor(name) {this.name = name;}sayHello() {return 'hello, I am ' + this.name;}
}var kevin = new Person('Kevin');
kevin.sayHello(); // hello, I am Kevin

对应到 ES5 中就是:

function Person(name) {this.name = name;
}Person.prototype.sayHello = function () {return 'hello, I am ' + this.name;
};var kevin = new Person('Kevin');
kevin.sayHello(); // hello, I am Kevin

我们可以看到 ES5 的构造函数 Person,对应 ES6 的 Person 类的 constructor 方法。

值得注意的是:类的内部所有定义的方法,都是不可枚举的(non-enumerable)

以上面的例子为例,在 ES6 中:

Object.keys(Person.prototype); // []
Object.getOwnPropertyNames(Person.prototype); // ["constructor", "sayHello"]

然而在 ES5 中:

Object.keys(Person.prototype); // ['sayHello']
Object.getOwnPropertyNames(Person.prototype); // ["constructor", "sayHello"]

实例属性

以前,我们定义实例属性,只能写在类的 constructor 方法里面。比如:

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

然而现在有一个提案,对实例属性和静态属性都规定了新的写法,而且 Babel 已经支持。现在我们可以写成:

class Person {state = {count: 0};
}

对应到 ES5 都是:

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

静态方法

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

ES6 中:

class Person {static sayHello() {return 'hello';}
}Person.sayHello() // 'hello'var kevin = new Person();
kevin.sayHello(); // TypeError: kevin.sayHello is not a function

对应 ES5:

function Person() {}Person.sayHello = function() {return 'hello';
};Person.sayHello(); // 'hello'var kevin = new Person();
kevin.sayHello(); // TypeError: kevin.sayHello is not a function

静态属性

静态属性指的是 Class 本身的属性,即 Class.propName,而不是定义在实例对象(this)上的属性。以前,我们添加静态属性只可以这样:

class Person {}Person.name = 'kevin';

因为上面提到的提案,现在可以写成:

class Person {static name = 'kevin';
}

对应到 ES5 都是:

function Person() {};Person.name = 'kevin';

new 调用

值得注意的是:类必须使用 new 调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用 new 也可以执行。

class Person {}Person(); // TypeError: Class constructor Foo cannot be invoked without 'new'

getter 和 setter

与 ES5 一样,在“类”的内部可以使用 get 和 set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

class Person {get name() {return 'kevin';}set name(newName) {console.log('new name 为:' + newName)}
}let person = new Person();person.name = 'daisy';
// new name 为:daisyconsole.log(person.name);
// kevin

对应到 ES5 中:

function Person(name) {}Person.prototype = {get name() {return 'kevin';},set name(newName) {console.log('new name 为:' + newName)}
}let person = new Person();person.name = 'daisy';
// new name 为:daisyconsole.log(person.name);
// kevin

Babel 编译

至此,我们已经知道了有关“类”的方法中,ES6 与 ES5 是如何对应的,实际上 Babel 在编译时并不会直接就转成这种形式,Babel 会自己生成一些辅助函数,帮助实现 ES6 的特性。

我们可以在 Babel 官网的 Try it out 页面查看 ES6 的代码编译成什么样子。

编译(一)

ES6 代码为:

class Person {constructor(name) {this.name = name;}
}

Babel 编译为:

"use strict";function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}
}var Person = function Person(name) {_classCallCheck(this, Person);this.name = name;
};

_classCallCheck 的作用是检查 Person 是否是通过 new 的方式调用,在上面,我们也说过,类必须使用 new 调用,否则会报错。

当我们使用 var person = Person() 的形式调用的时候,this 指向 window,所以 instance instanceof Constructor 就会为 false,与 ES6 的要求一致。

编译(二)

ES6 代码为:

class Person {// 实例属性foo = 'foo';// 静态属性static bar = 'bar';constructor(name) {this.name = name;}
}

Babel 编译为:

'use strict';function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}
}var Person = function Person(name) {_classCallCheck(this, Person);this.foo = 'foo';this.name = name;
};Person.bar = 'bar';

编译(三)

ES6 代码为:

class Person {constructor(name) {this.name = name;}sayHello() {return 'hello, I am ' + this.name;}static onlySayHello() {return 'hello'}get name() {return 'kevin';}set name(newName) {console.log('new name 为:' + newName)}
}

对应到 ES5 的代码应该是:

function Person(name) {this.name = name;
}Person.prototype =  {sayHello: function () {return 'hello, I am ' + this.name;},get name() {return 'kevin';},set name(newName) {console.log('new name 为:' + newName)}
}Person.onlySayHello = function () {return 'hello'
};

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 _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}
}var Person = function() {function Person(name) {_classCallCheck(this, Person);this.name = name;}_createClass(Person, [{key: 'sayHello',value: function sayHello() {return 'hello, I am ' + this.name;}}, {key: 'name',get: function get() {return 'kevin';},set: function set(newName) {console.log('new name 为:' + newName);}}], [{key: 'onlySayHello',value: function onlySayHello() {return 'hello';}}]);return Person;
}();

我们可以看到 Babel 生成了一个 _createClass 辅助函数,该函数传入三个参数,第一个是构造函数,在这个例子中也就是 Person,第二个是要添加到原型上的函数数组,第三个是要添加到构造函数本身的函数数组,也就是所有添加 static 关键字的函数。该函数的作用就是将函数数组中的方法添加到构造函数或者构造函数的原型中,最后返回这个构造函数。

在其中,又生成了一个 defineProperties 辅助函数,使用 Object.defineProperty 方法添加属性。

默认 enumerable 为 false,configurable 为 true,这个在上面也有强调过,是为了防止 Object.keys() 之类的方法遍历到。然后通过判断 value 是否存在,来判断是否是 getter 和 setter。如果存在 value,就为 descriptor 添加 value 和 writable 属性,如果不存在,就直接使用 get 和 set 属性。

写在后面

至此,我们已经了解了 Babel 是如何编译一个 Class 的,然而,Class 还有一个重要的特性就是继承,Class 如何继承,Babel 又该如何编译,欢迎期待下一篇《 ES6 系列之 Babel 是如何编译 Class 的(下)》

ES6 系列

ES6 系列目录地址:https://github.com/mqyqingfeng/Blog

ES6 系列预计写二十篇左右,旨在加深 ES6 部分知识点的理解,重点讲解块级作用域、标签模板、箭头函数、Symbol、Set、Map 以及 Promise 的模拟实现、模块加载方案、异步处理等内容。

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎 star,对作者也是一种鼓励。

ES6 系列之 Babel 是如何编译 Class 的(上)相关推荐

  1. ES6 系列之 let 和 const

    块级作用域的出现 通过 var 声明的变量存在变量提升的特性: if (condition) {var value = 1; } console.log(value); 复制代码 初学者可能会觉得只有 ...

  2. ES6 系列之模块加载方案

    前言 本篇我们重点介绍以下四种模块加载规范: AMD CMD CommonJS ES6 模块 最后再延伸讲下 Babel 的编译和 webpack 的打包原理. require.js 在了解 AMD ...

  3. 为什么let在php中报错,ES6系列之声明变量let与const

    本篇文章主要是向大家分享了关于ES6系列的声明变量let与const,有兴趣的朋友们可以参考一下本文中的内容 简介 概念 ES6 的第一个版本,在 2015 年 6 月发布了,正式名称就是<EC ...

  4. ES6系列_2之新的声明方式

    ES6系列_2之新的声明方式 在ES5中我们在声明时只有一种方法,就是使用var来进行声明,ES6对声明的进行了扩展,现在可以有三种声明方式. (1)var:它是variable的简写,可以理解成变量 ...

  5. 理解Babel是如何编译JS代码的及理解抽象语法树(AST)

    Babel是如何编译JS代码的及理解抽象语法树(AST) 1. Babel的作用是?    很多浏览器目前还不支持ES6的代码,但是我们可以通过Babel将ES6的代码转译成ES5代码,让所有的浏览器 ...

  6. 大白话,讲编程之《ES6系列连载》汇总,再也不用翻历史消息了

    想翻看以前的ES6系列章节,老是要查看公众号的历史消息,特别是在地铁里没wifi的时候,每次页面切换都要load很久,相当麻烦. 别怕,最懂你们的前端君又来了,来一篇完美的汇总,你要的ES6,都在这里 ...

  7. 重磅:为ES6系列设计的2套习题+答案解析

    ES6系列共20期的连载已经完满结束,但是我们对ES6的学习还不能停止. 学习ES6的重要性不言而喻,毕竟是JavaScript开发的趋势,所有开发者和运行平台都要向ES6规范靠拢.ES6知识已经成为 ...

  8. ES6 系列之 WeakMap的使用示例

    ES6 系列之 WeakMap的使用示例 这篇文章主要介绍了ES6 系列之 WeakMap的使用示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 前言 我们先从 Wea ...

  9. [ES6系列-01]Class:面向对象的“新仇旧恨”

    [原创]CoderPower 大家好,这里是码路工人有力量,我是码路工人,你们是力量. 这是公众号(码路工人有力量)开通后的第二篇,写得还是有待改进吧. 这次准备写一个关于ES6基础的短文系列,努力尽 ...

最新文章

  1. 谈谈Android重打包--架构
  2. 《你不知道的JavaScript》整理(五)——值与原生函数
  3. coursera 《现代操作系统》 -- 第十周 文件系统(2)
  4. leetcode 834. Sum of Distances in Tree | 834. 树中距离之和(树形DP)
  5. 计算机技术级生活中的应用,人工智能技术在计算机中的发展与应用
  6. php面试编程题_PHP程序员面试题(经典汇总,mysql为主)
  7. Spring Boot 使用 AOP 实现页面自适应
  8. 提高开发效率,eclipse的常用快捷键、旁门左道、系统错误小贴士
  9. 拓端tecdat|R语言使用ARIMA模型预测股票收益时间序列
  10. 计算机组成原理试题和答案2017,【2017年整理】计算机组成原理试题及答案9.doc...
  11. 打印当前html页面 有背景,word打印时页面背景颜色怎么去掉
  12. Fabric2.3分布式部署(fabric-ca)
  13. Python自动连接网络(自动登录网络准入系统)
  14. win7python怎么设置环境变量_如何在win7下设置python的环境变量
  15. PyTorch学习笔记2:nn.Module、优化器、模型的保存和加载、TensorBoard
  16. python操作excel遇到一系列问题
  17. word文档添加批注
  18. Octopus Deploy云程序部署,真正正确地完成其工作
  19. Linux查看进程 ps aux指令详解
  20. 串口服务器的作用和工作原理是什么

热门文章

  1. java判断类型_Java中类型判断的几种方式 - 码农小胖哥 - 博客园
  2. impala元数据放到mysql_impala系列: 同步Hive元数据和收集统计信息
  3. java多语言编程语言_为什么很多程序员信仰“Java是世界上最好的编程语言”
  4. STM32开启定时器就立即进Update中断问题探索
  5. 强制生成32位arm程序_ARM版本系列及家族成员梳理
  6. html怎么用chrome测试,通过chrome调试器测试了解浏览器解析和渲染HTML的过程
  7. abaqus的python安装文件在哪_在abaqus2016中安装xlwt和xlrd库教程
  8. Linux节点之间无密码问题,Linux下多节点SSH无密码互联实现
  9. 消息队列mysql redis那个好_Redis作为消息队列与RabbitMQ的比较
  10. 计算机技术在办公自动化中的应用,计算机技术在办公自动化中的应用浅析.doc...