参考文章:深入理解JS中的对象(三):class 的工作原理
学习交流,如有错漏,敬请指摘,共同学习!!
通过在线工具 ES6 to ES5 来分析 class 背后真正的实现
// ES6
class People
等同于
// ES5
"use strict";
1. function _instanceof(instance, Constructor) {
2. if (
3. Constructor != null
4. && typeof Symbol !== "undefined"
5. && Constructor[Symbol.hasInstance]
6. ) {
7. return !!Constructor[Symbol.hasInstance](instance);
8. } else {
9. return instance instanceof Constructor;
10. }
11. }
12.
13. function _classCallCheck(instance, Constructor) {
14. if (!_instanceof(instance, Constructor)) {
15. throw new TypeError("Cannot call a class as a function(不能将类作为函数调用)");
16. }
17. }
18.
19. var People = function People() {
20. _classCallCheck(this, People);
21. };
先说结论:
总的来说class做了这么几件事:
定义一个函数,确定这个函数是不是用new关键字创造出来的
确定这个函数是构造函数(我认为本质上仍然判断是不是new创造出来的)
下面详解一下代码
解析一下这个代码,我们可以看到:
一、(19-21行)
定义了一个变量People
函数People赋值给变量People,即People为函数。
People函数中调用了一个函数:_classCallCheck(this, People)
二、(13-17行)
_classCallCheck(this, People)函数的参数一个是this,一个是People函数。
在_classCallCheck(this, People)函数中,通过自定义的函数_instanceof判断传进来的this的原型对象prototype是否在People的原型链上出现。
翻译成人话就是,他判断这个People是否是通过关键字new出来的。
这也是必须通过new创建class的原因
如果不是通过new创建的,则直接抛出异常
三、(1-11行)
我们回看_instanceof这个函数
它在if中连用三个条件同时成立,我刚开始看的时候根本看不懂,于是去知乎提问:
这里给大家po一下知乎大佬的回答,因为不能转载,所以大家直接过去看,是真的厉害!!!
大佬说这三个条件都是一种防御性条件判断,上古时期面对鱼龙混则的JavaScript就是这么写的。
接着说,判断完该有的工具都有,我们就可以在这个里面
这个对象是不是new出来的
Constructor[Symbol.hasInstance](instance)
这句
首先说一下它的形式:
即取一个对象的属性,括号中是它的参数,示例如下
let obj = {
a:function(a){console.log(a)}
}
obj["a"](1) // 1
Constructor :Constructor构造函数属于被实例化的特定类对象 。
构造函数初始化这个对象,并提供可以访问其私有信息的方法。
Symbol.hasInstance用于判断某对象是否为某构造器的实例。
上面那一句话就是判断People是否为Constructor的实例。
补充说明一下中括号的用法:
// 中括号。对象[属性]
var obj= {name:'jack'};
obj['2a'] = 'test'; // 创建对象属性
obj['name']; // -->jack 获取对象属性
obj['2a']; // --> test (不能经过obj.2a获取) 通过中括号创建的对象的属性只能通过中括号获取。
此文顶部的参考文章中有提到两个问题:
1-不能直接像函数调用一样调用类People(),必须通过 new 调用类,如 new People()
这是因为
如果直接调用 People(),
由于是严格模式下执行`(使用class关键字,必然导致其所在js为严格模式)`,
此时的 this 为 undefined`
(非严格模式中,直接调用,函数中this指向全局;如果是严格模式,则是undefined)`,
调用 _instanceof 函数检查继承关系其返回值必然为 false,
所以必然会抛出 TypeError 错误。
2-不会声明提前
是因为
class 声明的类转化为的是一个函数表达式,并且用变量 People 保存函数表达式的值
而 函数表达式 只能在代码执行阶段创建而且不存在于变量对象中
所以如果在 class 声明类之前使用,就相当于在给变量 People 赋值之前使用
此时使用是没有意义的,因为其值为 undefined,直接使用反而会报错
所以 ES6 就规定了在类声明之前访问类会抛出 ReferenceError 错误(类没有定义)。