类方法中的打字稿“ this”


81

我知道这可能是非常痛苦的基本工作,但是我很难缠着它。

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

而且,我不确信自己在做这件事。我想要的最后一件事是我的班级中大多数都具有aa()函数,aProxy()因为我感觉自己在写两次相同的东西,所以需要使用该函数进行访问?正常吗


Answers:


117

如果要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;      
}

在TypeScript游乐场中查看

您可以看到在生成的JavaScriptthis中捕获了函数调用之外的内容:

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

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

要了解更多信息,请访问:thisTypeScript理解”(4:05)– YouTube


2
这不是必需的。您所建议的是惯用的JavaScript,但是TypeScript使得这没有必要。
Tatiana Racheva 2013年

1
在TS 0.9.1之前,不允许@TatianaRacheva在类成员的上下文中使用箭头函数(并且此答案早于此)。更新了对新语法的答案:)
basarat

18
您必须观看视频-非常有用。仅5分钟
Simon_Weaver 2014年

1
谢谢@basarat。当我看到您在视频中途使用箭头功能时,灯泡就开始对此进行介绍。我感激你。
西蒙(Simon)

1
要在TypeScript操场上生成@AaronLS,var _this = this;必须选择ES5;因为ES2015-内部函数-this代替了_this
Stefano Spinucci

18

如果您这样编写方法,则将按您期望的方式对待“ 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));
    }
}

2
以我的经验,可以更好地定义更新函数,如下所示:update =()=> {...}
Shaun Rowan 2014年

我经常使用打字稿,并且来回更改了很多次。此刻,我只包括花括号,如果它不仅仅是一个简单的方法调用。同样在使用打字稿+ linq(这是敬虔的)时,格式更好。例如:Enumerable.From(arr).Where(o => o.id == 123);
joelnet 2014年

我认为,如果您查看生成的JavaScript,将会看到很大的不同。这不是品味的问题。update =()=> {}将通过“ var _this = this”编译创建词法作用域,而您的语法不会。
肖恩·罗恩

1
您可能需要升级TypeScript库,因为它绝对包含“ _this”上下文。使用“()=> code()”或()=> {return code(); }”将输出100%相同的javascript代码,以下是输出:i.imgur.com/I5J12GE.png您还可以看到自己通过将代码粘贴到。typescriptlang.org/Playground
joelnet

显然bind(this)可能很糟糕,因为它会使原始函数args失去类型安全性
罗伯特·金(

6

参见打字稿语言规范的第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);
 }
};

不幸的是,链接断开了
吉米·凯恩

1
@JimmyKane我更新了链接。奇怪的是,这个文档已有一年多的历史了,仍然被他们的主页引用,但是我在答案中包含了重要内容。
Simon_Weaver

4

当您将函数作为回调传递时,就会出现问题。到回调执行时,“ 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的值。


当您说它编译成什么时,您正在显示哪个?(箭头实例还是仅通过方法对象的那个?)
Vaccano

Lambda。只需使用该代码创建一个TS,然后查看其编译内容。
彼得·莫里斯

2

简而言之,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

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

其他答案(包括已接受的答案)缺少关键点:

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


0

这样做怎么样?声明类型为“ 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”作为特殊含义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.