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

栏目: JavaScript · 发布时间: 5年前

内容简介:今天强行被某大厂社招面试了一波,时长持续半个小时,以面试官的一句“面试反馈还挺好的,希望以后保持联系”告终。时间节点发生在辞掉实习回学校做毕设的时候,这场面试就显得格外刺激。基础的数据结构和算法题都回答一二,但是也暴露了对ES6底层代码实现的不求甚解。不经过刻意准备的面试,也是原始状态的真实体现。这篇文章主要讲解面试中的一个问题 -之前只是了解ECMAScript 6 实现了class,class是一个

今天强行被某大厂社招面试了一波,时长持续半个小时,以面试官的一句“面试反馈还挺好的,希望以后保持联系”告终。时间节点发生在辞掉实习回学校做毕设的时候,这场面试就显得格外刺激。基础的数据结构和算法题都回答一二,但是也暴露了对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提供 getset 关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为,和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);
复制代码
  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 ,就为 descriptor 添加 valuewritable 属性,如果不存在,就直接使用 getset 属性。

最后,使用 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 说明使用了 getset 方法。

结尾

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


以上所述就是小编给大家介绍的《ES6中的class是如何实现的?(附Babel编译的ES5代码详解)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

产品觉醒:产品经理的视角与方法论

产品觉醒:产品经理的视角与方法论

判官(李泽澄) / 电子工业出版社 / 2018-11 / 59.80元

《产品觉醒:产品经理的视角与方法论》是作者多年工作经验的汇集,通过自己亲身经历来对产品运营、行业和人生选择做一个全面的复盘,为读者提供有一定深度的分析。 《产品觉醒:产品经理的视角与方法论》共7章,第1章介绍了产品经理应该具有的视角来分析和观察产品分析方法;第2章介绍了做产品时如何破局来解决相应的问题;第3章介绍了在做产品经理前先分析自己;第4章介绍了怎么来解决执行力的问题;第5章介绍了怎么......一起来看看 《产品觉醒:产品经理的视角与方法论》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具