在TypeScript中是否有“this”的别名?
我试图在TypeScript中编写一个类,该类定义了一个方法,作为jQuery事件的事件处理函数callback。
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创build一个隐藏_this别名的闭包types。
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的行为?
所以如上所述,没有TypeScript机制来确保方法总是绑定到this
指针(这不仅仅是一个jQuery问题)。这并不意味着没有合理的解决这个问题的简单方法。 你需要的是为你的方法生成一个代理,在调用你的callback之前恢复this
指针。 然后,您需要将该callback与该代理包装在一起,然后再将其传递到事件中。 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. } }
这是一个合理的解决scheme,但我个人发现,开发人员经常忘记包含代理服务器电话,所以我想出了一个替代的基于TypeScript的解决scheme来解决这个问题。 使用, HasCallbacks
类下面所有你需要做的是从HasCallbacks
派生你的类,然后任何以'cb_'
为前缀的方法将永久绑定this
指针。 你根本不能用不同的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({});
当使用箭头函数syntax () => { ... }
时, 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
因为箭头函数语法保留了“词法作用域”。
正如其他一些答案所涵盖的,使用箭头语法定义一个函数会引起this
引用,以便始终引用封闭的类。
所以要回答你的问题,这里有两个简单的解决方法。
使用箭头语法引用该方法
constructor(public id: string) { this.textarea = $(id); this.textarea.focusin(e => this.onFocusIn(e)); }
使用箭头语法定义方法
onFocusIn = (e: JQueryEventObject) => { var height = this.textarea.css('height'); }
您可以在构造函数中将成员函数绑定到它的实例。
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));
尝试这个
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 } }
史蒂文·伊克曼的解决scheme是方便的,但不完整。 丹尼·贝克特(Danny Becket)和山姆(Sam)的答案更短,更手动,并且在同一时间需要dynamic和词汇范围的“this”的callback。 跳到我的代码,如果我的解释是TL; DR …
我需要保留“this”以便与库callback一起使用的dynamic范围, 并且我需要对类实例进行词法范围的“this”。 我认为将实例传递给callback生成器是最优雅的,它有效地让参数在类实例上closures。 编译器告诉你,如果你错过了这样做。 我使用一个调用词法作用域参数“outerThis”的约定,但“自己”或其他名称可能会更好。
使用“this”这个关键词是从OO世界中被窃取的,当TypeScript采用它时(我从ECMAScript 6规范中推测出来),他们把词汇范围概念和dynamic范围概念混为一谈,只要一个方法被不同的实体。 我对此感到有些不解。 我更喜欢TypeScript中的“self”关键字,这样我就可以将词法范围的对象实例closures。 或者,JS可以被重新定义为在需要的时候需要明确的第一位“呼叫者”参数(从而一举破除所有的网页)。
这是我的解决scheme(从大class上删除)。 特别是在方法被称为的方式,特别是“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; dx += d3.event.dx; dy += d3.event.dy; // Referring to "this" in dynamic scoping context d3.select(this).attr("transform", function(d) { return "translate(" + dx + "," + dy + ")"; }); 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; } } }
除了在fat arrow lambda语法中提供的重新映射方便之外,TypeScript没有提供任何额外的方式(除了常规的JavaScript方法)以回到“真实的” this
引用之外(这是从back-compat的angular度允许的,因为不存在JS代码可以使用a =>
expression式)。
您可以向CodePlex网站发布build议,但是从语言devise的angular度来看,这里可能没有太多可能发生的事情,因为编译器可以提供的任何理智的关键字可能已经被现有的JavaScript代码使用。
你可以使用js eval函数: var realThis = eval('this');
我遇到过类似的问题。 我认为你可以在很多情况下使用.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'); }); });
我希望这可以帮助别人。
你可以将你的引用存储在另外一个variables中,也许是self
,然后访问这个引用。 我不使用打字稿,但是这是一个方法,在过去,香草javascript已经成功了。
看看这篇博文http://lumpofcode.blogspot.com/2012/10/typescript-dart-google-web-toolkit-and.html ,它详细讨论了在TypeScript类内部和之间组织调用的技术。
比上面所有的答案都要简单得多。 基本上我们通过使用关键字函数而不是使用'=>'结构来将JavaScript'翻译成类'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'); } }