class Simulator {
constructor() {
this.gates = Array();
this.gates.push(new AndGate(200, 200));
}
initialize() {
let canvas = document.getElementById('board');
canvas.width = 800;
canvas.height = 500;
canvas.setAttribute("style", "border: 1px solid black");
this.gates.push(new AndGate(100, 100));
}
run() {
setTimeout(this.onLoop, 1000);
}
onLoop() {
for (let gate of this.gates) {
gate.render();
}
}
}
let sim = new Simulator();
sim.initialize();
sim.run();
由于某种原因,我的 TypeScript 类的 JS 转译版本在
onLoop
函数中抛出错误。它报告TypeError: this.gates is undefined
。但是,如果我访问 sim
(模拟器对象)并手动访问它定义的 Gates 属性。我可以从控制台手动运行 onLoop 代码。
当函数通过引用传递时,它们会失去对
this
的引用。致电 setTimeout
时您将丢失此参考信息。
函数有一个
bind()
方法,基本上返回一个新函数,并带有对 this
的正确引用。
这样称呼它:
setTimeout(this.onLoop.bind(this), 1000)
或者,您也可以传递内联箭头函数。箭头函数不会丢失其
this
上下文。
setTimeout(() => this.onLoop(), 1000)
当在
this.onLoop
内部调用setTimeout
时,onLoop
内部的调用上下文是window,因为setTimeout
是window对象的函数。您可以通过使用 calls onLoop
的箭头函数来解决此问题,而不是直接传递 onLoop
:
class Simulator {
constructor() {
this.gates = Array();
//this.gates.push(new AndGate(200, 200));
}
initialize() {
//let canvas = document.getElementById('board');
//canvas.width = 800;
//canvas.height = 500;
//canvas.setAttribute("style", "border: 1px solid black");
// this.gates.push(new AndGate(100, 100));
this.gates.push({ render: () => console.log('rendered') });
}
run() {
setTimeout(() => this.onLoop(), 1000);
}
onLoop() {
for (let gate of this.gates) {
gate.render();
}
}
}
let sim = new Simulator();
sim.initialize();
sim.run();
或者通过将
this
函数的 onLoop
上下文绑定到实例化对象:
class Simulator {
constructor() {
this.gates = Array();
//this.gates.push(new AndGate(200, 200));
}
initialize() {
//let canvas = document.getElementById('board');
//canvas.width = 800;
//canvas.height = 500;
//canvas.setAttribute("style", "border: 1px solid black");
// this.gates.push(new AndGate(100, 100));
this.gates.push({ render: () => console.log('rendered') });
}
run() {
setTimeout(this.onLoop.bind(this), 1000);
}
onLoop() {
for (let gate of this.gates) {
gate.render();
}
}
}
let sim = new Simulator();
sim.initialize();
sim.run();
劫持这个问题(因为它是第一个在搜索“setTimeout this undefined”时弹出的问题)来发布我遇到的类似问题,该问题适合标题,但不适合问题的内容。
我有这个 Foo 类,我需要等待涉及 Foo 成员的条件。
class Foo {
constructor(bar) { this.bar = bar; }
async async_increment_bar_1(new_bar) {
const poll = resolve => {
if (this.bar === new_bar) {
resolve();
} else {
this.bar += 1;
setTimeout(_ => poll(resolve), 1000, new_bar);
}
};
await new Promise(poll);
}
}
第一种方法工作得很好,但是我发现第二种方法更简洁,而且它也不会用
poll()
函数污染命名空间。
class Foo {
constructor(bar) { this.bar = bar; }
async async_increment_bar_2(new_bar) {
await new Promise(function poll(resolve) {
if (this.bar === new_bar) { // <-- this will raise error
resolve();
} else {
this.bar += 1;
setTimeout(_ => poll(resolve), 1000, new_bar);
}
});
}
}
但是,这会引发以下错误:
TypeError: Cannot read properties of undefined (reading 'bar')
我该如何解决这个问题?
根据最上面的答案:
当函数通过引用传递时,它们会失去对 this 的引用。调用 setTimeout 时您将丢失此引用。
函数有一个 bind() 方法,它基本上返回一个新函数,并带有对此的正确引用。
要修复第二个代码片段,需要在通过引用传递
this
函数的每个实例中将 poll()
引用绑定到 poll()
函数;无论是在定义时还是在 setTimeout
lambda 内部再次调用时。
class Foo {
constructor(bar) { this.bar = bar; }
async async_increment_bar_2(new_bar) {
await new Promise(function poll(resolve) {
if (this.bar == new_bar) {
resolve();
} else {
this.bar += 1;
setTimeout(_ => poll.bind(this)(resolve), 1000, new_bar);
}
}.bind(this));
}
}
完整代码:
class Foo {
constructor(bar) { this.bar = bar; }
async async_increment_bar_1(new_bar) {
const poll = resolve => {
if (this.bar === new_bar) {
resolve();
} else {
this.bar += 1;
setTimeout(_ => poll(resolve), 1000, new_bar);
}
};
await new Promise(poll);
}
async async_increment_bar_2(new_bar) {
await new Promise(function poll(resolve) {
if (this.bar == new_bar) {
resolve();
} else {
this.bar += 1;
setTimeout(_ => poll.bind(this)(resolve), 1000, new_bar);
}
}.bind(this));
}
}
main = async () => {
let foo = new Foo(0);
console.log(`Initially foo.bar is ${foo.bar}`);
await foo.async_increment_bar_2(3);
console.log(`Finally foo.bar is ${foo.bar}`);
}
main();