我不确定在TypeScript中处理“this”范围的最佳方法。
以下是我转换为TypeScript的代码中常见模式的示例:
class DemonstrateScopingProblems {
private status = "blah";
public run() {
alert(this.status);
}
}
var thisTest = new DemonstrateScopingProblems();
// works as expected, displays "blah":
thisTest.run();
// doesn't work; this is scoped to be the document so this.status is undefined:
$(document).ready(thisTest.run);
现在,我可以将呼叫更改为...
$(document).ready(thisTest.run.bind(thisTest));
......确实有效。但它有点可怕。这意味着在某些情况下代码可以全部编译并正常工作,但如果我们忘记绑定范围,它就会破坏。
我想在课堂上这样做,所以在使用课程时我们不需要担心“这个”的范围是什么。
有什么建议吗?
另一种有效的方法是使用胖箭头:
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
alert(this.status);
}
}
这是一种有效的方法吗?
答案 0 :(得分:156)
这里有几个选项,每个选项都有自己的权衡。不幸的是,没有明显的最佳解决方案,它将真正取决于应用程序。
自动类绑定
如你的问题所示:
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
alert(this.status);
}
}
this
上下文而不是每个调用站点在调用时创建新闭包的效率更高。this
上下文super.
<强> Function.bind
如图所示:
$(document).ready(thisTest.run.bind(thisTest));
胖箭
在TypeScript中(出于解释原因,此处显示了一些虚拟参数):
$(document).ready((n, m) => thisTest.run(n, m));
答案 1 :(得分:14)
另一种解决方案需要一些初始设置,但是凭借其无形的轻巧,字面上的单字语法,通过getter使用Method Decorators来JIT绑定方法。
我已经创建了一个repo on GitHub来展示这个想法的实现(它有点冗长以适应其40行代码的答案,包括评论),你将简单地用作:
class DemonstrateScopingProblems {
private status = "blah";
@bound public run() {
alert(this.status);
}
}
我还没有在任何地方看过这个,但它完美无瑕。此外,这种方法没有明显的缺点:这个装饰器的实现 - 包括对运行时类型安全的一些类型检查 - 是微不足道的,并且在以后基本上没有开销。初始方法调用。
关键部分是在类原型上定义以下getter,它在第一次调用之前执行:
get: function () {
// Create bound override on object instance. This will hide the original method on the prototype, and instead yield a bound version from the
// instance itself. The original method will no longer be accessible. Inside a getter, 'this' will refer to the instance.
var instance = this;
Object.defineProperty(instance, propKey.toString(), {
value: function () {
// This is effectively a lightweight bind() that skips many (here unnecessary) checks found in native implementations.
return originalMethod.apply(instance, arguments);
}
});
// The first invocation (per instance) will return the bound method from here. Subsequent calls will never reach this point, due to the way
// JavaScript runtimes look up properties on objects; the bound method, defined on the instance, will effectively hide it.
return instance[propKey];
}
这个想法也可以更进一步,通过在类装饰器中执行此操作,迭代方法并在一次传递中为每个方法定义上述属性描述符。
答案 2 :(得分:14)
Necromancing。
有一个明显的简单解决方案,不需要箭头功能(箭头功能慢30%),或JIT方法通过吸气剂。
该解决方案是在构造函数中绑定this-context。
class DemonstrateScopingProblems
{
constructor()
{
this.run = this.run.bind(this);
}
private status = "blah";
public run() {
alert(this.status);
}
}
您可以使用此方法自动绑定构造函数中类中的所有函数:
class DemonstrateScopingProblems
{
constructor()
{
this.autoBind(this);
}
[...]
}
export function autoBind(self: any)
{
for (const key of Object.getOwnPropertyNames(self.constructor.prototype))
{
const val = self[key];
if (key !== 'constructor' && typeof val === 'function')
{
// console.log(key);
self[key] = val.bind(self);
} // End if (key !== 'constructor' && typeof val === 'function')
} // Next key
return self;
} // End Function autoBind
答案 3 :(得分:2)
在您的代码中,您是否尝试过更改最后一行,如下所示?
$(document).ready(() => thisTest.run());