javascript中this关键字详解
不管学习什么知识,习惯于把自己所学习的知识列成一个list,会有助于我们理清思路,是一个很好的学习方法。强烈推荐。
以下篇幅有点长,希望读者耐心阅读。
以下内容会分为如下部分:
1.涵义
1.1:this涵义
1.2:this指向的可变性
2.使用场合
2.1:全局环境
2.2:构造函数
2.3:对象的方法
3.使用注意点
3.1:避免多层嵌套this
3.2:避免数组处理方法中的this
3.3:避免回调函数中的this
1.涵义
1.1:this涵义
在我写的一篇关于 构造函数与new关键字 的关系的博文中谈及,new关键字总是会返回一个对象。这个对象可以是new调用构造函数时返回的空对象,也可以是在构造函数中使用return语句返回的复杂数据类型(包括对象,数组等)。
同样,与new关键字相同,this关键字总是返回一个对象。再说的详细一些,就是属性或方法“当前”所在的对象。
var Keith = {
firstName: 'Chou',
describe: function() {
return this.firstName;
}
};
console.log(Keith.describe()); //'Chou'
上面代码中,this.firstName表示describe方法当前所在对象的firstName属性。也就是说,在全局作用域下调用describe方法时,describe方法所在的当前对象是Keith,所以就是调用Keith.firstName。
1.2:this指向的可变性
由于对象的属性可以赋给另外一个对象,所以属性所在的当前对象是可变的。也就是说,this的指向是可变的。
var Keith = {
firstName: 'Chou',
describe: function() {
return this.firstName;
}
};
var Rascal={
firstName: 'King'
}
Rascal.describe=Keith.describe;
console.log(Rascal.describe()); //'King'
上面代码中,Keith对象中的describe属性赋给Rascal,于是describe方法中当前所在的对象就是Rascal,所以this.firstName就指向Rascal。因为是传址传递,所以修改firstName会对原对象有影响。如有疑问,请移步 这篇文章 。这个例子可能不便于理解,再看看下面这个例子。
function f(){
return this.firstName;
}
var Keith = {
firstName: 'Chou',
describe:f
};
var Rascal={
firstName: 'King',
describe:f
}
console.log(Keith.describe()); //'Chou'
console.log(Rascal.describe()); //'King'
上面代码中,把方法移到全局作用域下,函数f内部使用了this关键字。随着f所在的对象不同,this指向也就不同。
在全局作用域下this关键字会指向顶层对象(也就是window对象)。
var name='keith';
function person(){
var name='rascal';
return this.name;
}
console.log(person()); //'keith'
上面代码中,返回的是keith而不是rascal。原因在于this指向的是全局作用域。在全局作用域中定义一个函数,默认是指向window对象,而不是函数本身。但是,如果在函数内部不使用var来声明一个局部变量,那结果也会不同。
var name='keith';
function person(){
name='rascal';
return this.name;
}
console.log(person()); //'rascal'
上面代码中,在函数内部没有使用var来声明一个局部变量,那么此时函数内部的name属性不是局部变量,而是全局变量。所以会覆盖掉前面的name属性。如果对于局部变量和全局变量不了解,可以访问 这篇文章。
只要函数被赋给另外一个变量,this的指向会发生改变。
var Keith={
name:'keith',
describe:function(){
return this.name;
}
}
var name='rascal';
var f=Keith.describe;
console.log(f()) //'rascal'
上面代码中,返回的是rascal,而不是keith。因为Keith.describe被赋值给了f变量,而全局作用域下有一个name变量,所以Keith内部的函数的this指向就会指向f运行时所在的对象(顶层对象,也就是window对象)
总结一下:
1.javascript语言中,一切皆为对象(除了 undefined 和 null 之外),运行环境也是对象,所以函数都是在某个对象之中运行,this就是这个对象(环境)。
2.this的指向是动态的。如果函数在全局作用域中,那么this就会指向全局环境;如果函数位于某个对象中,那么this就会指向该对象。
2.使用场合
this的使用场合可以分为以下几个场合。
2.1:全局环境(全局作用域)
在全局作用域中使用this对象,它指向的就是顶层对象,也就是window对象。
function keith() {
return (this === window)
}
console.log(keith()) //true
上面代码中,不管是不是在函数内部,只要在全局作用域下运行,this就是指向顶层对象window。
2.2:构造函数
构造函数中的this,指向的是将要创建的对象实例。
function Keith() {
this.sex = 'boy';
}
var person = new Keith();
console.log(person.sex); //'boy'
上面代码中,在全局作用域下定义了Keith构造函数,然后调用构造函数并赋值给person对象实例。
构造函数创建的三个基本要求:函数名首字母大写;构造函数内部使用this关键字来指向即将生成的对象实例;使用new关键字来调用构造函数并返回对象实例。
如果想更进一步深入了解构造函数与new关键字的关系,请移步至 这篇文章 。
2.3:对象的方法
当A对象的方法被赋予B对象,该方法中的this就从指向A对象变成指向B对象。所以要特别小心,将某个对象的方法赋值个另外一个对象时,会改变this的指向。
var keith = {
sex: 'boy',
foo: function() {
return this.sex;
}
};
var rascal = {
sex: 'girl'
};
rascal.foo = keith.foo;
console.log(keith.foo()); //'boy'
console.log(rascal.foo()); //'girl'
上面代码中,把keith的foo函数赋值给了rascal,那么this的指向就从keith变成了rascal。这里涉及对象引用的知识,如有疑问,请移步至 这篇文章 。
如果某个方法位于多层对象的内部,这时为了简化书写,把该方法赋值给一个变量,往往会得到不一样的结果。
var a = {
b: {
p: 'keith',
c: function() {
return this.p;
}
}
};
var person = a.b.c;
console.log(person()); //undefined
上面代码中,c是两层对象里面的一个方法。为求简便,将其赋值给全局变量person,结果调用时,this指向了顶层对象window。而在window中变量p默认值为undefined。
要解决这个问题,可以只将c所在的对象赋值给person变量,或者是直接调用。
var person = a.b;
console.log(person.c()); //'keith'
console.log(a.b.c()); //'keith'
3.使用注意点
3.1:避免多层嵌套this
当在闭包中使用多层this,则this都会指向window。
function keith() {
console.log(this);
return function() {
return this;
}
}
keith(); //window
keith()(); //window
上面代码中,在一个函数中返回另外一个匿名函数是闭包的特点之一,可以看出,当在闭包中使用this对象都会指向全局作用域中的window对象。
如果在函数外包含一个对象,则内部this指向全局作用域,而外部this对象指向当前作用域。
var o = {
f1: function() {
console.log(this);
(function() {
console.log(this)
})();
}
};
o.f1(); //Object , Window
上面代码包含两层this,结果运行后,第一层指向当前对象,第二层指向全局对象。
实际执行的是如下代码。
function keith() {
console.log(this);
}
var o = {
f1: function() {
console.log(this);
var f2 = keith();
}
};
o.f1(); //Object , Window
要实现多层this嵌套,有两种解决方法:
一是在第二层中改用一个指向外层this的变量。
var o = {
f1: function() {
console.log(this);
var that = this;
(function() {
console.log(that);
})();
}
};
o.f1(); //Object , Object
上面代码中,定义了局部变量that,固定指向了外层的this,然后在内层中使用that,就不会发生this指向的改变。但是如果函数外部内有嵌套一个对象,this还是会指向全局。
二是Javascript中的严格模式。在严格模式下,如果内部函数的this指向了window对象,就会报错。
var a = {
count: 0,
fun: function() {
'use strict';
return this.count++;
}
}
var f = a.fun;
console.log(f()) //'TypeError: this is undefined'
上面代码中,fun方法使用严格模式声明。把a对象中的fun方法赋值给全局变量f,那么this此时指向window对象,在严格模式下,就会报错。如果函数外部没有嵌套一个对象,那么不会报错,而是会返回undefined。
3.2:避免数组处理方法中的this
数组的map和foreach方法,允许提供一个函数作为参数。这个函数内部不应该使用this。
var keith = {
a: 'Hello',
b: ['b1', 'b2'],
c: function() {
this.b.forEach(function(item) {
console.log(this.a + ' ' + item);
})
}
};
keith.c();
//undefined b1
//undefined b2
上面代码中,forEach方法的回调函数中的this,其实指向的是window对象,因此取不到keith.a的值,同上也属于避免多层嵌套this。也就是说,内层的this不指向外部函数,而是指向顶层对象。
要解决这个方法,可以使用that变量来代替回调函数中的this。
var keith = {
a: 'Hello',
b: ['b1', 'b2'],
c: function() {
var that = this;
this.b.forEach(function(item) {
console.log(that.a + ' ' + item);
})
}
};
keith.c();
//Hello b1
//Hello b2
另外一种方法,就是让this做为forEach方法的第二个参数,来固定它的运行环境。
var keith = {
a: 'Hello',
b: ['b1', 'b2'],
c: function() {
this.b.forEach(function(item) {
console.log(this.a + ' ' + item);
}, this)
}
};
keith.c();
//Hello b1
//Hello b2
3.3:避免回调函数中的this
回调函数中的this往往会改变指向。
var o = {
f: function() {
console.log(this === o);
}
};
o.f(); // true;
上面代码中,调用o对象的f方法,返回true。
但是,如果将f方法指定给某个按钮的click事件,this的指向就变了。
$('button').on('click',o.f);
上面代码中,使用了jquery方法来获取button元素,并绑定click事件。点击按钮后控制台会显示false。原因是此时this不再指向o对象了,而是指向按钮的DOM对象,因为f方法是在按钮对象的环境中被调用的。
总结一下:
a:如果想要多层嵌套this关键字,最常用的解决方法就是使用that变量,固定指向外层的this,然后在内层中使用that变量。就不会发生内层this指向全局的问题。
b:如果在回调函数中使用this关键字,注意this的指向问题。
鉴于篇幅的原因,会在下次的分享中谈及如何改变this的指向问题,会详细介绍call,apply,bind这三个方法。
完。
感谢大家的阅读。
转载请注明出处:http://www.cnblogs.com/Uncle-Keith/p/5814578.html
javascript中this关键字详解的更多相关文章
- [No000069]Javascript中this关键字详解
Quiz 请看下面的代码,最后alert出来的是什么呢?(chrome下按F12,选择Console直接复制粘贴运行) var name = "Bob"; var nameObj ...
- javascript中new关键字详解
和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 ...
- Javascript中prototype属性详解 (存)
Javascript中prototype属性详解 在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不 ...
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
- 【转载】C/C++中extern关键字详解
1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern ...
- (转)javascript中event对象详解
原文:http://jiajiale.iteye.com/blog/195906 javascript中event对象详解 博客分类: javaScript JavaScriptCS ...
- 【JavaScript中的this详解】
前言 this用法说难不难,有时候函数调用时,往往会搞不清楚this指向谁?那么,关于this的用法,你知道多少呢? 下面我来给大家整理一下关于this的详细分析,希望对大家有所帮助! this指向的 ...
- JavaScript中的this详解
前言 this用法说难不难,有时候函数调用时,往往会搞不清楚this指向谁?那么,关于this的用法,你知道多少呢? 下面我来给大家整理一下关于this的详细分析,希望对大家有所帮助! this指向的 ...
- JS中this关键字详解
本文主要解释在JS里面this关键字的指向问题(在浏览器环境下). 阅读此文章,还需要心平气和的阅读完,相信一定会有所收获,我也会不定期的发布,分享一些文章,共同学习 首先,必须搞清楚在JS里面,函数 ...
随机推荐
- java 显示透明背景png图片
首先理由ps生成一个背景透明的png图片,然后设置JPanel面板的透明属性,也就是panel.setOpaque(false);设置为透明 class MyPanel extends JLayere ...
- Linux 内存管理
查看Linux内存使用情况 free -m Linux内存清理:绝大多数情况下都不需要此操作,因为cache的内存在需要的时候是可以自动释放的- 最好先sync几次,再清理内存,有下面三个级别,数值越 ...
- iOS_UIImage_jpg<-->png转换
// png 图片转化是无损的. 可以有透明效果. // jpg 图片转化是有损的. 质量因子. - (void)jpgToPng { UIImage * image = [UIImage image ...
- android gridview几个重要属性(android:listSelector自带内部padding分析)
一.android:scrollbarStyle 决定状态条的位置 常用属性outsideOverlay,滚动条在最外层,gridview设置的padding在滚动条的内侧 二.andr ...
- ADO.NET封装的SqlHelper
参照别人的方法,顺便再次复习下ADO.NET的相关知识.为自己的类库做准备. namespace Common.SqlHelper { /// <summary> /// ADO.NET- ...
- 改进网站设计的免费jQuery插件Top 7
Animate Scroll.js是一个简单的jQuery插件,它用来为滚动增加延迟效果.同时你还可以自定义滚动样式(30多种滚动效果)和滚动速度,还有一个可以操纵”滚动结束位置”的”padding” ...
- 前端实现QQ会话功能(常用笔记3)
<a href="tencent://message/?uin=客服QQ号码&Menu=yes" target="blank"></a ...
- 【FTP】在自己的电脑上建立FTP服务器
<1> 添加新用户: “计算机”点击右键 “管理”得到: 点击“本地用户和组”,点击“用户”: 右键 “新用户”: 设定用户名与密码,这是以后连FTP服务器时用的登录用户名与密码. < ...
- Socket开发框架之数据传输协议
我在前面一篇随笔<Socket开发框架之框架设计及分析>中,介绍了整个Socket开发框架的总体思路,对各个层次的基类进行了一些总结和抽象,已达到重用.简化代码的目的.本篇继续分析其中重要 ...
- js带上框架和防止被iframe的代码
1.永远都会带着框架<script language="JavaScript"><!--if (window == top)top.location.href = ...