深入理解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 相关文章推荐
给jQuery方法添加回调函数一款插件的应用
Jan 21 Javascript
jQuery中offsetParent()方法用法实例
Jan 19 Javascript
jQuery实现无限往下滚动效果代码
Apr 16 Javascript
js 动态生成json对象、时时更新json对象的方法
Dec 02 Javascript
Vue系列:通过vue-router如何传递参数示例
Jan 16 Javascript
fetch 使用及如何接收JS传值
Nov 11 Javascript
js传递数组参数到后台controller的方法
Mar 29 Javascript
用vue2.0实现点击选中active其他选项互斥的效果
Apr 12 Javascript
vue+vant使用图片预览功能ImagePreview的问题解决
Apr 10 Javascript
解决antd Form 表单校验方法无响应的问题
Oct 27 Javascript
JavaScript实现网页tab栏效果制作
Nov 20 Javascript
vue 实现基础组件的自动化全局注册
Dec 25 Vue.js
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实现更新中间关联表数据的两种方法
2014/09/01 PHP
JavaScript效率调优经验
2009/06/04 Javascript
Array, Array Constructor, for in loop, typeof, instanceOf
2011/09/13 Javascript
html中使用javascript调用本地程序(exe、doc等)实现代码
2013/04/26 Javascript
jQuery中:has选择器用法实例
2014/12/30 Javascript
javascript实现九宫格相加数值相等
2020/05/28 Javascript
精彩的Bootstrap案例分享 重点在注释!(选项卡、栅格布局)
2016/07/01 Javascript
JSONP跨域请求实例详解
2016/07/04 Javascript
基于vue的短信验证码倒计时demo
2017/09/13 Javascript
jQuery实现的两种简单弹窗效果示例
2018/04/18 jQuery
Layui给数据表格动态添加一行并跳转到添加行所在页的方法
2018/08/20 Javascript
使用Angular material主题定义自己的组件库的配色体系
2019/09/04 Javascript
解决vue打包报错Unexpected token: punc的问题
2020/10/24 Javascript
[02:42]决战东方!DOTA2亚洲邀请赛重启荣耀之争
2017/03/17 DOTA
Python爬取网页中的图片(搜狗图片)详解
2017/03/23 Python
Python实现按当前日期(年、月、日)创建多级目录的方法
2018/04/26 Python
Python数据预处理之数据规范化(归一化)示例
2019/01/08 Python
Python多线程原理与用法实例剖析
2019/01/22 Python
Python面向对象程序设计类的多态用法详解
2019/04/12 Python
PyQt5+Caffe+Opencv搭建人脸识别登录界面
2019/08/28 Python
Python操作qml对象过程详解
2019/09/26 Python
Python的对象传递与Copy函数使用详解
2019/12/26 Python
基于pygame实现童年掌机打砖块游戏
2020/02/25 Python
Django Xadmin多对多字段过滤实例
2020/04/07 Python
Python安装并操作redis实现流程详解
2020/10/13 Python
python爬取天气数据的实例详解
2020/11/20 Python
英国第一的市场和亚马逊替代品:OnBuy
2019/03/16 全球购物
美国鲜花递送:UrbanStems
2021/01/04 全球购物
联想智利官方网站:Lenovo Chile
2020/06/03 全球购物
介绍一下Ruby中的对象,属性和方法
2012/07/11 面试题
酒店副总经理岗位职责范本
2014/02/04 职场文书
《草原》教学反思
2014/02/15 职场文书
初级党校心得体会
2014/09/11 职场文书
个人查摆剖析材料
2014/10/16 职场文书
工程服务质量承诺书
2015/04/29 职场文书
幼儿园开学温馨提示
2015/07/15 职场文书