理解JavaScript中的“this”
对于javascript的初学者来说,一般对“this”关键字都感到非常迷惑。本文的目的旨在让你全面的了解“this”,理解在每一个情景下如何使用“this”,希望通过本文,可以帮助同学们不在害怕“this”!!
从生活中发现
其实“this”就是我们平时用的一个代词。打个简单的比喻:
“小豆豆是一个很幽默的人,他非常喜欢看《暴走漫画》”
但是你也可以这样写:
“小豆豆是一个很幽默的人,小豆豆非常喜欢看《暴走漫画》”
但是日常生活中我们会一直用这种方式来描述一个人吗?如果你的回答是Yes,好吧,估计再也没有人愿意跟你做朋友了,我没骗你…(开个玩笑:-) )。 所以,人类就发明了这样一种看似高端、洋气、上档次的代名词,说白了javascript中的“this”也就是这么一个东东。
一个简单的例子:
person = {
firstName: "zhou",
lastName: "Quan",
fullName:function(){
alert(person.firstName + " " + person.lastName);//Zhou Quan
alert(this.firstName + " " + this.lastName);//Zhou Quan
}
}
person.fullName();
我们可以根据前面所说的方式来理解这个程序的结果, 但是这仅仅停留在表面而已,下面我们再进行更深一步的理解。
this基础
首先,我们知道javascript中所有的函数都拥有属性,就像一个对象也拥有属性一样。当一个函数被执行时,他就拥有了“this”属性--调用this所在函数的对象。this永远指向一个独一无二的对象,这个对象通常包含一个函数/方法,虽然它也可以被用在一个函数之外的全局作用域中(global scope)。注意:在严格模式(strict mode)中,在全局函数或者匿名函数中this的值为“undefined”(也就是说this没有绑定任何对象)。
“this”被用在一个函数内部(假设是function A),那么它包含的值是调用“function A”的对象。我们需要this来访问调用“function A”的对象的属性和方法,特别是当我们不知道到这个对象名叫什么的时候,或者这个对象没有名字的时候。其实,this就是调用当前函数的对象的一个代名词!
我们来举一个简单的例子:
var person = {
firstName: "zhou",
lastName: "Quan",
//this将为person对象,因为person对象会调用FullName方法
FullName:function(){
alert(this.firstName + " " + this.lastName); //zhou Quqn
}
}
person.FullName();
$ ("button").click (function (event) {
// $(this) 的值将是 ($("button")) 对象
// 因为这个button对象调用了 click () 方法
console.log ($ (this).prop ("name"));
});
this中存在的最大的问题
this是没有被指定值的,直到有一个对象调用了this所在的这个函数(我们暂且给它一个称号:this Function)。
也就是说,只有当有一个对象调用了this Function之后,this Fuction中的this才被指定!在大多数情况下,this Function被调用之后,this都会被指定一个值,但是,也有少数的情况下this是没有值的,这个问题我们将在后面进一步探讨。
全局作用域(global scope)中的this
当一段代码在浏览器中执行时,所有的全局变量和函数都是在“window”对象上定义的。因此,当”this”被用在全局函数中是,他指定的值是”window”对象。我们来看下面一段代码:
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的回调函数作为参数传递时我们会遇到这样这个问题:
// We have a simple object with a clickHandler method that we want to use when a button on the page is clicked
var user = {
name:"zhouquan",
age:21,
clickHandler:function (event) {
console.log (this.name + " " + this.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); //undefined
在上面的代码中,我们知道$(“button”)是一个对象,”user.clickHandler”作为click()方法的回调函数。user.clickHandler方法中的this不再是指向user对象,它现在指向的是调用点击事件的这个button对象。即使我们是使用“user.clickHandler”来调用clickHandler方法,但是clickHandler()方法是在button对象作为上下文(Context)的环境中运行的,所以,this指向的是button对象。
从这一点上我们可以看出,当上下文(Context)改变--当我们在其他的对象上执行一个方法。“this”所指向的不在是以前的那个对象,而是现在调用这个方法的新的对象。
为了解决这个问题,我们可以使用apply, call和bind的方法来指定“this”所指向的对象。所以上面代码的最后一行只需改为:
$("button").click (user.clickHandler.bind (user)); //zhouquan 21
解决闭包(closure)中的this问题
另外一种容易让我们感到困扰的是当我们使用了一个内部函数(闭包)的时候。首先我们应该明确的一点就是:闭包中不能通过“this”关键字来访问外边函数的this变量,因为闭包中的this他指向的window对象。来看一个例子:
var user = {
country: "china",
data:[
{name:"ZhouYi", age:21},
{name:"ZhouEr", age:22}
],
clickHandler:function(){
//在这个作用域中使用this是OK的,他指向的是user对象
this.data.forEach(function(person){
//但是这个内部函数的this不再指向user对象
console.log("What is This referring to? " + this); //Object Window
console.log(person.name + " is come from " + this.country);
//ZhouYi is come from undefined
//ZhouEr is come from undefined
})
}
};
user.clickHandler();
clickHandler:function(){
//在这个作用域中使用this是OK的,他指向的是user对象
//我们定义一个theUserObj来保存this的值
var theUserObj = this;
this.data.forEach(function(person){
//现在我们再用theUserObj来访问user对象的属性就没问题了
console.log(person.name + " is come from " + theUserObj.country);
//ZhouYi is come from china
//ZhouEr is come from china
})
}
解决方法作为变量进行传递时的this问题
当我们把一个对象里面的函数作为参数传递给另外一个对象时,this所指定的对象往往会超出我们的想象,来看个例子吧:
var data = [
{name: "ZhouYi", age:21},
{name: "ZhouEr", age:22}
];
var user = {
//这里的data属性是属于user对象的
data :[
{name:"ZhouYi", age: 31},
{name:"ZhouEr", age: 32}
],
showData:function(){
console.log(this.data[0].name + " " + this.data[0].age) ;
}
}
//把user.showData赋值给一个变量
var showUserData = user.showData;
//当我们执行这个showUserData函数时,输出的数据来自全局的data(最外层定义的),而不是来自与user对象中的data.
showUserData(); //ZhouYi 21
解决这个问题的方式跟前面提到的解决回调函数中的this问题类似,我们还是采用apply, call和bind的方法来绑定指定的上下文,也就是this该指向哪个对象。
var showUserDate = user.showData.bind(user);
解决方法借用中的this问题
何谓方法调用?打个简单的比方,有两个对象A和B,A、B有相同的属性,但是B比A还多了一个方法,这个方法是对B中的数据进行处理,现在我们想把B这种方法也给A用用:
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 sumOfSores = this.scores.reduce(function(prev, cur, index, array){
return prev + cur;
});
this.avgScore = sumOfSores /this.scores.length;
}
}
//由于avg()是被appController调用的,所以avg中的this指向的是appController对象。
gameController.avgScore = appController.avg();
console.log(gameController.avgScore); //undefined
如何解决这个问题呢? 为了让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的上下文,也就是this所指向的对象;第二个参数表示要进行运算的数据。当然啦,我们还可以通过这种方式达到同样的效果:
gameController.avg = appController.avg;
gameController.avg();
小结
希望我写的这些东西可以给你带来一些帮助,如果你觉得哪个地方存在不足,请你在评论处提出来,我将及时完善,免得误导后面的同学;如果你觉得这篇文章讲得还可以,请帮我推荐给更多的有需要的同学阅读,谢谢!
参考资料:http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/
理解JavaScript中的“this”的更多相关文章
- 理解JavaScript中的原型继承(2)
两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...
- 深入理解JavaScript中创建对象模式的演变(原型)
深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...
- 深入理解JavaScript中的属性和特性
深入理解JavaScript中的属性和特性 JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性. 主要内容如下: 理解JavaSc ...
- 深入理解javascript中执行环境(作用域)与作用域链
深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...
- 【干货理解】理解javascript中实现MVC的原理
理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程 ...
- 理解javascript中的策略模式
理解javascript中的策略模式 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 使用策略模式的优点如下: 优点:1. 策略模式利用组合,委托等技术和思想,有效 ...
- 深入理解javascript中的立即执行函数(function(){…})()
投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014-06-12 我要评论 这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是 ...
- 转载 深入理解JavaScript中的this关键字
转载原地址: http://www.cnblogs.com/rainman/archive/2009/05/03/1448392.html 深入理解JavaScript中的this关键字 1. 一 ...
- js架构设计模式——理解javascript中的MVVM开发模式
理解javascript中的MVVM开发模式 http://blog.csdn.net/slalx/article/details/7856769 MVVM的全称是Model View ViewMod ...
- 全面理解Javascript中Promise
全面理解Javascript中Promise 最近在学习Promise的时候,在网上收集了一些资料,发现很多的知识点不够系统,所以小编特意为大家整理了一些自认为 比较好的文章,供大家更好地学习js中非 ...
随机推荐
- 如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车
阅读目录 前言 回顾 梳理 实现 结语 一.前言 之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能.本篇准备把剩下的购物车的基本概念一次处理完. 二.回顾 在动手之前我对之 ...
- RabbitMq应用二
在应用一中,基本的消息队列使用已经完成了,在实际项目中,一定会出现各种各样的需求和问题,rabbitmq内置的很多强大机制和功能会帮助我们解决很多的问题,下面就一个一个的一起学习一下. 消息响应机制 ...
- JSON.parse()和JSON.stringify()
1.parse 用于从一个字符串中解析出json 对象.例如 var str='{"name":"cpf","age":"23&q ...
- ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之目录导航
ASP.NET MVC with Entity Framework and CSS是2016年出版的一本比较新的.关于ASP.NET MVC.EF以及CSS技术的图书,我将尝试着翻译本书以供日后查阅. ...
- 解决vs创建或打开C++浏览数据库文件*.sdf时发生错误的问题
VS2012, 创建或打开C++浏览数据库文件*.sdf时发生错误. IntelliSense 和浏览信息将不能用于C++项目. 请确保已安装 Microsoft SQL Server Compac ...
- EntityFramework的多种记录日志方式,记录错误并分析执行时间过长原因(系列4)
前言 Entity Framework 延伸系列目录 今天我们来聊聊EF的日志记录. 一个好的数据库操作记录不仅仅可以帮你记录用户的操作, 更应该可以帮助你获得效率低下的语句来帮你提高运行效率 废话不 ...
- FILE文件流的中fopen、fread、fseek、fclose的使用
FILE文件流用于对文件的快速操作,主要的操作函数有fopen.fseek.fread.fclose,在对文件结构比较清楚时使用这几个函数会比较快捷的得到文件中具体位置的数据,提取对我们有用的信息,满 ...
- php 基础代码大全(不断完善中)
下面是基础的PHP的代码,不断完善中~ //语法错误(syntax error)在语法分析阶段,源代码并未被执行,故不会有任何输出. /* [命名规则] */ 常量名 类常量建议全大写,单词间用下划线 ...
- CommandPattern
/** * 命令模式 * @author TMAC-J * 将调用者和接受者分离 * 可以将一组命令组合在一起,适合很多命令的时候 */ public class CommandPattern { i ...
- 简单分析JavaScript中的面向对象
初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...