深入理解Javascript中this的作用域


Posted in Javascript onAugust 12, 2014

大家在使用Javascript的时候经常被this这个家伙搞得晕头转向的。对大多数有OOP开发经验的开发人员来说this是当前作用域中引用普通元素的标识符,但是在Javascript中它却显得古灵精怪的,因为它不是固定不变的,而是随着它的执行环境的改变而改变。在Javascript中this总是指向调用它所在方法的对象。

举一个简单的例子:

function test(){

alert(this);

}

var obj=function(){

var name='testObj';

}

obj.objTest=test;

test();

obj.objTest();

把这段代码放到HTML中运行这个页面,你会看到首先提示一个警告[object window],然后第二个警告。

var obj=function(){

var name='testObj';

}

我们先定义了一个test()方法,并在方法内部调用alert()方法将this显示出来,然后定义了一个obj函数对象,并给它加了一个私有的字段name,同时给它加了一个静态的方法objTest(),而这个函数则直接指向test()函数。

分别调用test()和obj.objTest()方法,第一次警告框提示的是Window对象,而第二次提示的是我们定义的obj这个函数的代码。这说明了test函数在两次执行的时候this的值是不同的!

这就说明了当调用函数的对象不同的时候,其内部的this关键字指代的对象是不同的。这里需要值得注意的是Javascript是基于对象的语言,当我们的变量或者函数定义在<script></script>标签的根下的时候其实相当于给window对象加了相应的属性或方法,所以当我们利用function test(){}代码定义一个函数的时候,其实相当于给window对象添加了一个新的函数,即window.test()函数。

我们可以做一个实验:

function test(){

alert(this);

}

alert(test===window.test);

警告框提示的将是true,这说明当我们在调用test()这个函数时相当于调用的是window.test()。所以当我们调用test()函数的时候调用这个函数的对象其实是window对象,this指代的是window对象,所以我们在alert(this)的时候弹出的警告窗口内容是[object Window]。我们将obj.objTest=test相当于把obj.objTest()指向test(),所以当我们调用obj.objTest()函数时相当于在obj调用了test()这个函数,所以现在this指代的是obj对象,提示的就是obj这个Function也就是我们看到的代码。

说到这应该也解释的差不多了,可能上面的例子太抽象,想象不出来它能在什么情况下用到,那我们现在就假设一个需求,做一个贴近实用一点的例子。

假设我们现在页面中的所有超链接在点击之后颜色要改为红色,用Javascript实现。大体的思路应该是获取页面中所有的<a>标签,然后遍历所有的<a>标签,给每一个注册一个click事件,事件触发后我们将它的color值设为red。

示例代码如下:

//改变颜色

function changeColor(){

this.style.color='#f00';

}

//初始化,给所有 a 标签注册事件

function init(){

var customLinks=document.getElementsByTagName('a');

for(i in customLinks){

//你也可以使用事件侦听器方式来注册事件

//由于要兼容IE,FF等浏览器可能需要更多代码,您可以自行编写

customLinks[i].onclick=changeColor;

}

}

window.onload=init;

将这段代码添加到HTML文档中,并在文档中添加一些超链接,当超链接点击后颜色会变成红色,这里我们定义的changeColor()函数中this关键字在点击超链接触发函数的时候它指代的是当前这个超链接。而如果你直接调用changeColor()函数浏览器会报错,提示Error: ‘this.style' is null or not an object或者undefined之类的错误。

不知道说到这能不能让正在看文章的你对Javascript中的this关键字有了一些自己的了解呢?或者你已经不耐烦了?(:P)

其实要想真正对这个问题有更深入的理解那么必须对Javascript的作用域和作用域链有深入的理解。

作用域,顾名思义就是指某一属性或方法具有访问权限的代码空间,简单的说也就是这个变量或方法它在代码中的的适用范围。在大多数的OOP中主要有public,private,protect三种作用域,对着三种作用域在这里就不详细解释了,如果有OOP的经验应该都有深入的了解。在这里我要说的是这三种作用域类型对Javascript来说几乎是毫无意义的,因为Javascript中只有一种公共作用域,在Javascript中作用域是在函数中进行维护的。举个例子:

var test1='globle variable';

function example(){

var test2='example variable';

alert(test1);

alert(test2);

}

example();

alert(test1);

alert(test2);

根据我们前面解释的,这里的test1变量相当于window的一个属性,所以它会在整个window作用域内起作用,而test2则在example()函数的内部声明,所以它的作用域也就维持在example()方法的内部,如果在函数的外部调用test2浏览器会提示出错。而在example()内部调用test1则没问题。

根据这个我们再举一个例子:

var test='globle variable';

function example(){

var test='example variable';

}

example();

alert(test);

这个例子运行会是什么结果呢?对,警告框会提示“globle variable”,因为example()函数内部的test变量其作用域只维持在内部,不会影响外部的test变量。如果我们将example()内部test变量的var关键字去掉呢?你可以自己试试。

说到这就有牵扯出另外一个概念,那就是作用域链的概念。作用域链就是可以确定变量值的路径。由上面一个例子可以看出,var关键字是用来维护作用域链的,如果变量使用了var关键字声明那么他就可以看作为作用域链的终点。同样函数的形参的定义也会起到类似的作用。

说到这你对this这个精灵古怪的家伙有了比较清晰的认识了吧?根据它简单的一个诠释,this总是指向调用它所在函数的对象,根据作用域和作用域链,我们会很清晰的确定this的真面目。临末尾再来一个开始那个例子的简单变化:

function test(){

alert(this);

}

var obj=function(){

var name='testObj';

}

obj.objTest=test;

obj.objTest2=function(){

test();

}

test();

obj.objTest();

obj.objTest2();

你猜会提示什么内容呢?你可以运行一下试试(:P);

既然this是根据调用其所在函数的对象的改变而改变的,那我们可不可以强制改变它的调用对象呢?答案是肯定的,以后的文章会介绍一下这部分内容,以及Javascript中不同类型的数据成员的实现方式,闭包等概念。

本人在学习过程中的一些经验和心得体会,写出来一是与大家分享另外也能检视自己的不足,如写的有问题还请批评指教,甚为感谢!

Javascript 相关文章推荐
JavaScript实现页面滚动图片加载(仿lazyload效果)
Jul 22 Javascript
JQuery页面图片切换和新闻列表滚动效果的具体实现
Sep 26 Javascript
页面js遇到乱码问题的解决方法是和无法转码的情况
Apr 30 Javascript
详解JavaScript的AngularJS框架中的表达式与指令
Mar 05 Javascript
js和jQuery设置Opacity半透明 兼容IE6
May 24 Javascript
JavaScript九九乘法口诀表的简单实现
Oct 04 Javascript
基于JavaScript实现本地图片预览
Feb 08 Javascript
详解vue.js之props传递参数
Dec 12 Javascript
JavaScript使用math.js进行精确计算操作示例
Jun 19 Javascript
详解vue-cli3多页应用改造
Jun 04 Javascript
微信小程序使用车牌号输入法的示例代码
Aug 20 Javascript
从零开始在vue-cli4配置自适应vw布局的实现
Jun 08 Javascript
javascript实现在某个元素上阻止鼠标右键事件的方法和实例
Aug 12 #Javascript
JavaScript弹出窗口方法汇总
Aug 12 #Javascript
Javascript中3种实现继承的方法和代码实例
Aug 12 #Javascript
jQuery判断checkbox是否选中的3种方法
Aug 12 #Javascript
jquery判断浏览器后退时候弹出消息的方法
Aug 11 #Javascript
jQuery根据ID获取input、checkbox、radio、select的示例
Aug 11 #Javascript
JavaScript中跨域调用Flash的方法
Aug 11 #Javascript
You might like
用PHP制作静态网站的模板框架(一)
2006/10/09 PHP
利用PHP制作简单的内容采集器的代码
2007/11/28 PHP
php遍历数组的方法分享
2012/03/22 PHP
CI框架开发新浪微博登录接口源码完整版
2014/05/28 PHP
PHP中的类型提示(type hinting)功能介绍
2015/07/01 PHP
thinkPHP多域名情况下使用memcache方式共享session数据的实现方法
2016/07/21 PHP
ThinkPHP5.1验证码功能实现的示例代码
2020/06/08 PHP
用jscript实现新建和保存一个word文档
2007/06/15 Javascript
javascript实现文本域写入字符时限定字数
2014/02/12 Javascript
多个jQuery版本共存的处理方案
2015/03/17 Javascript
详解JavaScript的AngularJS框架中的表达式与指令
2016/03/05 Javascript
Nuxt.js实现校验访问浏览器类型的中间件
2018/08/24 Javascript
JavaScript创建对象方式总结【工厂模式、构造函数模式、原型模式等】
2018/12/19 Javascript
layui动态渲染生成左侧3级菜单的方法(根据后台返回数据)
2019/09/23 Javascript
Vue组件通信入门之Provide和Inject机制
2019/12/29 Javascript
云服务器部署Node.js项目的方法步骤(小白系列)
2020/03/23 Javascript
vue绑定数字类型 value为数字的实例
2020/08/31 Javascript
使用Python内置的模块与函数进行不同进制的数的转换
2016/03/12 Python
Django卸载之后重新安装的方法
2017/03/15 Python
django项目运行因中文而乱码报错的几种情况解决
2017/11/07 Python
Python3中内置类型bytes和str用法及byte和string之间各种编码转换 问题
2018/09/27 Python
python语言线程标准库threading.local解读总结
2019/11/10 Python
在jupyter notebook 添加 conda 环境的操作详解
2020/04/10 Python
Python pip install如何修改默认下载路径
2020/04/29 Python
基于python实现计算两组数据P值
2020/07/10 Python
css3 给背景设置渐变色的方法
2019/09/12 HTML / CSS
使用CSS3实现多列布局与多背景的技巧
2016/02/29 HTML / CSS
美国知名的旅游网站:OneTravel
2018/10/09 全球购物
什么是用户模式(User Mode)与内核模式(Kernel Mode) ?
2015/09/07 面试题
女大学生毕业找工作的自我评价
2013/10/03 职场文书
《童年的发现》教学反思
2014/02/14 职场文书
高中教师考核方案
2014/05/18 职场文书
文艺晚会策划方案
2014/06/11 职场文书
2014报到证办理个人委托书
2014/10/08 职场文书
幼师辞职信范文
2015/02/27 职场文书
劳动保障个人工作总结
2015/03/04 职场文书