ES6 系列之 Babel 是如何编译 Class 的(上)
栏目: JavaScript · 发布时间: 6年前
内容简介:在了解 Babel 是如何编译 class 前,我们先看看 ES6 的 class 和 ES5 的构造函数是如何对应的。毕竟,ES6 的 class 可以看作一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。ES6 中:对应到 ES5 中就是:
前言
在了解 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 为:daisy console.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 为:daisy console.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,对作者也是一种鼓励。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Xcode 编译疾如风系列(二):并行编译
- 编写 MSBuild 内联编译任务(Task)用于获取当前编译环境下的所有编译目标(Target)
- 使用 Visual Studio 编译时,让错误一开始发生时就停止编译(以便及早排查编译错误节省时间)
- Go编译缓存导致C文件修改后未重新编译
- Android Apk反编译系列教程(一)如何反编译APK
- 漫话:如何给女朋友解释什么是编译与反编译
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
算法分析-有效的学习方法(影印版)
Jeffrey J.McConnell / 高等教育出版社 / 2003-03-01 / 28.0
本书主要目标是提高读者关于算法对程序效率的影响等问题的认知水平,并培养读者分析程序中的算法所必需的技巧。各章材料以激发读者有效的、协同的学习方法的形式讲述。通过全面的论述和完整的数学推导,本书帮助读者最大限度地理解基本概念。 本书内容包括促使学生参与其中的大量程序设计课题。书中所有算法以伪码形式给出,使得具备条件表达式、循环与递归方面知识的读者均易于理解。本书以简洁的写作风格向读者介绍了兼具......一起来看看 《算法分析-有效的学习方法(影印版)》 这本书的介绍吧!