为什么 'this' 在此 TypeScript 片段中引用 'window'?

Why is 'this' refering to 'window' in this TypeScript snippet?

给定这段代码:

module movieApp {
    export interface IHomeControllerScope extends ng.IScope {
        moviesToDownload: string[];
        active: string;

        deleteMovieFromDownloadList(movie: any);

        markMovieAsDownloaded(movie: any);
    }

    export class HomeController {
        public static $inject = [
            '$scope',
            '$location',
            'MovieService'
        ];

        constructor(private $scope: IHomeControllerScope, private $location: ng.ILocationService, private MovieService) {
            this.$scope.$on('$locationChangeSuccess', (event) => {
                this.setActiveUrlPart();
            });

            MovieService.getMoviesToDownload().then(response => {
                this.$scope.moviesToDownload = response;
            });
        }

        private setActiveUrlPart() {
            var parts = this.$location.path().split('/');
            this.$scope.active = parts[1];
        }

        public get moviesToDownload() {
            return this.$scope.moviesToDownload;
        }

        public markMovieAsDownloaded(movie: any) {
            movie.Downloaded = true;
        }

        public deleteMovieFromDownloadList(movie: any) {
            this.MovieService.deleteMovieFromDownloadList(movie).then(() => {
                debugger;
                this.$scope.moviesToDownload = _.without(this.$scope.moviesToDownload, movie);
            });
        }
    }
}

app.controller("HomeController", movieApp.HomeController);

一切正常,但在 deleteMovieFromDownloadList 方法中 this.$scope.moviesToDownload = _.without(this.$scope.moviesToDownload, movie); 行中,this 指的是 window 对象,而不是我期望的实际对象.

生成的 JavaScript 如下所示:

var movieApp;
(function (movieApp) {
    var HomeController = (function () {
        function HomeController($scope, $location, MovieService) {
            var _this = this;
            this.$scope = $scope;
            this.$location = $location;
            this.MovieService = MovieService;
            this.$scope.$on('$locationChangeSuccess', function (event) {
                _this.setActiveUrlPart();
            });

            MovieService.getMoviesToDownload().then(function (response) {
                _this.$scope.moviesToDownload = response;
            });
        }
        HomeController.prototype.setActiveUrlPart = function () {
            var parts = this.$location.path().split('/');
            this.$scope.active = parts[1];
        };

        Object.defineProperty(HomeController.prototype, "moviesToDownload", {
            get: function () {
                return this.$scope.moviesToDownload;
            },
            enumerable: true,
            configurable: true
        });

        HomeController.prototype.markMovieAsDownloaded = function (movie) {
            movie.Downloaded = true;
        };

        HomeController.prototype.deleteMovieFromDownloadList = function (movie) {
            var _this = this;
            this.MovieService.deleteMovieFromDownloadList(movie).then(function () {
                debugger;
                _this.$scope.moviesToDownload = _.without(_this.$scope.moviesToDownload, movie);
            });
        };
        HomeController.$inject = [
            '$scope',
            '$location',
            'MovieService'
        ];
        return HomeController;
    })();
    movieApp.HomeController = HomeController;
})(movieApp || (movieApp = {}));

app.controller("HomeController", movieApp.HomeController);
//# sourceMappingURL=HomeController.js.map

可以看到,在生成的JS中,具体的方法使用了_this。这看起来不错,对吧?

有人可以向我解释发生了什么以及如何解决这个问题吗?

编辑:

我将它与 Angular 结合使用:

<body data-ng-app="movieApp" data-ng-controller="HomeController as homeCtrl">
  <div class="col-sm-1">
    <i class="fa fa-trash-o" data-ng-click="homeCtrl.deleteMovieFromDownloadList(m)" title="Verwijder uit lijst"></i>
  </div>
</body>

编辑二: 在尝试了所有建议然后设置回我在此处发布的原始代码后,一切正常!我不知道怎么回事,但我想这与 Chrome / VS 2013 有关。无论如何,感谢那些试图帮助我的人。

我猜问题出在如何调用 deleteMovieFromDownloadList 上。如果你这样做:

var myController = new HomeController();
someFramework.doSomethingWithCallback(myController.deleteMovieFromDownloadList);

...someFramework 会在没有绑定到预期上下文的情况下调用回调。如果是这种情况,您可以通过以下方式修复它:

var boundCallback = myController.deleteMovieFromDownloadList.bind(myController);
someFramework.doSomethingWithCallback(boundCallback);

更新:

在通读 Angular 文档时,您放入点击处理程序的内容似乎并不是真正的 JavaScript,而是由 Angular 解释的。所以也许 homeCtrl.deleteMovieFromDownloadList(m) 本质上是在做这样的事情:

var fn = homeCtrl.deleteMovieFromDownloadList;
fn(m):

...这当然不会正确设置 this

设置好控制器后,你不能这样做吗?

data-ng-click="deleteMovieFromDownloadList(m)"

可能 "deleteMovie..." 函数绑定到按钮或其他 UI 元素。在这种情况下,此函数在 window 上下文中执行。要解决此问题,您应该在控制器的构造函数中定义函数体:

constructor(private $scope: IHomeControllerScope, private $location: ng.ILocationService, private MovieService) {
// other initialization code...

this.deleteMovieFromDownloadList = (movie: any) => {
    this.MovieService.deleteMovieFromDownloadList(movie).then(() => {
        debugger;
        this.$scope.moviesToDownload = _.without(this.$scope.moviesToDownload, movie);
    });
  }
}

并在您的控制器中声明适当的函数 class:

deleteMovieFromDownloadList: (movie: any) => void;

Javascript 在构造函数方面有一些奇怪的行为。我做了一个例子来向你展示它是如何工作的:

function myClass() {
    var me = this;
    this.property = 'test';
    this.windowObj = function() {
        windowObjTest();
    }

    var windowObjTest = function() {
        console.log(this); // this = window object
    }

    this.myself = function() {
        myselfTest();
    }

    var myselfTest = function() {
        console.log(me); // me = this object
    }
}

var myobj = new myClass();
myobj.windowObj();
myobj.myself();

this.windowObj();是调用私有函数windowObjTest();的public函数。在一个私有函数中,this 是对 window 对象的引用,而不是这个实例(别问我,dev 很高什么的……)。要将当前对象的引用获取到私有函数中,您必须将当前对象的引用设置到构造函数中:var me = this;.

尝试示例并查看控制台。应该出现两个条目:

- Window test.php //console.log(this);
- myClass { property="test", windowObj=function(), myself=function()} //console.log(me);