[译文]深入理解JavaScript的this关键字(二)
来源:互联网 发布:淘宝好评返现处罚条例 编辑:程序博客网 时间:2024/06/06 08:44
原文:Understand JavaScript’s “this” With Clarity, and Master It
上一篇文章:[译文]深入理解JavaScript的this关键字(一)
this关键词最令人误解的场景和相关技巧
this关键字令人误解的场景包括:在borrow method中使用this;将一个使用this的方法赋给变量;在回调函数中使用this;在闭包中使用this。我们会依次查看这些场景,然后为了维护合适的this值提出相应的解决方案。
0 一个重要的笔记
在我们开始正式学习前关于Context(上下文)的一点小知识
JS中的context类似于英语中一个句子的主语:
John is the winner who returned the money.
John是句子中的主语;我们可以说句子的context是John,因为句子的焦点是在他上身。即使who这个代词也是指John,也就是,先行物。正如我们使用分号以切换句子的主语,我们可以有一个对象是当前的context,然后通过另一个对象来调用函数,将上下文切换成另一个对象。
在JS代码中是这样的:
var person = { firstName: 'hhhha', lastName: 'wooow', showFullName: function() { console.log(this.firstName + ' ' + this.lastName); }}person.showFullName(); // hhhha wooowvar anotherPerson = { firstName: 'zyyyy', lastName: 'hhhhhha'}// change the context person.showFullName.apply(anotherPerson); // zyyyy hhhhhha
1 Fix this when used in a method passed as a callback
当然我们将一个方法(方法内使用this)将作为一个回调函数,事情就变量有些麻烦。比如下面这个例子:
// We have a simple object with a clickHandler method that we want to use when a button on the page is clicked var user = { data:[ {name:"T. Woods", age:37}, {name:"P. Mickelson", age:43} ], clickHandler:function (event) { var randomNum = ((Math.random () * 2 | 0) + 1) - 1; // random number between 0 and 1 // This line is printing a random person's name and age from the data array console.log (this.data[randomNum].name + " " + this.data[randomNum].age); } } // The button is wrapped inside a jQuery $ wrapper, so it is now a jQuery object // And the output will be undefined because there is no data property on the button object $ ("button").click (user.clickHandler); // Cannot read property '0' of undefined
在上述代码,由于button( $ (“button”))是一个对象,然后我们将user.clickHandler方法传递给它的click方法用作回调函数。我们知道在这个回调函数中,this不会再指向user对象。this现在指向另一个对象(user.clickHandler在这个对象中执行),因为this在user.clickHandler方法中被调用。调用user.clickHandler的对象的是button: user.clickHandler会在button的click方法中被调用。
即使我们现在通过user.clickHandler调用clickHandler()(我们不得不这么做,因为clickHandler这个方法定义在user上面),现在clickHandler这个方法在button上下文(context)这中被定义,这个context是this指向的对象。所以this现在指向$(“button”)对象。
在这个代码中,很明显,context改变了——当我们在其他对象上而不是原来对象上执行一个方法时,this关键字就不再指向原来的对象了,原来的对象是指this被定义的对象,它反而指向调用方法的对象了。
修复“方法被传递作为回调函数“引发的this问题的解决方案是:因为我们想让this.data指向user对象上的data属性,我们可以使用Bind().Apply(),Call()方法专门指定this的值。
我已经写了篇详细的文章: JavaScript’s Apply, Call, and Bind Methods are Essential for JavaScript Professionals 关于这些方法,包括如何在这些令人误解的场景使用它们设置this的值。我不会再这里赘述这篇文章的内容,我建议你阅读整篇文章,我认为这篇文章是一个JS开发者必读的。
为了修复之前例子中的这个问题,我们可以这么使用bind方法:
$("button").click (user.clickHandler.bind (user)); // P. Mickelson 43
我们已经将user.clickHandler方法和user对象绑定。
2 Fix this inside closure
另一个this令人误解的例子是当我们使用一个闭包函数时。我们必须记住:闭包不能通过this关键词访问挖不不函数的this变量因为this变量只能通过函数自身访问,不能被内部函数访问。举个例子:
var user = { tournament:"The Masters", data :[ {name:"T. Woods", age:37}, {name:"P. Mickelson", age:43} ], clickHandler:function () { // the use of this.data here is fine, because "this" refers to the user object, and data is a property on the user object. this.data.forEach (function (person) { // But here inside the anonymous function (that we pass to the forEach method), "this" no longer refers to the user object. // This inner function cannot access the outer function's "this" console.log ("What is This referring to? " + this); //[object Window] console.log (person.name + " is playing at " + this.tournament); // T. Woods is playing at undefined // P. Mickelson is playing at undefined }) } } user.clickHandler(); // What is "this" referring to? [object Window]
在匿名函数中的this变量不能访问外部函数的的this,所以this是绑定全局window对象的,前提是严格模式不被使用。
在匿名函数中维护this值的解决方案:
为了解决上述问题,我们使用一种在JS的普遍实践,然后在进入forEach之前,将this值赋给另一个变量。
var user = { tournament:"The Masters", data :[ {name:"T. Woods", age:37}, {name:"P. Mickelson", age:43} ], clickHandler:function (event) { // To capture the value of "this" when it refers to the user object, we have to set it to another variable here: // We set the value of "this" to theUserObj variable, so we can use it later var theUserObj = this; this.data.forEach (function (person) { // Instead of using this.tournament, we now use theUserObj.tournament console.log (person.name + " is playing at " + theUserObj.tournament); }) } } user.clickHandler(); // T. Woods is playing at The Masters // P. Mickelson is playing at The Masters
很多JS开发者喜欢使用一个变量that去设置this的值,像这样:var that = this
;用什么变量名指代并不重要。但是,that变量名的使用令我感到尴尬,我尽量用一个名词去指代this指代的对象,所以我在前面的代码中这么使用:var theUserObj = this
3 Fix this when method is assigned to a variable
如果我们将一个使用this的方法赋给一个对象,this的值会超出我们想象:它会绑定另一个对象。让我们看下面的代码:
// This data variable is a global variable var data = [ {name:"Samantha", age:12}, {name:"Alexis", age:14} ]; var user = { // this data variable is a property on the user object data :[ {name:"T. Woods", age:37}, {name:"P. Mickelson", age:43} ], showData:function (event) { var randomNum = ((Math.random () * 2 | 0) + 1) - 1; // random number between 0 and 1 // This line is adding a random person from the data array to the text field console.log (this.data[randomNum].name + " " + this.data[randomNum].age); } } // Assign the user.showData to a variable var showUserData = user.showData; // When we execute the showUserData function, the values printed to the console are from the global data array, not from the data array in the user object // showUserData (); // Samantha 12 (from the global data array)
针对上述问题的解决方法是这样的:我们可以在bind方法中专门设定this的值以修复这个问题。
// Bind the showData method to the user object var showUserData = user.showData.bind (user); // Now we get the value from the user object, because the <em>this</em> keyword is bound to the user object showUserData (); // P. Mickelson 43
4 Fix this when borrowing methods
borrowing methods在JS开发者是一个普遍的实践。作为JS开发者,我们常常会碰到这个实践。偶尔,我们会使用这个节省时间的时间。如果像了解更多关于borrowing methods的知识,可以阅读我的深度文章: JavaScript’s Apply, Call, and Bind Methods are Essential for JavaScript Professionals
让我们this在borrow methods的context的关联性:
// We have two objects. One of them has a method called avg () that the other doesn't have // So we will borrow the (avg()) method var gameController = { scores :[20, 34, 55, 46, 77], avgScore:null, players :[ {name:"Tommy", playerID:987, age:23}, {name:"Pau", playerID:87, age:33} ] } var appController = { scores :[900, 845, 809, 950], avgScore:null, avg :function () { var sumOfScores = this.scores.reduce (function (prev, cur, index, array) { return prev + cur; }); this.avgScore = sumOfScores / this.scores.length; } } //If we run the code below, // the gameController.avgScore property will be set to the average score from the appController object "scores" array // Don't run this code, for it is just for illustration; we want the appController.avgScore to remain null gameController.avgScore = appController.avg();
avg方法的this关键字不会指向gameController对象,她会指向appController对象,因为它在appController对象中被调用。
解决方案:
为了解决这个问题,确保appController.avg()方法中的this指向gameController,我们能够使用apply()方法,像下面这样:
// Note that we are using the apply () method, so the 2nd argument has to be an array—the arguments to pass to the appController.avg () method. appController.avg.apply (gameController); // The avgScore property was successfully set on the gameController object, even though we borrowed the avg () method from the appController object console.log (gameController.avgScore); // 46.4 // appController.avgScore is still null; it was not updated, only gameController.avgScore was updated console.log (appController.avgScore); // null
gameController对象借用appController的avg方法。appController的avg方法的this值会被设为gameController对象,因为我们会传递gameController对象作为第一个参数给apply方法,apply方法的第一个参数总是会显示地设置this的值。
5 最后想说的话
现在你经有工具(bind,apply,call和保存this的值给一个变量)去任何一个场景中去征服JS的this。
当context切换的时候,this这个关键词变得有些麻烦。你需要铭记于心的是:this总是指向调用this函数的对象。
好好学习,天天coding。
- [译文]深入理解JavaScript的this关键字(二)
- [译文]深入理解JavaScript的this关键字(一)
- Javascript之this关键字的深入理解
- 深入理解javascript关键字this
- 深入理解JavaScript中关键字this的使用
- 深入理解JavaScript中的this关键字
- 深入理解Javascript之this关键字
- 深入理解Javascript之this关键字
- 深入理解JavaScript中的this关键字
- 深入理解Javascript之this关键字
- 深入理解Javascript之this关键字
- 深入理解Javascript之this关键字
- 深入理解JavaScript中的this关键字
- 深入理解Javascript之this关键字
- 深入理解JavaScript中的this关键字
- 深入理解Javascript之this关键字
- 深入理解JavaScript中的this关键字
- 深入理解JavaScript中的this关键字
- Mybatis中#与$的区别
- SPringMVC-拦截器
- maven配置ssh环境
- spring 整合hibernate整合方式
- 384. Shuffle an Array
- [译文]深入理解JavaScript的this关键字(二)
- 莫比乌斯与积性函数
- 多目标优化
- MapReduce&Yarn 运行原理
- python平台使用mongodb存储大文件
- 学习java8的lambda表达式
- 刘咚咚19910512
- 心得
- 在fullpage.js中 transform的的用法,用于过度