本文共 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:
错图2:
发现问题时,我首先想到的是“背景色的属性名是不是写错了?”,因为在写的时候我就不太确定,也没有去查一下,就凭借记忆中的印象直接写上去了。而且我记得背景颜色好像是没有缩写的,但是还不确定,毕竟平时js用的太少,没感觉。先不管了,就这样写了:backgroundColor
。后来百度查了一下还真的没记错。
background-color
,自称“中划线写法“或“分割线写法”;backgroundColor
;fontSize
等。但是百度的过程中,发现也有形如这样的错误写法,如:bgColor
和 bgcolor
。为了弄清楚到底有没有这两个写法,我又去查了一下。
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
的起跑线,后面的路还很漫长,当然也有很长的一段路要走。毕竟我现在还处于基础阶段,存在很多疑惑和不解,只能说,继续加油吧!有什么问题大家可以在下方评论或者留言,我们可以一起学习交流,共同进步!