博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript基础教程之JS函数中对this的疑惑与不解
阅读量:4195 次
发布时间:2019-05-26

本文共 6889 字,大约阅读时间需要 22 分钟。

Write By Monkeyfly

以下内容均为原创,如需转载请注明出处。

前提

最近渐渐的对JavaScript产生了学习的兴趣,遇到不会的就想把问题搞明白,即使打破砂锅我也要问到底。近期在JavaScript重新学习的过程中发现,我几乎每天都能碰到一些难以理解的问题,说真的,主要还是自己太菜了,逻辑思维能力也不好,理解起来感觉非常的吃力,在询问的过程中也有人说我笨,讲了半天都不理解。其实我自己也心知肚明,该虚心请教的还是要虚心请教,毕竟自己的能力在这里放着,不努力提高等什么呢,对吧。

今天呢,主要学习了,在自己手动敲写委托事件案例的过程中,运行的时候发现了一个错误:

错误代码如下所示:

Uncaught TypeError: Cannot read property 'style' of undefined

现场还原

HTML代码如下:

  • 111
  • 222
  • 333
  • 444

JS代码如下:

//获取到ul元素var oUl = document.getElementById("list2");//获取到li元素的集合,是一个伪数组var oLi = document.querySelectorAll("#list2 li");//获取到input元素var oBtn = document.getElementById("btn");/*现在要实现这么一个效果:鼠标移入li,li变黄;鼠标移出li,li变绿*/for (var i = 0; i < oLi.length; i++) {    /*给每一个li元素绑定鼠标移入事件,并设置背景颜色*/    oLi[i].onmouseover = function(){
oLi[i].style.backgroundColor = "yellow"; }; /*给每一个li元素绑定鼠标移出事件,并设置背景颜色*/ oLi[i].onmouseout = function(){
oLi[i].style.backgroundColor = "green"; };}

错误代码图如下所示:

错图1:

错误图片1

错图2

错误图片2

问题分析:

发现问题时,我首先想到的是“背景色的属性名是不是写错了?”,因为在写的时候我就不太确定,也没有去查一下,就凭借记忆中的印象直接写上去了。而且我记得背景颜色好像是没有缩写的,但是还不确定,毕竟平时js用的太少,没感觉。先不管了,就这样写了:backgroundColor。后来百度查了一下还真的没记错。

  • 在CSS中,属性名一般是这样写的background-color,自称“中划线写法“或“分割线写法”;
  • 在js中,设置属性用的是驼峰写法:形如backgroundColorfontSize等。

但是百度的过程中,发现也有形如这样的错误写法,如:bgColorbgcolor。为了弄清楚到底有没有这两个写法,我又去查了一下。

1.bgcolor 是HTML的用法,这个属性规定了文档(body)的背景颜色。table元素也能用。如下所示:

但是官方呢,不赞成使用 body 元素的 bgcolor 属性;而且HTML5 也明确规定不支持 <table>标签的bgcolor 属性。都让使用 CSS 代替。自己打脸了吧。

2.bgColor是JScript的用法,那就更不用去管了。直接跳过。

经过一阵查询,发现不是属性名的拼写错误,于是就直接在浏览器的开发人员工具打断点分析,定位一下到底是哪个地方出错了,然后对oLi[i]添加了监听,即 这里写图片描述,发现oLi[i](for循环中的一个li元素对象)提示“undefined”。如下图所示:

这里写图片描述

奇怪,怎么会“undefined”呢?不应该啊,oLi[i].onmouseover都没问题,oLi[i].style.backgroundColor就不能用了,什么鬼?然后我就去看了作者的源码。因为我是按照人家的案例自己敲的代码,写之前只看了要实现什么功能就自己开始写了,想在写完之后再与作者的代码进行比对,看看自己写的代码到底和人家写的有什么区别或者有什么问题。

这不,问题就来了。

贴上原作者的代码:

//鼠标移入变红,移出变白for(var i=0; i

这区别很容易就看出来了,人家写的方法中用的是this对象,而我没有用(其实我根本就不会用),然后我就很纳闷,一直不理解为什么就非要用this,用原始的对象难道就不可以吗?我觉得我写的也没有错啊,不是都可以实现的吗?为什么偏偏就我的报错了,真奇怪。突然之间感觉我什么都不会,连this对象都不会用,而且别人用了还不知道为什么要这么用。瞬间感觉自己弱爆了,因为我觉得一般人都不会卡在这里,至少也知道this的用法,不像我这种初级的遇到了各种解决不了的问题。于是就去百度了this对象的用法,结果看了半天只知道了:

this是Javascript语言的一个关键字。它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。this不管在什么情况下都指的是,调用函数的那个对象。

我也知道this这样用没问题,我想知道的是原因。于是乎,我就又去群里找大神请教了,你猜怎么着?

第一个帮我的人:远程协助我解决问题,我非常感激。我们交流了半天,她最终放弃了,说了那么多,演示了那么久,我还是不明白,理解不了。因为我觉得她说的还是不够清晰透彻,没从根本上说明白到底为什么要用this。最后她实在没办法了,就直呼我:“太笨了,而且这个好难讲啊,我觉得我讲的已经够清楚了,你为什么就是不明白?剩下的只能靠你自己了。我帮不了你。”我只能说:“你说得对,我也很无奈,我就是难以理解。”

大概过程:

向她说明了问题,并且截图做了详细的错误标识,然后就进入了不停的答复中。下面看图说话。

图1:指明错误位置

问题

图2: 让我在控制台输出一下oLi[i]的值,很明显undefined

这里写图片描述

图3:

她:你可以理解为this属于自己。在循环里或者事件里,this就属于这个事件

我:它也是每次循环才会执行,当i=0时就是图片的那样

这里写图片描述

她:这个样只有第一个li元素才会执行,其他都不执行

我:所以把 0 换成 i 就不可以了?
她:对,你创建一个input,给这个输入框一个点击事件,然后打印this.value,我感觉这个是能让你最快理解的方法。

举了一个简单的例子给我讲,我就试了一下:oBtn.onclick = function(){    //此时两条语句输出内容相同,且没有报错    console.log(oBtn.value);    console.log(this.value);  }//  我:这两个都可以实现打印出value。如果只有一个元素,this和它本身都能用.方法。//  她:这里this指向的就是oBtn。oBtn创建了一个方法。所以this指向的就是这个方法,但是这个方法赋值给了oBtn,所以就指向oBtn。 //  我:为什么元素一多就不能用了? oLi[i]在每一次的循环中也是指每一个,比如说第一次循环时,i=0;oLi[i]就代表oLi[0]//  她:如果不用this,用oLi[i]指的就是全部啊。上面循环的是oLi,只能用this来指向自己。//  我:刚开始i=0;oLi[i]此时指的也不是全部吧。而且oLi[i]一次循环只能代表一个值,怎么表示全部?  //  她:你去百度看看this的指向吧。//  我:好吧。如果只有一个元素,this和它本身都能用.方法。那我去好好学习一下//  她:这个好难讲啊。你好笨啊!//  我:我知道this指的是当前对象。//  她:我觉得我讲的很清楚了。//  我:当前对象就是当前的元素节点,即当前触发点击事件的li元素。//  她:你如果oLi[i]的话不就指向全部了么?oLi[i]  就变成了全部啊。。只有靠[0.1.2.3] 去取值。。//  我:但是,这是有条件的。从 for i=0 开始,每次循环的时候,i只有一个取值。//  她:[]  这个是原生的一个下标 。//  我:oLi[i].onmouseover  它都能找到是oLi[0]//  她:你是先循环的。把所有的li都循环出来了。你又给所有的li加了一个事件。然后你有给所有事件添加了一个背景颜色。//  我:在函数内部就无法识别oLi[i]是谁?//  她:就是说[i] 就是所有。//  她:当然了。你把所有的li都加了事件。而且你移入的时候哪个去变呢? 浏览器无法识别只有报错喽//  我:不是这样走的吗? 循环一次,找到第一个li,注册事件;然后循环第二次,找到第二个li,再注册事件//  她:不是。  只要在循环里 [i]  就是所有//  我:[i]是代表每一个。只不过是因为循环指定了条件  所以它才是一个确定的值。//  她:你去创建5个li在循环着5个li  给个点击事件。在事件里面打印 li[i]  你看看是啥

举例:

这里写图片描述

调试:

这里写图片描述

//  我:undefined//  她:就是li[i]没有被。。//  我:没找到li[i],我刚才试过了//  她:这个只能你自己了。我帮不了你。//  我:我再好好想想

此时,我就去百度搜了一下“”结果还真在网上遇到了同样的问题

,然后就认真看了下面的评论。

问题如下图所示:

问题

答案如下图所示:

这里写图片描述

刚开始看还没看懂,这时第二位帮助我的人出现了。他直接私聊我帮我回答并解释问题,真的是太感谢了。

解决问题的过程

//  他:把oLi[i]换成this//  我:我不知道为什么oLi[i]不能用//  我:不是这样走的吗? 循环一次,找到第一个li,注册事件;然后循环第二次,找到第二个li,再注册事件//      在每次循环中,oLi[i]的值都是确定的。我觉得和this效果等价呀。

他说:

这里写图片描述

//  我:嗯,根据你说的我好好想想。//  他:用jq写很快,$(this)//  我:js从上往下执行的,for循环中的语句也是依次执行,比如说,当走到下图所示的箭头的地方时,按你的意思,for循环已经走完了?

这里写图片描述

/*  我:明白你的意思了,每次循环的时候,每个li元素点击事件已经被添加上了,但是每次循环的时候function里面的内容是不被执行的,对吧?*///  他:你不信吗,你在for循环下面加句console.log(i)//  我:我不是不信,是我不理解//  他:你看看会不会循环完?

我试了一下:

这里写图片描述

//  他:每次循环的时候,每个li元素点击事件已经被添加上了,这句话不对,是你鼠标移入的时候事件才触发。//  他:你看吧,鼠标还没有移入,循环走完了已经。//  我:事件已经被添加了,只是还没被触发。//  我:鼠标移入事件是  在for循环完之后  li元素挨个注册的吗?注册完成以后,然后每个Li元素依次执行事件中的内容//  他:不是,是你鼠标移入时,当前的这个li已经找不到了//  我:添加事件和触发事件 是分开进行的//  我:第一步,for循环走完;第二步,给每一个li元素注册鼠标移入事件;第三步,注册完之后,当鼠标移入的时候,每个li元素才会触发之前注册的事件//  我:你看我这样写对不对?//  他:对

我当时按照我的理解,用画板画了张图发给他:(如下)

这里写图片描述

//  他:for循环为什么要对号入座呢?//  我:for循环之后console.log(i)  得到0;1;2;3//  他:for走完了,现在的最后一个值是3//  我:在for循环内部写,console.log(i),每个i都输出来了//  他:是啊,没毛病,//  我:for走完了,现在的最后一个值是4,因为最后i++//  他:对,是4//  我:for循环不是当i=0时,就进去循环了?转一圈,然后1+1;执行第二次循环//  他:最后一个是4,然后你的操作是,鼠标移入,那么当前的这个oLi[i]存在吗?//  我:执行第一次循环的时候只是给oLi[0]注册了鼠标移入事件,然后就自加+1,去执行第二次循环了//  我:这个时候 i 根本进不去function里面?//  我:因为此时并没有触发事件   【此时,貌似渐渐明白了】//  他:对,事件并没有触发//  我:如果function里面什么也不写,for依然会执行四次。oLi[i]的每一个都会注册鼠标移入事件,只是没有触发的条件。//  他:嗯

我:描述了我的理解1

这里写图片描述

//  我:可以这样理解吗?

我:描述了我的理解2

这里写图片描述

//  他:恩,在移入事件内部,i 它不会被识别出来,从而导致这条代码无法实现功能//  我:最后一次for执行完毕的时候,i=4//  他:你可以这样理解(如下图所示)

这里写图片描述

//  我:这个时候才会去执行function,但是所有的i现在都已经是4了。//  我:绑定事件的时候,i 还是刚开始的 i,0或者1或者2或者3,但是最后要执行function的时候,i 全部变成4了//  他:我调用changeBackground()这个函数,这个函数内部的 oLi[i]并不知道是什么,那么就报错了//  我:相当于绑定的所有事件都只对最后一个oLi[i]生效,但是并没有oLi[4]//  我:oLi[i]只有三个,所以加断点调试的时候,oLi[4]监听后被认为是undefined//  我:我明白了。其实,本质还是变量作用域的问题。 【此时,我终于明白了,不容易啊】//  他:就是作用域的问题//  我:此时用的oLi[i],就等于未定义

我:找的别人推荐的解决方法

这里写图片描述

//  他:函数自执行//  我:怪不得别人说我笨,不适合学编程。我当时没选择java,而选了前端。就是因为我的逻辑思维能力很差,现在才发现原来js同样需要大量的逻辑思维//  他:你喜欢扣问题,寻根究底的。可不是吗?那你html和css呢,这两个也要学好。//  我:这两个还可以,就是js很弱。不理解为什么是这样,下次碰到还是不会用。//  他:见多了就会用了。//  我:但是绑定事件和触发事件不是都在for循环里面写的吗?按道理来说,两个里面都可以识别 i//  我:for循环是一个作用域,绑定事件和触发事件在这个作用域里面//  他:for包裹的没问题。还是上面我给你发的这个代码,你好好看看//  我:这个function能不能写到for循环外面?写到外面this就用不了把。

我:根据上面说的,自己的理解,乱写的。

这里写图片描述

//  我:这样实现应该怎么改写?//  他:不能这样写的,只是让你那样理解的。//  我:只能写在里面吗?或者说只能写在一起对吗?就么有办法写在外面了?//  他:不是哇,这个方法是帮助理解的,里外都不能这样写。//  我:那就只有这一种写法了,连着写。//  他:对//  我:好的,我知道了。

最后调试的时候发现,此时oLi[i]的值确实是4。所以在这里只能用this对象了。

如下图所示:

这里写图片描述

其实,本质还是变量的作用域的问题,这才是核心。

oLi[i].onmouseover = function(){
this.style.backgroundColor = "yellow";};

就等价于:

oLi[i].onmouseover = changeBackground();//很明显,这是两个作用域function changeBackground(){
oLi[i].style.background='#fff';//此时i肯定是undefined}

结束语(知易行难)

今天写了这么多,而且也如实地模拟还原了情景,就是为了能够让大家看清所有的疑惑和问题,能够清晰地看到我艰难行走的每一步,从根源去发现问题,了解问题和解决问题;从没有见过this对象到了解this对象。现在大家应该知道为什么要用this对象了吧。当然这这是一个基础,你可能会说,就这么一个简单的问题,为什么非要长篇大论的说这么多,写这么多,值得吗?不管别人怎么说,我只坚持做好我自己的就好了。

最后,希望这篇文章能够对大家有所帮助。关于如何让它们分开来写,还没有找到解决办法,现在学艺不精,等以后再说吧。其实,现在只是认识this的起跑线,后面的路还很漫长,当然也有很长的一段路要走。毕竟我现在还处于基础阶段,存在很多疑惑和不解,只能说,继续加油吧!有什么问题大家可以在下方评论或者留言,我们可以一起学习交流,共同进步!

你可能感兴趣的文章
Java创建对象的方法
查看>>
Extjs自定义组件
查看>>
TreeGrid 异步加载节点
查看>>
Struts2 标签库讲解
查看>>
Google Web工具包 GWT
查看>>
材料与工程学科相关软件
查看>>
MPI的人怎么用仪器
查看>>
windows 下AdNDP 安装使用
查看>>
Project 2013项目管理教程(1):项目管理概述及预备
查看>>
ssh客户端后台运行
查看>>
哥去求职,才说了一句话考官就让我出去
查看>>
一位超算中心管理人员的空间
查看>>
Weapons of Mass Destruction, Detection
查看>>
环境资源与相关词汇中英文对照
查看>>
fluorenscence aerodynamic particcle sizer
查看>>
HOME - Research Highlights & Publications
查看>>
Leibniz Institute for Tropospheric Research
查看>>
Laser Spectroscopy Group
查看>>
顺风比EMS强多了!
查看>>
搭建CPU+GPU 集群
查看>>