我正在将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污染全局范围?我只是希望它包含在一个很好的本地范围内。 我已经看到它在其他地方说过#34;只需回到旧的JS语法" (即没有TypeScript类)。 How can I define an AngularJS service using a TypeScript class that doesn't pollute the global scope?
但是我们使用TypeScript /的全部意义是/ class语法。 TypeScript是否与任何知道不会走向全球的(明智的)JS程序员发生冲突?
答案 0 :(得分:4)
您可以简单地将课程包装在module
模块本身将是全局的,但如果您不导出该类,则无需担心使用单个模块名称污染全局范围。
所以这个TypeScript:
module MyModule {
class MyClass {
constructor(){}
}
}
将产生以下JS:
var MyModule;
(function (MyModule) {
var MyClass = (function () {
function MyClass() {
}
return MyClass;
})();
})(MyModule || (MyModule = {}));
答案 1 :(得分:2)
在最新版本的TypeScript中,你可以在IIFE中嵌套一个类:
(() => {
class Example {
}
})();
结果输出为:
(function () {
var Example = (function () {
function Example() {
}
return Example;
}());
})();
您可以避免使用AMD或CommonJS等模块模式添加到全局范围。当您使用其中任何一个时,每个TypeScript文件都被视为外部模块,并且不在全局范围内。
此示例出于示例的目的删除了Angular,但是当RequireJS将define
方法添加到global时,您的代码都没有放在此范围内。
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实现它 - 您仍然可以获得自动完成和类型检查,这些都是主要的好处,即使你没有上课。
答案 2 :(得分:1)
Josh是对的。使用模块。同样,使用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语法。
答案 3 :(得分:0)
就像在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模式来避免命名空间污染。这是我的建议。