Javascript 为什么编译器不';“翻译”;这";链接到上下文无关变量?
假设我有一个类(非常简单的场景) 它由TypeScript编译器编译为:Javascript 为什么编译器不';“翻译”;这";链接到上下文无关变量?,javascript,typescript,Javascript,Typescript,假设我有一个类(非常简单的场景) 它由TypeScript编译器编译为: var Student = (function () { function Student() { this.name = "John"; } Student.prototype.sayHello = function () { console.log("Hi, I'm " + this.name); //here is the problem. Accessing
var Student = (function () {
function Student() {
this.name = "John";
}
Student.prototype.sayHello = function () {
console.log("Hi, I'm " + this.name); //here is the problem. Accessing name via this
};
return Student;
})();
现在,如果我创建一个对象并调用一个方法,一切都会正常工作
var student = new Student();
student.sayHello(); //prints Hi, I'm John
但是,如果我从回调调用该方法,它将中断(此
按预期引用了一个窗口)
我知道JavaScript中的这个与C#或Java之间的区别。我也知道,TypeScript试图解决这个差异。例如,此代码:
class Student
{
name = "John";
sayHelloTo(other)
{
other(() => this.name);
}
}
会被编译成
var Student = (function () {
function Student() {
this.name = "John";
}
Student.prototype.sayHelloTo = function (other) {
//note, the compiler solves the problem by capturing this into local variable
var _this = this;
other(function () {
return _this.name;
});
};
return Student;
})();
为什么编译器不在第一个场景中为类成员创建类似于的变量呢?
我希望在下一个代码中看到一些东西(不是真正的输出,此代码也不正确,只是为了表明我的意图)
我使用了类型脚本v0.9.7编译器编译器唯一能做的就是确保每个构造的对象都有一个原型函数的绑定副本。这将涉及一个非常重要的语义变化,所以它不能真正做到这一点
翻译后的代码返回一个可以访问闭包的函数,这是真的。但是,在您建议的备选方案中,只有一个\u this
将由构造函数创建的所有实例共享。闭包位于被调用以创建“学生”构造函数的函数中;当构造构造函数时,该函数只运行一次,然后再也不会运行。因此,每次调用newstudent()
都会更新该共享变量\u this
。(在本例中,导致问题的方式是在学生实例上更改“name”属性。如果它们都命名为“John”,则无所谓:)
最基本的问题是,在JavaScript中,函数和任何对象之间都没有内在的关系。当你打电话的时候
setTimeout(student.sayHello, 100);
第一个参数表达式的计算结果是对“sayHello”函数的普通引用。引用来自对象的事实已丢失。我想Typescript的另一种替代方法是捕获这些类型的表达式,并在该点上创建绑定函数。也就是说,类代码本身将保持不变,但是setTimeout()
调用将被转换为
setTimeout(student.sayHello.bind(student), 100);
这会对我不能说的一切产生什么样的影响。我也不知道编译器知道它应该进行转换有多难;有时它可能没有意义。您可能需要像下面这样更改sayHello函数,使其生成所需的代码。注意sayHello=()=>{}
这将仍然适用于多个学生,而您的示例并非如此
class Student
{
name = "John";
sayHello = () =>
{
console.log("Hi, I'm " + this.name);
}
}
它将生成如下代码:
function Student() {
var _this = this;
this.name = "John";
this.sayHello = function () {
console.log("Hi, I'm " + _this.name);
};
}
另一种可能是将调用更改为setTimeout,如下所示
setTimeout(() => { student.sayHello() });
我不确定javascript
标记在这里是否可用。它不会有任何好处。\u此
的值将设置为什么?@Pointy以更正此
的值。我已经执行了这个示例,效果很好。我可能在JavaScript部分遗漏了一些全局性的东西,但是这个特定的类示例对于student.sayHello()
和setTimeout(student.sayHello)
在创建两个学生之后,在将其中一个学生的“name”属性设置为“Peter”之后,可以尝试使用它。我明白你的意思。我完全同意,我的上一个代码是不正确的,我只是指出了方向,我看到了问题所在。我不能提出这个问题的解决方案,谢谢你的回复。您能告诉我这是如何在所有实例中共享的吗?我只是不知道这怎么可能,如果我有一个var\u this代码>在自调用函数中。我会非常感谢你的邀请example@IlyaIvanov好的,我将扩展答案。现在我明白你关于单的观点了。谢谢我会尽力理解整个情况,然后接受答案。如果你不介意的话,迈克,你能不能再详细解释一下另一个答案?不是一个sayHello=()=>console.log(“嗨,我是”+this.name)
语法是这个问题的解决方案,因为它在Student
构造函数中创建了一个\u this
变量?我对JS没有什么经验,因此我很容易错过该语言的一些关键案例。这种方法基本上与使用.bind()
-它正在创建另一个函数,新函数显式地维护学生实例和函数之间的关系。(基本上,sayHello=()=>console.log(“嗨,我是”+this.name)是一个非常好的解决方案)代码>(注意,您可能会错过{}
,就像在C#lambda表达式中一样)对-这就像使用.bind()
。
class Student
{
name = "John";
sayHello = () =>
{
console.log("Hi, I'm " + this.name);
}
}
function Student() {
var _this = this;
this.name = "John";
this.sayHello = function () {
console.log("Hi, I'm " + _this.name);
};
}
setTimeout(() => { student.sayHello() });