一直以来,javascript里边的this都是一个很难理解的东西,之前看的最多的就是阮一峰老师关于this的理解:
http://www.ruanyifeng.com/blo...
http://www.ruanyifeng.com/blo...
今天在留言区发现了一国外大神关于this的理解,借助翻译工具读了一下原文,相对来说是最好的关于理解this的文章,就翻译了一下,也算是记录一下。
JavaScript的一个常用特性是“this”关键字,但它也常常是该语言中最令人困惑和误解的特性之一。“this”到底是什么意思?它是如何决定的?
本文试图澄清这种困惑,并以一种清晰的方式解释这个问题的答案。
“this”关键字对于那些用其他语言编程的人来说并不新鲜,而且它通常引用在通过类的构造函数实例化类时创建的新对象。例如,如果我有一个类Boat(),它有一个方法moveBoat(),当在moveBoat()方法中引用“this”时,我们实际上是在访问新创建的Boat()对象。
在JavaScript中,当使用“new”关键字调用函数构造函数时,函数构造函数中也有这个概念,但是它不是惟一的规则,而且“this”常常可以引用来自不同执行上下文的不同对象。如果您不熟悉JavaScript的执行上下文,我建议您阅读我关于这个主题的另一篇文章(本人注:文章找不到了)。谈得够多了,让我们来看一些JavaScript例子:
// 全局作用域
foo = 'abc';
alert(foo); // abc
this.foo = 'def';
alert(foo); // def
无论何时在全局上下文中使用关键字“this”(而不是在函数中),它总是指向全局对象。现在让我们看看函数中“this”的值:
var boat = {
size: 'normal',
boatInfo: function() {
alert(this === boat);
alert(this.size);
}
};
boat.boatInfo(); // true, 'normal'
var bigBoat = {
size: 'big'
};
bigBoat.boatInfo = boat.boatInfo;
bigBoat.boatInfo(); // false, 'big'
那么上面的“this”是如何确定的呢?我们可以看到一个对象boat,它有一个属性size和一个方法boatInfo()。在boatInfo()中,如果该对象的值是实际的boat对象,它将发出警报,并警告该对象的size属性。因此,我们使用boat.boatInfo()调用函数,可以看到这是boat对象,并且boat的size属性是正常的。
然后我们创建另一个对象bigBoat,它的size属性为big。然而,bigBoat对象没有一个boatInfo()方法,因此我们使用bigBoat从boat复制该方法。boatInfo = boat.boatInfo。现在,当我们调用bigBoat.boatInfo()并输入函数时,我们看到它不等于boat, size属性现在是big。为什么会这样?这个值在boatInfo()中是如何变化的?
您必须意识到的第一件事是,任何函数中的这个值都不是静态的,它总是在每次调用函数时确定的,但是在函数实际执行之前,它是代码。函数内部的值实际上是由父作用域提供的,在父作用域中调用函数,更重要的是,函数语法是如何编写的。
每当调用一个函数时,我们必须查看方括号/圆括号“()”的左边。如果在括号的左边我们可以看到一个引用,那么传递给函数调用的“this”的值就是该对象所属的值,否则它就是全局对象。让我们来看一些例子:
function bar() {
alert(this);
}
bar(); // global - 因为方法bar()在调用时属于全局对象
var foo = {
baz: function() {
alert(this);
}
}
foo.baz(); // foo - 因为方法baz()在调用时属于对象foo
如果到目前为止一切都很清楚,那么上面的代码显然是有意义的。通过用两种不同的方式编写call / invoke语法,我们可以在同一个函数中更改“this”的值,从而使事情变得更加复杂:
var foo = {
baz: function() {
alert(this);
}
}
foo.baz(); // foo - 因为baz在调用时属于foo对象
var anotherBaz = foo.baz;
anotherBaz(); // global - 因为方法anotherBaz()在调用时属于全局对象,而不是foo
在这里,我们看到baz()中的“this”值每次都是不同的,因为它的语法调用有两种不同的方式。现在,让我们看看“this”在深度嵌套对象中的值:
var anum = 0;
var foo = {
anum: 10,
baz: {
anum: 20,
bar: function() {
console.log(this.anum);
}
}
}
foo.baz.bar(); // 20 - 因为()的左边是bar,它在调用时属于baz对象
var hello = foo.baz.bar;
hello(); // 0 - 因为()的左边是hello,它在调用时属于全局对象
另一个经常被问到的问题是如何在事件处理程序中确定关键字“this”?答案是,事件处理程序中的“this”总是指向它所触发的元素。我们来看一个例子:
I am an element with id #test
function doAlert() {
alert(this.innerHTML);
}
doAlert(); // undefined
var myElem = document.getElementById('test');
myElem.onclick = doAlert;
alert(myElem.onclick === doAlert); // true
myElem.onclick(); // I am an element
这里我们可以看到,当第一次调用doAlert()时,它会发出未定义的警报,因为doAlert()属于全局对象。然后我们写myElem。onclick = doAlert,它将函数doAlert()复制到myElem的onclick()事件。这基本上意味着无论何时触发onclick(),它都是myElem的一个方法,这意味着“This”的值就是myElem对象。
关于这个主题,我想指出的最后一点是,“this”的值也可以使用call()和apply()手动设置,覆盖了我们今天讨论的内容。同样有趣的是,当在函数构造函数中调用“this”时,“this”引用构造函数中所有实例中新创建的对象。原因是函数构造函数是用“new”关键字调用的,它创建了一个新对象,其中构造函数中的“this”总是引用刚刚创建的新对象。
总结
希望今天的博客文章已经澄清了对“this”这个关键字的任何误解,你可以一直知道“this”的正确值。我们现在知道,“this”的值从来不是静态的,它的值取决于函数是如何调用的。
原文:http://davidshariff.com/blog/...
欢迎关注小程序,感谢您的支持!