【问题标题】:TypeScript - she's gotta have it? (where it == global scope)TypeScript - 她必须拥有它吗? (它==全局范围)
【发布时间】:2015-02-08 20:54:48
【问题描述】:

我正在将 Angular 应用程序转换为使用 TypeScript,但这是一个通用的 TypeScript 问题,与 Angular 无关。 角度js文件是这样的:

(function () {
  var app = angular.module('myModule', []);
  app.controller('myController', ['$scope',
    function ($scope) {
      $scope.myNewProperty = "Bob";
    }
  ]);
})();

我已将其转换为可爱的 TypeScript 类语法:

class myController {
    constructor($scope) {
        $scope.myNewProperty = "Bob";
    }
}

angular.module('myModule', []).controller("myController", myController);

一切正常,除了生成的 JS 没有按照 JS 模块模式包装(即在外部匿名函数中):

var myController =     (function () {
      app.controller('myController', ['$scope',
        function ($scope) {
          $scope.myNewProperty = "Bob";
        }
      ]);
    })();

var app = angular.module('myModule', []);

所以 myController 现在是全局的。如果我将类放在 TypeScript 模块中,那么 js 会使用模块名称作为全局变量生成:

var TsModule;
(function (TsModule) {
    var myController =     (function () {
          app.controller('myController', ['$scope',
            function ($scope) {
              $scope.myNewProperty = "Bob";
            }
          ]);
        })();

    var app = angular.module('myModule', []);
})(TsModule || (TsModule = {}));

如何阻止 TypeScript 以这种方式污染全局范围?我只想将它全部包裹在一个不错的本地范围内。 我在别处看到它说“回到旧的 JS 语法”(即没有 TypeScript 类)。 How can I define an AngularJS service using a TypeScript class that doesn't pollute the global scope?

但是我们使用 TypeScript 的重点是 / 是 / 类语法。 TypeScript 是否与任何(明智的)知道不走向全球的 JS 程序员格格不入?

【问题讨论】:

  • 自从我使用 TypeScript 以来已经有一段时间了,所以我可能会停止使用这个,但是您是否尝试过将上面的 TypeScript 代码包装在一个立即调用的 lambda 函数中? (() => { // Code here... })();
  • 我感到很沮丧,但是为所有文件使用一个定义明确的模块名称几乎不会污染全局范围。它将在全局范围内创建一个变量,即使那样,如果您不导出您的类,那么它永远不会超过一个空对象。这是一个很小的问题。
  • 仅供参考(您可能已经知道)类表达式将在 TS 中出现,这将允许它们进入 IIFE。对于 OP,这个关于 TypeScript 模块模式的视频(我制作的)可能对你有用:youtube.com/watch?v=KDrWLMUY0R0&hd=1
  • @AluanHaddad - 是的,他们现在可以了。这在 2014 年 12 月是不允许的。

标签: javascript angularjs scope typescript


【解决方案1】:

快速更新

在最新版本的 TypeScript 中,您可以在 IIFE 中嵌套一个类:

(() => {
    class Example {

    }
})();

结果输出是:

(function () {
    var Example = (function () {
        function Example() {
        }
        return Example;
    }());
})();

原答案

您可以避免使用 AMD 或 CommonJS 等模块模式添加到全局范围。当您使用其中任何一个时,每个 TypeScript 文件都被视为一个外部模块,并且不在全局范围内。

此示例出于示例的目的删除了 Angular,但是当 RequireJS 将 define 方法添加到全局时,您的任何代码都不会放在此范围内。

MyModule.ts

export class myController {
    constructor($scope) {
        $scope.myNewProperty = "Bob";
    }
}

app.ts

import MyModule = require('MyModule');

var controller = new MyModule.myController('');

HTML

<script src="Scripts/require.js" data-main="app.js"></script>

app.js 的样子:

define(["require", "exports", 'MyModule'], function (require, exports, MyModule) {
    var controller = new MyModule.myController('');
});

或者...如您所知...如果您愿意,您仍然可以使用您已经在使用的 JavaScript 来实现它 - 您仍然可以获得自动完成和类型检查,即使您是'没有上课。

【讨论】:

  • 感谢您的回复。我不熟悉 AMD (RequireJS) 或 CommonJS。为此添加一个额外的库是我希望避免的复杂性(希望有一些 TS 解决方案)......但我会调查。
  • 这取决于您希望在多大程度上远离全局范围。您可以使用单个模块并使用每个模块对其进行扩展,这将是“全局范围内的唯一一件事”,这可能是您可以接受的。这个答案是“全局范围内没有代码”的答案。
【解决方案2】:

您可以简单地将您的类包装在 module

模块本身将是全局的,但如果您不导出类,则无需担心使用单个模块名称污染全局范围。

所以这个 TypeScript:

module MyModule {
  class MyClass {
     constructor(){}
  }
}

会产生如下JS:

var MyModule;
(function (MyModule) {
    var MyClass = (function () {
        function MyClass() {
        }
        return MyClass;
    })();
})(MyModule || (MyModule = {}));

【讨论】:

  • 您好,感谢您的回复。我正在编辑我的问题以扩展我的行:“如果我将类放在 TypeScript 模块中,那么 js 会使用模块名称作为全局变量生成:” - 编辑是生成的代码,非常漂亮和你的差不多。关键是(在您的示例中) var MyModule 现在是全局的。我们已经将控制器全局替换为模块全局。如果另一个 javascript 库有一个 MyModule 全局变量,那么就会出现混乱!
  • @user603563 - 并不是因为它使用了显示模块模式。因此,如果它已经存在,它不会重新定义模块。只有当您向该模块添加您希望外部可用的东西时,才会出现混乱。在这种情况下,您不会将类添加到模块中,因此永远不会发生冲突。这只是一个通过。
  • 我刚刚回到我的 JS 引用,是的,我重新了解到,与其对先前声明的 MyObject 进行核对,MyObject 的后续 var-ing 只会添加到现有对象(或者如果, 在这种情况下, 没有对 MyObject 做任何事情). 好的, 好吧(虽然我很讨厌, 因为它没有回答 TypeScript 是否可以完全避免污染全局范围) 我会将此标记为答案, 因为有了这些 cmets这确实充分解释了为什么它不像我想象的那么重要......
  • @AluanHaddad - 我明白这一点,但问题是专门关于使用 TypeScript 语法的。在撰写本文时,规范的做法是创建一个不带导出的module,这会创建一个闭包。从那以后情况发生了很大变化,内部模块是使用namespace 创建的。我可能会争辩说,从长远来看,使用真正的 es6 模块是一个更好的选择。
  • @Josh 是的,最好使用 es6 模块。不管问题是关于如何在不污染命名空间的情况下做到这一点。命名空间只是内部模块的一个新关键字。关键是对所提出的问题有一个非常简单的答案,而且不是您给出的答案,实际上没有人给出正确的答案。记住 JavaScript 是有效的 ThpeScript。正确答案见我的回复。
【解决方案3】:

乔希是正确的。使用模块。同样,使用 grunt 或 gulp uglifyer 可能是一个好主意,它可以选择在构建时将整个应用程序包装在一个闭包中。

这不是对您问题的回答,而是一个建议。

顺便说一句,考虑一下控制器的这种语法

module myModule {

    // We export the class so that we can access its interface in tests.
    // The build time gulp or grunt uglify will wrap our app in a closure
    // so that none of these exports will be available outside the app. 
    export class MyController {

         myNewProperty = "bob";

        // Next we will use Angulars $inject annotation to inject a service
        // into our controller. We will make this private so that it can be 
        // used internally.
        static $inject = ['someService'];
        constructor(private someService: ng.ISomeService) {}

        // we can access our class members using 'this'
        doSomething() {
            this.someService.doSomething(this.myNewProperty);
        }

    }

    angular.module('app').controller('MyController', MyController);

}

除了这种语法,您还可以使用 controllerAs 语法。

【讨论】:

    【解决方案4】:

    就像在 JavaScript 中一样,您可以通过将声明包装在立即调用的函数表达式中来完全避免命名空间污染。

    原来的JavaScript:

    (function () {
      var app = angular.module('myModule', []);
      app.controller('myController', ['$scope',
        function ($scope) {
          $scope.myNewProperty = "Bob";
        }
      ]);
    })();
    

    变成下面的 TypeScript:

    (function () {
      class MyController {
        static $inject = ['$scope'];
        contructor($scope: ng.IScope & { myNewProperty: string }) {
          $scope.myNewProperty = 'Bob';
        }
      }
    
      angular.module('myModule', [])
        .controller('MyController', MyController);
    })();
    

    请注意,这不会将名称引入周围范围。实际上,原始的 JavaScript 是完全有效的 TypeScript,但它没有利用 TypeScript。 另请注意,我对样式稍作了编辑。

    无论如何,如果您不使用带有模块加载器的模块,例如 RequireJS、SystemJS 或您拥有的其他模块,您仍然可以通过遵循久经考验的 IIFE 模式来避免命名空间污染。这是我的建议。

    【讨论】:

      猜你喜欢
      • 2020-07-14
      • 1970-01-01
      • 2013-02-25
      • 1970-01-01
      • 1970-01-01
      • 2012-04-29
      • 1970-01-01
      相关资源
      最近更新 更多