我试图在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行为?
答案 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)
我需要保留“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');
}
}