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

作者 : 开心源码 本文共4434个字,预计阅读时间需要12分钟 发布时间: 2022-05-12 共198人阅读

今天强行被某大厂社招面试了一波,洋洋洒洒(kengchibiedu)半个小时,以面试官的一句“面试反馈还挺好的,希望以后保持联络”告终。时间节点发生在安安稳稳回学校做毕设的时候,这场突如其来的面试就显得格外刺激,基础的数据结构和算法题都答复一二,但是也暴露了对框架和ES6底层代码实现的不求甚解。不经过刻意准备的面试,也是原始状态的真实表现。

这篇文章主要讲解面试中的一个问题 – 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的classconstructor是构造方法,对应的是ES5中的构造函数Personthis关键字则代表实例对象。

里面的class类可以看做是构造函数的另一种写法,由typeof Person === 'function'truePerson === Person.prototype.constructortrue可以得出,类的数据类型就是函数,类本身指向构造函数。也可以说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的)

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

class

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

  1. 运行模式
"use strict";//class默认开启严格模式
  1. 私有函数: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,设置 configurabletrue。以上两个布尔值是为了限制 Object.keys() 之类的方法被遍历到。通过判断 value 能否存在,来判断能否是 getter 和 setter。假如存在 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;}
  1. 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 Constructorfalse,抛出异常。
通过调用_createClass函数,遍历函数数组。key为方法名,若有value说明是有具体的 function 公告,若无 value 说明使用了get 或者 set 方法。

结尾

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

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » ES6中的class是如何实现的?(附Babel编译的ES5代码详解)

发表回复