【问题标题】:this inside prototype function equal to window instead of object instancethis 内部原型函数等于窗口而不是对象实例
【发布时间】:2014-04-14 22:31:31
【问题描述】:

HeadDirective.prototype.link 中的以下代码中,this 等于全局window 对象而不是HeadDirective 实例。我的理解是原型函数中this的值就是包含对象本身。

var HeadDirective = (function () {
    function HeadDirective($rootScope, $compile) {
        this.$rootScope = $rootScope;
        this.$compile = $compile;
        this.restrict = 'E';
    }
    HeadDirective.prototype.link = function (scope, elem) {
        var html = '<link rel="stylesheet" ng-repeat="cssUrl in routeStyles" ng-href="{{cssUrl}}" />';
        elem.append(this.$compile(html)(scope));
        scope.routeStyles = [];
        this.$rootScope.$on('$routeChangeStart', function (e, next, current) {
            if (next && next.$$route && next.$$route.css) {
                if (!Array.isArray(next.$$route.css)) {
                    next.$$route.css = [next.$$route.css];
                }
                angular.forEach(next.$$route.css, function (sheet) {
                    scope.routeStyles.push(sheet);
                });
            }
        });
        this.$rootScope.$on('$routeChangeSuccess', function (e, next, current) {
            if (current && current.$$route && current.$$route.css) {
                if (!Array.isArray(current.$$route.css)) {
                    current.$$route.css = [current.$$route.css];
                }
                angular.forEach(current.$$route.css, function (sheet) {
                    scope.routeStyles.splice(scope.routeStyles.indexOf(sheet), 1);
                });
            }
        });
    };
    return HeadDirective;
})();

directives.directive('head', [
    '$rootScope', '$compile', function ($rootScope, $compile) {
        return new HeadDirective($rootScope, $compile);
    }]);

以上代码由以下 TypeScript 生成:

class HeadDirective implements ng.IDirective {

    constructor(private $rootScope: ng.IRootScopeService, private $compile: ng.ICompileService) {}

    link(scope: IScope, elem: JQuery): void {
        var html = '<link rel="stylesheet" ng-repeat="cssUrl in routeStyles" ng-href="{{cssUrl}}" />';
        elem.append(this.$compile(html)(scope));
        scope.routeStyles = [];
        this.$rootScope.$on('$routeChangeStart', (e: ng.IAngularEvent, next?: IRoute, current?: IRoute): any => {
            if(next && next.$$route && next.$$route.css){
                if(!Array.isArray(next.$$route.css)){
                    next.$$route.css = [next.$$route.css];
                }
                angular.forEach(next.$$route.css, (sheet: string) => {
                    scope.routeStyles.push(sheet);
                });
            }
        });
        this.$rootScope.$on('$routeChangeSuccess', (e: ng.IAngularEvent, next?: IRoute, current?: IRoute): any => {
            if(current && current.$$route && current.$$route.css){
                if(!Array.isArray(current.$$route.css)){
                    current.$$route.css = [current.$$route.css];
                }
                angular.forEach(current.$$route.css, (sheet) => {
                    scope.routeStyles.splice(scope.routeStyles.indexOf(sheet), 1);
                });
            }
        });
    }

    restrict = 'E';
}

directives.directive('head', ['$rootScope','$compile', ($rootScope: ng.IRootScopeService, $compile: ng.ICompileService): ng.IDirective =>{
    return new HeadDirective($rootScope, $compile);
}]);

根据最新的 TypeScript 语言规范:

表达式中this 的类型取决于引用发生的位置:

  • 在构造函数、实例成员函数、实例成员访问器或实例成员变量初始化器中,this 属于包含类的类实例类型。
  • 在静态成员函数或静态成员访问器中,this 的类型是包含类的构造函数类型。
  • 在函数声明或标准函数表达式中,this 是 Any 类型。
  • 在全局模块中,this 是 Any 类型。

在所有其他上下文中,引用 this 是编译时错误。

TypeScript 语言规范非常明确。在成员函数(编译成原型函数)内部,this 指的是类实例。这显然不是我所看到的。

有什么想法吗? Browserify 会不会干扰this

【问题讨论】:

标签: javascript this typescript browserify


【解决方案1】:

this 关键字与上下文高度相关。例如,如果方法被事件调用,this 将是作为事件目标的对象。

您可以通过将this 填充到变量中,或使用JavaScript call(或apply)方法绑定this 的范围来解决这个问题。

简短的例子......这是前提:

class MyClass {
    constructor(private myProp: string) {

    }

    myMethod() {
        alert(this.myProp);
    }
}

var myClass = new MyClass('Test');

// 'Test'
myClass.myMethod();

// undefined
window.setTimeout(myClass.myMethod, 1000);

解决方案一 - 箭头语法

在 TypeScript 中,箭头语法将自动为您将 this 转换为名为 _this 的变量,并替换箭头函数内的用法...因此这将解决上面的 undefined 问题,而不是警告 Test

class MyClass {
    constructor(private myProp: string) {

    }

    public myMethod = () => {
        alert(this.myProp);
    }
}

解决方案二 - 调用方法

您可以使用call 方法将上下文this 替换为您喜欢的任何对象,在下面的示例中,我们将其重置为myClass 实例。

无论您是编写 TypeScript 还是纯 JavaScript,这都适用……而第一个解决方案实际上是 TypeScript 解决方案。

window.setTimeout(function() { myClass.myMethod.call(myClass) }, 1000);

或者更短(要清楚,这里使用箭头函数与范围无关 - 它只是一个更短的语法 箭头函数只有在你有 this 时才会影响范围):

window.setTimeout(() => myClass.myMethod.call(myClass), 1000);

【讨论】:

  • 那么,您是否会说 TypeScript 语言规范做出了它无法兑现的承诺?
  • 规范描述的是您引用的部分中this 关键字的编译时类型,而不是值。因为 TypeScript 在运行时与 JavaScript 具有相同的 this 语义,所以总是有可能得到“错误”的值。
  • 好的。如果对象的构造函数、方法、访问器和修改器中的this 并不总是等于类实例本身,那么拥有类就没有意义了。如果我想要 JavaScript 语义,我会使用 JavaScript。
  • @SteveTaylor 我理解你的沮丧 - 但实际上你会想要选择何时使用 this 作为实例和 this 作为上下文。例如,如果您可以在上下文中使用 this,则事件处理效果会更好。
  • 是的,我希望至少能够 选择 没有丑陋的黑客(例如,将所有东西都推入构造函数,从而无法实现接口并通常使 @987654345 @ 和构造函数关键字不比 cmets 好)。如果选择失败,我更喜欢所有其他带类的语言所具有的,这是一种内置的、有保证的方法,可以从其自己的 instance 方法访问实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-21
  • 1970-01-01
  • 1970-01-01
  • 2012-09-11
  • 1970-01-01
相关资源
最近更新 更多