• 前端分享学习博客,探究前端相关技术,推动天朝前端发展,有任何问题都可以留言一起探究
  • 由于站内自链接问题,部分pre中的代码首字母使用大写以过滤筛选
  • 欢迎友链互换,还有,如果有大神请不要黑我的站点(o´・ェ・`o)
  • 如果你觉得博客还不错,请Ctrl+D收藏( *︾▽︾)

JavaScript笔记之this

不久前一跟一位前辈聊天,说要把基础打牢,然后需思考/理解现行前端框架的设计思想,以及他们之间的差异,并且问了我一些问题,我以为我都知道,但大部分是了解,没有到理解透彻的地步,惭愧,所以开了这个JavaScript笔记的系列坑,温习巩固一下,也希望能给各位看官老爷一点提高/启发;

如标题,本篇开坑JavaScript中的keyword:'this'的坑;JavaScript中的this,可以理解为一个代词,用来替代JavaScript前文环境出现过的对象,使得代码显得平顺,优美;比如有个人说:“'薛陈磊'很帅,'他'有很多书”,这里的‘他’,就代指‘薛陈磊’,这是个普通的表达句,约定俗成,不显得突兀,当然描述为“薛陈磊很帅,薛陈磊有很多书”也是可以的,但是第一个表达方式才是大众化的惯用句,所以,JavaScript也是一样,使用this作为关键词(keyword)来代指;看一个简单的示例:

var person = {
    firstName: "Penelope",
    lastName: "Barrymore",
    fullName: function () {
    // Notice we use "this" just as we used "he" in the example sentence earlier:
        console.log(this.firstName + " " + this.lastName);
    // We could have also written this:
        console.log(person.firstName + " " + person.lastName);
    }
}

在上一个例子中,如果我们使用person.firstName 和 person.lastName,代码可能就会变得模糊/歧义;设想如果有另一个全局变量‘person’(大项目代码量大我们可能不知道其他地方有),这个时候的person.firstName 和 person.lastName可能会通过原型(property)访问到公共变量'person'的firstName和lastName,this关键字不仅引用前对象,而且还包含前对象的值;所以如例所示最好是使用this.xxx来返回当前对象的属性方法;

JavaScript’s this Keyword 基本定义

JavaScript中所有的方法函数function和对象objects都有属性(properties),当一个function运行时,function中的this就获取了当前方法中对象的值;this始终指的是一个对象,并在function内使用;注意在严格模式下,this在全局方法和匿名方法中的值为undefined,并没有绑定对象;

this在一个function里被调用的时候,this包含调用这个函数的对象的值,就算不知道外面对象的名字,也可以通过this在function里访问外面对象的属性和方法,而且有时候这个‘外部对象’可能就没名字,这是一个很便利的用法:

var person = {
    firstName:"Penelope",
    lastName:"Barrymore",
    //this在showFullName中调用的时候,this就代表对象person,拥有person的值;
    showFullName:function () {
    console.log (this.firstName + " " + this.lastName);
    }

}

person.showFullName(); // Penelope Barrymore

jQuery示例:

$("button").click(function (event) {
// $(this)代表对象$("button"),你不知道哪一按钮会被点击,所以这里的$("button")也不知道是哪个,使用$(this)一了百了,点击哪个代表哪个
    console.log($(this).prop("name"));
});

所以我们可以理解为this使用的原理是,这个关键词不会被分配任何值,直到有对象调用了function,并且在functiion中使用了this;

全局环境中的this

在全局环境中,脚本在浏览器中编译运行,所有的变量和方法都会定义在window对象上,所以如果我们在全局环境中使用this,实际上指向的是全局window(非严格模式下),会检索全部的脚本方法和web page:

var firstName = "Peter",
	lastName = "Ally";
function showFullName(){
	// "this" inside this function will have the value of the window object
	// because the showFullName() function is defined in the global scope, just like the firstName and lastName
	console.log(this.firstName + " " + this.lastName);
}
var person = {
	firstName: "Penelope",
	lastName: "Barrymore",
	showFullName: function() {
		// "this" on the line below refers to the person object, because the showFullName function will be invoked by person object.
		console.log(this.firstName + " " + this.lastName);
	}
}
showFullName(); // Peter Ally

// window is the object that all global variables and functions are defined on, hence:
window.showFullName(); // Peter Ally

// "this" inside the showFullName() method that is defined inside the person object still refers to the person object, hence:
person.showFullName(); // Penelope Barrymore

注意的问题

有时候使用this会出现一些问题,当我们引用一个使用this的方法,这个方法使用this作为回调函数传值,并且this在方法里是以闭包形式出现,是在一个inner function里,下面的示例会来说说这些问题;


1.将一个方法作为回调函数进行传值,并且这个方法中使用了this:

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,也就是找不到‘this’这一点上一篇说bind/apply/call时说过

在上文代码中,将user.clickHandler方法作为回调传给click(),但是user.clickHandler在回调里没有指向object user,因为执行代码的时候this是定义在user.clickHander里面的,并且现在是button这个对象引用user.clickHandler的,this不会被指向到user,实际上现在的this是这个button对象本身;

解决这个问题可以参考我上一篇JavaScript笔记之Apply、Call and Bind,使用bind来绑定this:

$ ("button").click (user.clickHandler);
//Instead of this
 $("button").click (user.clickHandler.bind(user)); // P. Mickelson 43

2.this在闭包中的问题

另一个容易出问题的地方是在闭包中使用this,闭包不能通过this关键字访问外部函数(outer function)的this变量,包括内部函数(inner function),而只能被其函数本身(或者object,其实function本身就是object…):

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,所以就指向了公共的window对象,解决这个问题可以在匿名函数中使用this并传递给forEach:

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

也就是经常见到的:

var that = this;

3.方法被赋值给变量,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)

通过绑定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.调用方法时this的问题

调用方法在JavsScript中很普遍普通的做法,比较方法写出来就是被调用使用的哈:

// 创建两个对象,一个有avg方法,一个没有,没有的可以借助另一个来创建avg方法
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指向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, gameController.scores);

// 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

这四个问题,你看明白了吗?

this、作用域、执行上下文的概念在JavaScript开发中非常重要,透彻的理解这些概念也许不是一篇所谓“x分钟入门到精通”能说明白的,仍需要很多的实践练习,多想多用才能融会贯通;


想挑战一下?来这里:

reference:http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it,最近这几篇JavaScript笔记参照了好多这个站点的内容,然后精简、重写,作者非常吊,因为翻译过程中的错误或遗漏,可以参考源站,祝你幸福:);

有任何问题可以留言给我;


薛陈磊的博客 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明JavaScript笔记之this
喜欢 (2)
[905044086@qq.com]
分享 (0)
作者薛陈磊
关于作者:
非著名前端工程师,关注Html5、Css3、Javascript、Node.js和各种前端框架发展,学习管理技巧和团队建设方法,期待遇到更多前端小伙伴一起学习进步;
说点什么...
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址