尽管看上去this引用了它在代码中所存在于的对向,事实上在方法被调用之前它并没有被赋值,而赋给它的值又严格地依赖于实际调用“this方法”的对象。this通常会被赋予调用对象的值,下面有一些特殊情况。
全局范围里的this
在全局域中,代码在浏览器里执行,所有变量和方法都属于window对象。因而当我们在全局域中使用this关键字的时候,它会被指向(并拥有)全局变量window对象。如前所述,严格模式除外。window对象是JS一个程序或一张网页的主容器。
因而:
var firstName = "Peter", lastName = "Ally"; function showFullName() { //在这个方法中,this将指向window对象。因为showFullName()出现在全局域里。 console.log(this.firstName + " " + this.lastName); } var person = { firstName: "Penelope", lastName: "Barrymore", showFullName: function() { //下面这行代码,this指向person对象,因为showFullName方法会被person对象调用。 console.log(this.firstName + " " + this.lastName); } } showFullName(); // Peter Ally //window对象包含了所有的全局变量和方法,因而会有以下输出 window.showFullName(); // Peter Ally //使用了this关键字的showFullName方法定义在person对象里,this关键字指向person对象,因以会有以下输出 person.showFullName(); // Penelope Barrymore
对付this有绝招
当方法内使用了this关键字时,这几种情况最容易引起误解:方法被借用;把方法赋值给某个变量;方法被用作回调函数(callback),被作为参数传递;this所在的方法是个闭包(该方法是一个内部方法)。针对这几种情况,我们将逐一攻破。在此之前,我们先简单介绍一下“上下文”(context)。
JS当中的上下文跟这句话中的主语(subject)类似:“John是赢家,他还了钱”。这句话的主语是John。我们也可以说这句话的上下文是John,因为我们在这句话中关注的是John,即使这里有一个“他”字来代指John这个先行词。正如我们可以使用分号来切换句子的主语一样,通过使用不同的对象来对方法进行调用,当前的上下文对象同样可以被切换。
类似地,以下JS代码:
var person = { firstName: "Penelope", lastName: "Barrymore", showFullName: function() { // 上下文 console.log(this.firstName + " " + this.lastName); } } //使用person对象调用showFullName的时候,上下文是person对象 //showFullName内部的this指向person对象 person.showFullName(); // Penelope Barrymore //如果我们用不同的对象来调用showFullName var anotherPerson = { firstName: "Rohit", lastName: "Khan" }; //我们可以使用apply方法来显式设置this的值—迟些我们会讲到apply方法 //this会指向任何一个调用了this方法的对象,因此会有以下输出结果 person.showFullName.apply(anotherPerson); // Rohit Khan //所以现在的上下文是anotherPerson,因为anotherPerson通过借助使用apply方法间接调用了person的showFullName方法
现在我们开始正式讨论应付this关键字的绝招,例子里包含了this所引发的错误以及解决方案。
1.当this被用作回调函数传入其它方法
当我们把一个使用了this关键字的方法当成参数作为回函数的时候,麻烦就来了。例如:
//以下是一个简单的对象,我们定义了一个clickHandler方法。我们想让这个方法在页面上某个button被单击时执行。 var user = { data: [{ name: "T. Woods", age: 37 }, { name: "P. Mickelson", age: 43 }], clickHandler: function(event) { var randomNum = ((Math.random() * 2 | 0) + 1) - 1; // 随机返回0或1 //下面这行代码会从数组data里随机打印姓名和年龄 console.log(this.data[randomNum].name + " " + this.data[randomNum].age); } } //button对象被jQuery的$方法包装,现在变成一个jQuery对象 //所以输出结果是undefined,因为button对象没有data这个属性 $("button").click(user.clickHandler); // 无法读取未定义的属性
上面的代码中,我们把user.clickHandler当成回调函数传入$(“button”)对象的click事件,user.clickHandler中的this将不再指向user对象转。谁调用了这个包含this的方法this就会指向谁。真正调用user.clickHandler的对象是button对象—user.clickHandler会在button对象的单击方法里执行。
注意,尽管我们使用user.clickHandler来调用clickHander方法(我们也只能这么做,因为clickHandler是定义在user对象上的),clickHandler方法本身会被现在被this所指向的上下文对象所调用。所以this现在指向的是$(“button”)对象。
当上下文改变时—当我们在其它对象而非原对象上执行某个方法的时候,显然this关键字不再指向定义了this关键字的原对象。
解决方案: