【问题标题】:Typescript "this" inside a class method类方法中的打字稿“this”
【发布时间】:2013-04-15 23:43:07
【问题描述】:

我知道这可能是非常基本的,但我很难理解它。

class Main
{
     constructor()
     {
         requestAnimationFrame(this.update);  //fine    
     }

     update(): void
     {
         requestAnimationFrame(this.update);  //error, because this is window
     }

}

看来我需要一个代理,所以让我们说使用 Jquery

class Main
{
     constructor()
     {
         this.updateProxy = $.proxy(this.update, this);
         requestAnimationFrame(this.updateProxy);  //fine    
     }

     updateProxy: () => void
     update(): void
     {
         requestAnimationFrame(this.updateProxy);  //fine
     }

}

但是来自 Actionscript 3 背景,我不确定这里发生了什么。抱歉,我不确定 Javascript 从哪里开始,TypeScript 从哪里结束。

updateProxy: () => void

而且,我不相信我这样做是正确的。我想要的最后一件事是我的大部分班级都有一个需要使用aProxy() 访问的 a() 函数,因为我觉得我正在写两次相同的东西?正常吗?

【问题讨论】:

标签: jquery this typescript proxy-classes


【解决方案1】:

简而言之,this 关键字始终具有对调用该函数的对象的引用。

在 Javascript 中,由于函数只是变量,因此您可以传递它们。

例子:

var x = {
   localvar: 5, 
   test: function(){
      alert(this.localvar);
   }
};

x.test() // outputs 5

var y;
y.somemethod = x.test; // assign the function test from x to the 'property' somemethod on y
y.test();              // outputs undefined, this now points to y and y has no localvar

y.localvar = "super dooper string";
y.test();              // outputs super dooper string

当您使用 jQuery 执行以下操作时:

$.proxy(this.update, this);

您正在做的是覆盖该上下文。 jQuery 会在幕后告诉你:

$.proxy = function(fnc, scope){
  return function(){
     return fnc.apply(scope);  // apply is a method on a function that calls that function with a given this value
  }
};

【讨论】:

    【解决方案2】:

    如果你想this 捕获 TypeScript 的方法是通过箭头函数。引用安德斯的话:

    箭头函数中的this 是词法范围的

    这是我喜欢使用它的方式:

    class test{
        // Use arrow functions
        func1=(arg:string)=>{
                return arg+" yeah" + this.prop;
        }
        func2=(arg:number)=>{
                return arg+10 + this.prop;
        }       
    
        // some property on this
        prop = 10;      
    }
    

    View this in the TypeScript Playground

    可以看到,在生成的 JavaScript this 被捕获函数调用之外:

    var _this = this;
    this.prop = 10;
    this.func1 = function (arg) {
        return arg + " yeah" + _this.prop;
    };
    

    因此不会使用函数调用中的this 值(可能是window)。

    了解更多:“Understanding this in TypeScript” (4:05) – YouTube

    【讨论】:

    • 这不是必需的。您的建议是惯用的 JavaScript,但 TypeScript 使这变得不必要。
    • @TatianaRacheva 在 TS 0.9.1 之前不允许在类成员的上下文中使用箭头函数(这个答案是在那之前的)。更新了新语法的答案:)
    • 您必须观看视频 - 非常有用。只需 5 分钟
    • 谢谢你,@basarat。当我看到你在视频中途使用箭头功能时,灯泡就继续为 this 寻​​找上下文。我很感激你。
    • @AaronLS 在 TypeScript 操场上生成 var _this = this; 你必须选择 ES5;由于 ES2015 - 内部函数 - 使用 this 代替 _this
    【解决方案3】:

    如果你这样写你的方法,'this' 将按照你期望的方式处理。

    class Main
    {
        constructor()
        {
            requestAnimationFrame(() => this.update());
        }
    
        update(): void
        {
            requestAnimationFrame(() => this.update());
        }
    }
    

    另一种选择是将“this”绑定到函数调用:

    class Main
    {
        constructor()
        {
            requestAnimationFrame(this.update.bind(this));
        }
    
        update(): void
        {
            requestAnimationFrame(this.update.bind(this));
        }
    }
    

    【讨论】:

    • 根据我的经验,更新函数最好这样定义:update = () => { ... }
    • 我一直在使用 typescript 并且来回更改了很多次。目前,如果它不仅仅是一个简单的方法调用,我只包括花括号。同样在使用 typescript + linq (这很不错)时,格式更好。示例:Enumerable.From(arr).Where(o => o.id == 123);
    • 我想如果你看一下生成的javascript,你会发现有很大的不同。这不是品味问题。 update = () => {} 将通过“var _this = this”编译创建词法范围,您的语法不会。
    • 您可能需要升级您的 TypeScript 库,因为它绝对包含“_this”上下文。使用 "() => code()" 或 () => { return code(); }" 将输出 100% 相同的 javascript 代码。输出如下:i.imgur.com/I5J12GE.png。您也可以将代码粘贴到 typescriptlang.org/Playground 中自己查看
    • 显然 bind(this) 可能很糟糕,因为它失去了原始函数 args 的类型安全
    【解决方案4】:

    参见打字稿语言规范的第 72 页 https://github.com/Microsoft/TypeScript/blob/master/doc/TypeScript%20Language%20Specification.pdf?raw=true

    箭头函数表达式

    在示例中

    class Messenger {
     message = "Hello World";
     start() {
     setTimeout(() => alert(this.message), 3000);
     }
    };
    var messenger = new Messenger();
    messenger.start();
    

    使用箭头函数表达式会导致回调具有 与周围的“开始”方法相同。编写回调 作为标准函数表达式,必须手动 安排对周围环境的访问,例如将其复制到 一个局部变量:

    这是实际生成的 Javascript:

    class Messenger {
     message = "Hello World";
     start() {
     var _this = this;
     setTimeout(function() { alert(_this.message); }, 3000);
     }
    };
    

    【讨论】:

    • 链接已损坏
    • @JimmyKane 我更新了链接。奇怪的是,这个文档已有一年多的历史,仍然被他们的主页引用,但我在答案中包含了重要内容。
    【解决方案5】:

    当您将函数作为回调传递时,就会出现问题。到回调执行时,“this”的值可能已更改为 Window、调用回调的控件或其他内容。

    确保在将引用传递给要回调的函数时始终使用 lambda 表达式。例如

    public addFile(file) {
      this.files.push(file);
    }
    //Not like this
    someObject.doSomething(addFile);
    //but instead, like this
    someObject.doSomething( (file) => addFile(file) );
    

    这编译成类似的东西

    this.addFile(file) {
      this.files.push(file);
    }
    var _this = this;
    someObject.doSomething(_this.addFile);
    

    因为 addFile 函数是在特定对象引用 (_this) 上调用的,所以它不使用调用者的“this”,而是使用 _this 的值。

    【讨论】:

    • 当你说它编译成什么时,你显示的是哪一个? (箭头实例还是只传递方法对象的那个?)
    • lambda。只需使用该代码创建一个 TS 并查看它编译的内容。
    【解决方案6】:

    聚会很晚,但我认为对于这个问题的未来访客来说,考虑以下几点非常重要:

    其他答案,包括接受的答案,错过了一个关键点:

    myFunction() { ... }
    

    myFunction = () => { ... }
    

    相同的东西“除了后者捕获this”。

    第一种语法在原型上创建一个方法,而第二种语法在其值为函数的对象上创建一个属性(这也恰好捕获了this)。您可以在转译的 JavaScript 中清楚地看到这一点。

    要完整:

    myFunction = function() { ... }
    

    与第二种语法相同,但没有捕获。

    所以,在大多数情况下使用箭头语法可以解决您绑定到对象的问题,但它并不相同,在很多情况下您确实希望在原型而不是属性。

    在这些情况下,使用代理或.bind() 实际上正确的解决方案。 (虽然可读性不好。)

    在此处阅读更多内容(主要不是关于 TypeScript,而是原则立场):

    https://medium.com/@charpeni/arrow-functions-in-class-properties-might-not-be-as-great-as-we-think-3b3551c440b1

    https://ponyfoo.com/articles/binding-methods-to-class-instance-objects

    【讨论】:

      【解决方案7】:

      这样做怎么样? 声明一个“myClass”类型的全局变量,并在类的构造函数中对其进行初始化:

      var _self: myClass;
      
      class myClass {
          classScopeVar: string = "hello";
      
          constructor() {
              _self = this;
          }
      
          alerter() {
              setTimeout(function () {
                  alert(_self.classScopeVar)
              }, 500);
          }
      }
      
      var classInstance = new myClass();
      classInstance.alerter();
      

      注意:重要的是不要使用“自我”,因为它已经具有特殊含义。

      【讨论】:

      • 大问题:类的所有实例都有相同的_self,所以它不起作用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多