TypeScript中是否存在“this”的别名?

时间:2012-10-06 03:26:15

标签: jquery typescript

我试图在TypeScript中编写一个类,该类具有一个定义的方法,该方法充当jQuery事件的事件处理程序回调。

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin(onFocusIn);
    }

    onFocusIn(e: JQueryEventObject) {
        var height = this.textarea.css('height'); // <-- This is not good.
    }
}

在onFocusIn事件处理程序中,TypeScript将“this”视为该类的“this”。但是,jQuery会覆盖此引用并将其设置为与事件关联的DOM对象。

另一种方法是在构造函数中将lambda定义为事件处理程序,在这种情况下,TypeScript会创建一种带有隐藏_this别名的闭包。

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin((e) => {
            var height = this.textarea.css('height'); // <-- This is good.
        });
    }
}

我的问题是,是否有其他方法可以使用TypeScript在基于方法的事件处理程序中访问此引用,以克服此jQuery行为?

12 个答案:

答案 0 :(得分:97)

使用箭头函数语法this时保留() => { ... }的范围 - 以下是TypeScript For JavaScript Programmers的示例。

var ScopeExample = { 
  text: "Text from outer function", 
  run: function() { 
    setTimeout( () => { 
      alert(this.text); 
    }, 1000); 
  } 
};

请注意,this.text会为您提供Text from outer function,因为箭头函数语法会保留&#34;词法范围&#34;。

答案 1 :(得分:21)

如上所述,没有TypeScript机制来确保方法总是绑定到它的this指针(这不仅仅是一个jQuery问题。)这不是意味着没有相当直接的方法来解决这个问题。你需要的是为你的方法生成一个代理,在调用你的回调之前恢复this指针。然后,您需要使用该代理包装回调,然后再将其传递给事件。 jQuery有一个内置的机制,称为jQuery.proxy()。以上是使用该方法的上述代码的示例(请注意添加的$.proxy()调用。)

class Editor { 
    textarea: JQuery; 

    constructor(public id: string) { 
        this.textarea = $(id); 
        this.textarea.focusin($.proxy(onFocusIn, this)); 
    } 

    onFocusIn(e: JQueryEventObject) { 
        var height = this.textarea.css('height'); // <-- This is not good. 
    } 
} 

这是一个合理的解决方案,但我个人发现开发人员经常忘记包含代理呼叫,因此我已经提出了另一种基于TypeScript的解决方案来解决这个问题。使用下面的HasCallbacks课程,您需要做的就是从HasCallbacks派生您的课程,然后任何前缀为'cb_'的方法都会将this指针永久绑定。您根本无法使用不同的this指针来调用该方法,在大多数情况下这种指针更为可取。这两种机制都可以正常使用,无论您发现哪种更容易使用。

class HasCallbacks {
    constructor() {
        var _this = this, _constructor = (<any>this).constructor;
        if (!_constructor.__cb__) {
            _constructor.__cb__ = {};
            for (var m in this) {
                var fn = this[m];
                if (typeof fn === 'function' && m.indexOf('cb_') == 0) {
                    _constructor.__cb__[m] = fn;                    
                }
            }
        }
        for (var m in _constructor.__cb__) {
            (function (m, fn) {
                _this[m] = function () {
                    return fn.apply(_this, Array.prototype.slice.call(arguments));                      
                };
            })(m, _constructor.__cb__[m]);
        }
    }
}

class Foo extends HasCallbacks  {
    private label = 'test';

    constructor() {
        super();

    }

    public cb_Bar() {
        alert(this.label);
    }
}

var x = new Foo();
x.cb_Bar.call({});

答案 2 :(得分:20)

正如其他一些答案所述,使用箭头语法定义函数会导致对this的引用始终引用封闭类。

所以回答你的问题,这里有两个简单的解决方法。

使用箭头语法

引用该方法
constructor(public id: string) {
    this.textarea = $(id);
    this.textarea.focusin(e => this.onFocusIn(e));
}

使用箭头语法

定义方法
onFocusIn = (e: JQueryEventObject) => {
    var height = this.textarea.css('height');
}

答案 3 :(得分:6)

您可以在构造函数中将成员函数绑定到其实例。

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin(onFocusIn);
        this.onFocusIn = this.onFocusIn.bind(this); // <-- Bind to 'this' forever
    }

    onFocusIn(e: JQueryEventObject) {
        var height = this.textarea.css('height');   // <-- This is now fine
    }
}

或者,只需在添加处理程序时绑定它。

        this.textarea.focusin(onFocusIn.bind(this));

答案 4 :(得分:3)

试试这个

class Editor 
{

    textarea: JQuery;
    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin((e)=> { this.onFocusIn(e); });
    }

    onFocusIn(e: JQueryEventObject) {
        var height = this.textarea.css('height'); // <-- This will work
    }

}

答案 5 :(得分:3)

史蒂文·艾克曼的解决方案很方便,但不完整。 Danny Becket和Sam的答案更短,更具手动性,并且在同样的一般情况下失败,即同时需要动态和词汇范围“this”的回调。如果下面的解释是TL,请跳到我的代码; DR ...

我需要保留“this”以用于动态范围,以便与库回调一起使用,我需要为类实例提供一个带有词法作用域的“this”。我认为将实例传递给回调生成器是最优雅的,有效地让参数闭包在类实例上。编译器会告诉您是否错过了这样做。我使用调用词法范围参数“outerThis”的约定,但“self”或其他名称可能更好。

使用“this”关键字是从OO世界中窃取的,当TypeScript采用它时(根据我推测的ECMAScript 6规范),无论何时调用方法,它们都会混淆一个词法范围的概念和一个动态范围的概念。一个不同的实体。我对此有点恼火;我更喜欢TypeScript中的“self”关键字,这样我就可以将词法范围内的对象实例移出它。或者,JS可以被重新定义为在需要时需要一个明确的第一位置“调用者”参数(从而一举打破所有网页)。

这是我的解决方案(从大班中删除)。特别是在调用方法的方式上,特别是“dragmoveLambda”的主体:

export class OntologyMappingOverview {

initGraph(){
...
// Using D3, have to provide a container of mouse-drag behavior functions
// to a force layout graph
this.nodeDragBehavior = d3.behavior.drag()
        .on("dragstart", this.dragstartLambda(this))
        .on("drag", this.dragmoveLambda(this))
        .on("dragend", this.dragendLambda(this));

...
}

dragmoveLambda(outerThis: OntologyMappingOverview): {(d: any, i: number): void} {
    console.log("redefine this for dragmove");

    return function(d, i){
        console.log("dragmove");
        d.px += d3.event.dx;
        d.py += d3.event.dy;
        d.x += d3.event.dx;
        d.y += d3.event.dy; 

        // Referring to "this" in dynamic scoping context
        d3.select(this).attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

        outerThis.vis.selectAll("line")
            .filter(function(e, i){ return e.source == d || e.target == d; })
            .attr("x1", function(e) { return e.source.x; })
            .attr("y1", function(e) { return e.source.y; })
            .attr("x2", function(e) { return e.target.x; })
            .attr("y2", function(e) { return e.target.y; });

    }
}

dragging: boolean  =false;
// *Call* these callback Lambda methods rather than passing directly to the callback caller.
 dragstartLambda(outerThis: OntologyMappingOverview): {(d: any, i: number): void} {
        console.log("redefine this for dragstart");

        return function(d, i) {
            console.log("dragstart");
            outerThis.dragging = true;

            outerThis.forceLayout.stop();
        }
    }

dragendLambda(outerThis: OntologyMappingOverview): {(d: any, i: number): void}  {
        console.log("redefine this for dragend");

        return function(d, i) {
            console.log("dragend");
            outerThis.dragging = false;
            d.fixed = true;
        }
    }

}

答案 6 :(得分:2)

除了胖箭头lambda语法中提供的this重新映射方便之外,TypeScript没有提供任何额外的方法(超出常规JavaScript的方法)回到“真正的”this引用。从back-compat的角度来看是允许的,因为没有现有的JS代码可以使用=>表达式。)

您可以向CodePlex网站发布建议,但从语言设计的角度来看,可以在这里发生的可能并不多,因为编译器可能提供的任何理智的关键字可能已经被现存使用JavaScript代码。

答案 7 :(得分:2)

您可以使用js eval函数:var realThis = eval('this');

答案 8 :(得分:1)

我遇到过类似的问题。我认为您可以在许多情况下使用.each()this作为后续事件的不同值。

JavaScript方式:

$(':input').on('focus', function() {
  $(this).css('background-color', 'green');
}).on('blur', function() {
  $(this).css('background-color', 'transparent');
});

TypeScript方式:

$(':input').each((i, input) => {
  var $input = $(input);
  $input.on('focus', () => {
    $input.css('background-color', 'green');
  }).on('blur', () => {
    $input.css('background-color', 'transparent');
  });
});

我希望这有助于某人。

答案 9 :(得分:0)

您可以将对this的引用存储在另一个变量.. self中,并以这种方式访问​​引用。我不使用打字稿,但这是一种过去使用vanilla javascript成功的方法。

答案 10 :(得分:0)

查看此博客文章http://lumpofcode.blogspot.com/2012/10/typescript-dart-google-web-toolkit-and.html,它详细讨论了在TypeScript类内和跨TypeScript类组织调用的技术。

答案 11 :(得分:0)

与上述所有答案相比,有更简单的解决方案。基本上我们通过使用关键字函数而不是使用'=&gt;'来回退到JavaScript将'this'转换为'this'类

的构造
class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        var self = this;                      // <-- This is save the reference
        this.textarea = $(id);
        this.textarea.focusin(function() {   // <-- using  javascript function semantics here
             self.onFocusIn(this);          //  <-- 'this' is as same as in javascript
        });
    }

    onFocusIn(jqueryObject : JQuery) {
        var height = jqueryObject.css('height'); 
    }
}