【问题标题】:Are babel decorators the same as TypeScript's?babel 装饰器和 TypeScript 的一样吗?
【发布时间】:2018-07-21 23:30:40
【问题描述】:

我经常用 TypeScript 写代码,但很少用 babel,我知道 TypeScript 是如何在装饰器上工作的,而且 babel 也支持装饰器,它们的作用是一样的吗?

【问题讨论】:

    标签: javascript typescript babeljs


    【解决方案1】:

    是的,它们是相同的,从某种意义上说它们产生相同的行为,但它们有不同的实现。

    两者都遵循 ECMAScript 规范并尽早为我们带来功能。您可以期望它们两者上的内容将来可能会被节点或浏览器支持。

    代码:

    function f() {
      console.log("f(): evaluated");
      return function (target, propertyKey, descriptor) {
          console.log("f(): called");
      }
    }
    
    function g() {
      console.log("g(): evaluated");
      return function (target, propertyKey, descriptor) {
          console.log("g(): called");
      }
    }
    
    class C {
      @f()
      @g()
      method() {}
    }
    
    new C().method();
    

    TypeScript 输出:

    var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
        var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
        if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
        else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
        return c > 3 && r && Object.defineProperty(target, key, r), r;
    };
    function f() {
        console.log("f(): evaluated");
        return function (target, propertyKey, descriptor) {
            console.log("f(): called");
        };
    }
    function g() {
        console.log("g(): evaluated");
        return function (target, propertyKey, descriptor) {
            console.log("g(): called");
        };
    }
    class C {
        method() { }
    }
    __decorate([
        f(),
        g()
    ], C.prototype, "method", null);
    new C().method();
    

    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; }; }();
    
    var _dec, _dec2, _desc, _value, _class;
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) {
      var desc = {};
      Object['ke' + 'ys'](descriptor).forEach(function (key) {
        desc[key] = descriptor[key];
      });
      desc.enumerable = !!desc.enumerable;
      desc.configurable = !!desc.configurable;
    
      if ('value' in desc || desc.initializer) {
        desc.writable = true;
      }
    
      desc = decorators.slice().reverse().reduce(function (desc, decorator) {
        return decorator(target, property, desc) || desc;
      }, desc);
    
      if (context && desc.initializer !== void 0) {
        desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
        desc.initializer = undefined;
      }
    
      if (desc.initializer === void 0) {
        Object['define' + 'Property'](target, property, desc);
        desc = null;
      }
    
      return desc;
    }
    
    function f() {
      console.log("f(): evaluated");
      return function (target, propertyKey, descriptor) {
        console.log("f(): called");
      };
    }
    
    function g() {
      console.log("g(): evaluated");
      return function (target, propertyKey, descriptor) {
        console.log("g(): called");
      };
    }
    
    var C = (_dec = f(), _dec2 = g(), (_class = function () {
      function C() {
        _classCallCheck(this, C);
      }
    
      _createClass(C, [{
        key: "method",
        value: function method() {}
      }]);
    
      return C;
    }(), (_applyDecoratedDescriptor(_class.prototype, "method", [_dec, _dec2], Object.getOwnPropertyDescriptor(_class.prototype, "method"), _class.prototype)), _class));
    
    
    new C().method();
    

    运行后输出:

    > ts-node ts-example.ts
    f(): evaluated
    g(): evaluated
    g(): called
    f(): called
    
    > node babel-example.js
    f(): evaluated
    g(): evaluated
    g(): called
    f(): called
    

    【讨论】:

    • 所以现在它们是相同的,还有一个问题,因为 JavaScript 什么时候支持Array.prototype.forEach?同一个版本是否支持类定义和 forEach?为什么 babel 使用 ES5 函数而不是 ES6 类,因为它可以使用 forEach。
    • Angular 装饰器是否与@babel/plugin-proposal-decorators 中的装饰器相同,其中legacy 选项设置为true?我猜他们不是,但我不确定...谢谢!
    【解决方案2】:

    不确定装饰器的最新状态,但半年前 Babel 和 TypeScript 装饰器在某些情况下的行为不同。装饰器不是 一个规范,它只是Stage 2。这就是为什么在 TypeScript 中装饰器是一个实验性功能。

    例如,来自core-decorators 的不可枚举在不久前在 TypeScript 中不起作用。几乎所有的装饰器都适用于这些领域。

    所以答案是:“它们相似,但不相等”。不要建议您编写一个装饰器,它在 babel 和 TypeScript 中的工作方式类似。直到它被标准化并且实现符合标准。

    【讨论】:

      【解决方案3】:

      不,它们是不同的。 Babel 不支持参数装饰器,TS 的属性装饰器也不支持属性的初始化器。

      【讨论】:

        猜你喜欢
        • 2018-03-16
        • 2019-02-23
        • 2016-02-21
        • 1970-01-01
        • 2015-08-19
        • 2017-02-04
        • 2016-05-18
        • 2016-02-05
        相关资源
        最近更新 更多