jQuery TypeScript 中是否有“this”的别名?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12756423/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there an alias for 'this' in TypeScript?
提问by Todd
I've attempted to write a class in TypeScript that has a method defined which acts as an event handler callback to a jQuery event.
我试图在 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.
}
}
Within the onFocusIn event handler, TypeScript sees 'this' as being the 'this' of the class. However, jQuery overrides the this reference and sets it to the DOM object associated with the event.
在 onFocusIn 事件处理程序中,TypeScript 将“this”视为类的“this”。但是,jQuery 会覆盖 this 引用并将其设置为与事件关联的 DOM 对象。
One alternative is to define a lambda within the constructor as the event handler, in which case TypeScript creates a sort of closure with a hidden _this alias.
一种替代方法是在构造函数中定义一个 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.
});
}
}
My question is, is there another way to access the this reference within the method-based event handler using TypeScript, to overcome this jQuery behavior?
我的问题是,是否有另一种方法可以使用 TypeScript 在基于方法的事件处理程序中访问 this 引用,以克服这种 jQuery 行为?
采纳答案by Steven Ickman
So as stated there is no TypeScript mechanism for ensuring a method is always bound to it's this
pointer (and this isn't just a jQuery issue.) That doesn't mean there isn't a reasonably straightforward way to address this issue. What you need is to generate a proxy for your method that restores the this
pointer before calling your callback. You then need to wrap your callback with that proxy before passing it into the event. jQuery has a built in mechanism for this called jQuery.proxy()
. Here's an example of your above code using that method (notice the added $.proxy()
call.)
因此,如上所述,没有 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.
}
}
That's a reasonable solution but I've personally found that developers often forget to include the proxy call so I've come up with an alternate TypeScript based solution to this problem. Using, the HasCallbacks
class below all you need do is derive your class from HasCallbacks
and then any methods prefixed with 'cb_'
will have their this
pointer permanently bound. You simply can't call that method with a different this
pointer which in most cases is preferable. Either mechanism works so its just whichever you find easier to use.
这是一个合理的解决方案,但我个人发现开发人员经常忘记包含代理调用,所以我想出了一个基于 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({});
回答by Fenton
The scope of this
is preserved when using the arrow function syntax () => { ... }
- here is an example taken from TypeScript For JavaScript Programmers.
this
使用箭头函数语法时会保留范围() => { ... }
- 这里是从TypeScript For JavaScript Programmers 中获取的示例。
var ScopeExample = {
text: "Text from outer function",
run: function() {
setTimeout( () => {
alert(this.text);
}, 1000);
}
};
Note that this.text
gives you Text from outer function
because the arrow function syntax preserves the "lexical scope".
请注意,this.text
给你Text from outer function
,因为箭头函数语法保留了“词法范围”。
回答by Sam
As covered by some of the other answers, using the arrow syntax to define a function causes references to this
to always refer to the enclosing class.
正如其他一些答案所涵盖的那样,使用箭头语法定义函数会导致引用this
始终引用封闭类。
So to answer your question, here are two simple workarounds.
因此,要回答您的问题,这里有两个简单的解决方法。
Reference the method using the arrow syntax
使用箭头语法引用方法
constructor(public id: string) {
this.textarea = $(id);
this.textarea.focusin(e => this.onFocusIn(e));
}
Define the method using the arrow syntax
使用箭头语法定义方法
onFocusIn = (e: JQueryEventObject) => {
var height = this.textarea.css('height');
}
回答by Drew Noakes
You can bind a member function to its instance in the constructor.
您可以在构造函数中将成员函数绑定到其实例。
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
}
}
Alternatively, just bind it when you add the handler.
或者,只需在添加处理程序时绑定它。
this.textarea.focusin(onFocusIn.bind(this));
回答by Eric
Steven Ickman's solution is handy, but incomplete. Danny Becket and Sam's answers are shorter and more manual, and fail in the same general case of having a callback that needs both dynamic and lexically scoped "this" at the same time. Skip to my code if my explanation below is TL;DR...
Steven Ickman 的解决方案很方便,但不完整。Danny Becket 和 Sam 的答案更短且更手动,并且在具有同时需要动态和词法范围“this”的回调的相同一般情况下失败。如果我下面的解释是 TL;DR...
I need to preserve "this" for dynamic scoping for use with library callbacks, andI need to have a "this" with lexical scoping to the class instance. I argue that it is most elegant to pass the instance into a callback generator, effectively letting the parameter closure over the class instance. The compiler tells you if you missed doing so. I use a convention of calling the lexically scoped parameter "outerThis", but "self" or another name might be better.
我需要为动态范围保留“this”以与库回调一起使用,并且我需要有一个“this”和类实例的词法范围。我认为将实例传递给回调生成器是最优雅的,有效地让参数闭包覆盖类实例。编译器会告诉您是否错过了这样做。我使用调用词法范围参数“outerThis”的约定,但“self”或其他名称可能更好。
The use of the "this" keyword is stolen from the OO world, and when TypeScript adopted it (from ECMAScript 6 specs I presume), they conflated a lexically scoped concept and a dynamically scoped concept, whenever a method is called by a different entity. I'm a little miffed at this; I would prefer a "self" keyword in TypeScript so that I can hand the lexically scoped object instance off of it. Alternately, JS could be redefined to require an explicit first-position "caller" parameter when it is needed (and thus break all web pages in one fell swoop).
“this”关键字的使用是从 OO 世界中窃取的,当 TypeScript 采用它时(我认为来自 ECMAScript 6 规范),每当一个方法被不同的实体调用时,它们就会将词法范围概念和动态范围概念混为一谈. 我对此有点恼火;我更喜欢 TypeScript 中的“self”关键字,以便我可以将词法范围的对象实例从它传递出去。或者,可以重新定义 JS 以在需要时要求明确的第一个“调用者”参数(从而一举破坏所有网页)。
Here's my solution (excised from a large class). Take a gander in particular at the way the methods are called, and the body of "dragmoveLambda" in particular:
这是我的解决方案(从大班中删除)。特别注意方法的调用方式,尤其是“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;
}
}
}
回答by Gabriel C.
Try 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
}
}
回答by Ryan Cavanaugh
TypeScript doesn't provide any extra way (beyond the regular JavaScript means) to get back to the 'real' this
reference other than this
remapping convenience provided in the fat arrow lambda syntax (which is allowable from a back-compat perspective since no existing JS code could be using a =>
expression).
this
除了this
粗箭头 lambda 语法中提供的重新映射便利之外,TypeScript 没有提供任何额外的方式(超出常规 JavaScript 手段)来返回“真实”引用(从向后兼容的角度来看这是允许的,因为没有现有的 JS 代码)可能使用=>
表达式)。
You could post a suggestion to the CodePlex site, but from a language design perspective there's probably not much that canhappen here, since any sane keyword the compiler could provide might already be in use by extant JavaScript code.
你可以发表建议,CodePlex上的网站,但是从语言设计的角度来看有可能不会太多,可以在这里发生的,因为任何理智的关键字,编译器可以提供可能已在由现存的JavaScript代码使用。
回答by reptile
You can use js eval function: var realThis = eval('this');
您可以使用 js eval 函数: var realThis = eval('this');
回答by obrys
I have faced a similar problem. I think you can use .each()
in many cases to keep this
as a different value for later events.
我也遇到过类似的问题。我认为您可以.each()
在许多情况下使用this
为以后的事件保留不同的值。
The JavaScript way:
JavaScript 方式:
$(':input').on('focus', function() {
$(this).css('background-color', 'green');
}).on('blur', function() {
$(this).css('background-color', 'transparent');
});
The TypeScript way:
打字稿方式:
$(':input').each((i, input) => {
var $input = $(input);
$input.on('focus', () => {
$input.css('background-color', 'green');
}).on('blur', () => {
$input.css('background-color', 'transparent');
});
});
I hope this helps someone.
我希望这可以帮助别人。
回答by Ezward
Check out this blog post http://lumpofcode.blogspot.com/2012/10/typescript-dart-google-web-toolkit-and.html, it has a detailed discussion of techniques for organizing calls within and across TypeScript classes.
查看这篇博客文章http://lumpofcode.blogspot.com/2012/10/typescript-dart-google-web-toolkit-and.html,它详细讨论了在 TypeScript 类内和跨 TypeScript 类组织调用的技术。