了解babel是如何将class语法糖转换为es5的语法

准备工作

  • 在目录下创建两个文件,分别是index.js和.babelrc
js 复制代码
class Parent {
    static type = 'Parent';
    #salary = 0;
    name = '';
    static showType() {
        console.log(this.type)
    }
    #seeSalary() {
        console.log(this.#salary)
    }
    speakName() {
        this.#seeSalary()
        console.log(this.name)
    }
}

class Child extends Parent {
    constructor(name) {
        super();
        this.name = name;
    }
    speakName() {
        super.speakName();
    }
}
babelrc 复制代码
{
    "presets": [
        "@babel/preset-env"
    ]
}
shell 复制代码
npm install @babel/core @babel/cli @babel/preset-env --save-dev
npx babel index.js -o dist.js

编译后私有变量和方法怎么存储?

js 复制代码
var _salary = new WeakMap();
var _seeSalary = new WeakSet();
var Parent = function () {
  function Parent() {
    _classPrivateMethodInitSpec(this, _seeSalary);
    _classPrivateFieldInitSpec(this, _salary, {
      writable: true,
      value: 0
    });
  }
}
function _seeSalary2() {
  console.log(_classPrivateFieldGet(this, _salary));
}

function _classPrivateMethodInitSpec(obj, privateSet) {
  privateSet.add(obj); 
}
function _classPrivateFieldInitSpec(obj, privateMap, value) {
  privateMap.set(obj, value);
}
  • 总结这部分代码:私有变量会存储在weakMap中,键是对象,值是变量值;私有方法存储在weakSet中,键是对象。对于方法存储了对象还不够,执行方法是需要函数体的,函数体定义在外部。
  • 问题是,我调用的是seeSalary又不是seeSalary2。首先要说的是,私有方法只能在类方法中调用,外部是没办法调用的,那么在方法中调用的时候会对调用seeSalary进行拦截去执行seeSalary2
  • 还有一个问题,调用babel转换为es5的语法,怎么还有weakMapweakSet呢?Babel 是包含编译和polyfill两部分的。

编译后静态或公开变量和方法怎么存储?

js 复制代码
var Parent = function() {
  function Parent() {
    ...
    _defineProperty(this, "name", '');
  }
  // 第一个参数是public方法,第二个参数是static方法
  _createClass(Parent, [{
    key: "speakName",
    value: function speakName() {
      _classPrivateMethodGet(this, _seeSalary, _seeSalary2).call(this);
      console.log(this.name);
    }
  }], [{
    key: "showType",
    value: function showType() {
      console.log(this.type);
    }
  }]);
  return Parent;
}
_defineProperty(Parent, "type", 'Parent');

function _createClass(Constructor, protoProps, staticProps) { 
  if (protoProps) _defineProperties(Constructor.prototype, protoProps); 
  if (staticProps) _defineProperties(Constructor, staticProps);
  Object.defineProperty(Constructor, "prototype", { writable: false });
  return Constructor; 
}
function _classPrivateMethodGet(receiver, privateSet, fn) { 
  if (!privateSet.has(receiver)) { 
    throw new TypeError("attempted to get private field on non-instance"); 
  } 
  return fn; 
}
  • 总结这部分代码:public的属性会定义在实例自身上,public的方法会定义在构造器的prototype身上;static的属性和方法都会定义在类自身上。

那么继承是怎么实现的?

js 复制代码
var Child = function(_Parent2) {
  _inherits(Child, _Parent2);
  function Child(name) {
    var _this;
    _this = _callSuper(this, Child);
    _this.name = name;
    return _this;
  }
  _createClass(Child, [{
    key: "speakName",
    value: function speakName() {
      _get(_getPrototypeOf(Child.prototype), "speakName", this).call(this);
    }
  }]);
  return Child; 
}(Parent);

function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function");
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } });
  Object.defineProperty(subClass, "prototype", { writable: false });
  if (superClass) _setPrototypeOf(subClass, superClass); 
}
function _setPrototypeOf(o, p) { 
  _setPrototypeOf = Object.setPrototypeOf ?
    Object.setPrototypeOf.bind() :
    function _setPrototypeOf(o, p) { o.__proto__ = p; return o; };
  return _setPrototypeOf(o, p);
}
function _callSuper(t, o, e) {
  return o = 
    _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? 
      Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) :
      o.apply(t, e)); 
}
  • 总结这部分代码:继承是通过修改子类的prototype指向了一个新对象,新对象的prototype指向了父类的prototype,且新对象的constructor保持子类的constructor不变且允许外部代码修改。然后将子类的prototype拒绝被赋值为其他对象。最后将子类的__proto__指向父类。
  • 有个问题,为什么要创建一个新对象而不是让子类的prototype直接指向父类的prototype呢?这是因为在之前代码中给子类prototype添加公开方法的时候避免影响父类。
  • 还有一个问题,为什么需要__proto__指向父类呢?这是为了静态属性和方法也能让子类调用到父类的,前面也提到了静态的方法和属性都是挂载到类自身。

拓展:原型链

  • 每个对象(数组、函数等)都有__proto__属性,通过该属性指向其他对象串联出原型链
  • 函数不仅仅有__proto__还有prototype,但是寻找原型链并不会经过prototype,除非你是new了一个类,因为new关键字将类的prototype作为实例__proto__的值了。

例子

  • 例一
js 复制代码
function P() {}
P.prototype.x = 'x'

function C() {}
C.prototype = P.prototype
console.log(C.x) // undefined
  • 例二
js 复制代码
function P() {}
P.prototype.x = 'x'
P.x = 'xxx'

function C() {}
C.__proto__ = P;
console.log(C.x)  // xxx
相关推荐
WeiXiao_Hyy40 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡1 小时前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone1 小时前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农2 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king2 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳2 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵3 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星3 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js