为什么此js函数里只能用this才能实现?

function highlightRows() {
    if(!document.getElementsByTagName) return false;
    var tables = document.getElementsByTagName("table");
    for(var i=0;i<tables.length;i++){
        var rows = tables[i].getElementsByTagName("tr");
        for(var j =0;j<rows.length;j++){
            rows[j].onmouseover=function(){
                //rows[j].style.fontWeight="bold"; //这样无效
                this.style.fontWeight="bold";
            }
            rows[j].onmouseout=function() {
                //rows[j].style.fontWeight="normal"; //这样无效
                this.style.fontWeight="normal";
            }
        }
    }
}

鼠标悬浮的高亮效果

为什么注释的那两行不行,非要用this呢,这里this不是应该就是指代rows[j]么?

阅读 6.4k
8 个回答

变量作用域问题

假设
rows.length = 9
最后一次for执行完毕的时候
j = 9

function() {
  rows[j].style.fontWeight="normal";
}

所有的匿名函数里面 j = 9,
绑定事件的时候j还不是9,执行的时候j已经全部是都是9了

也就是你绑定的所有事件都只对最后一个生效。
解决方法是用闭包

(function(j){
  rows[j].onmouseout=function() {
    rows[j].style.fontWeight="normal";
  }
})(j);

因为j不是你想象的j

你的事件绑定的function,可以拿到rows[j],但是等到这个function执行的时候,for循环早结束了,你的j拿到的都是循环后的值。。。所以设置的都是最后一项吧

因为函数定义时所处的环境和函数运行时所处的环境(里面的东西),往往是不同的。
尤其是像题目中那样,存在异步的情况下。

简单地说,你那个回调函数被调用的时候,可能是1个小时以后了,那时候的j早就与注册时的j不同了。

代码

for(var j =0;j<rows.length;j++){
            rows[j].onmouseover=function(){
                this.style.fontWeight="bold";
            }
            rows[j].onmouseout=function() {
                this.style.fontWeight="normal";
            }
        }

执行时,给当前循环内rows[j]也就是tr元素绑定事件,绑定后mouseover触发即会执行匿名函数。
假设你table有3行,当该段代码执行结束后,j已经变成3,此时不能够拿到正确的tr了呐。

 //rows[j].style.fontWeight="bold"; //这样无效

这个rows[j]还真不是this呢。

for(var j =0;j<rows.length;j++){

      var rows[j].index=j;
        rows[j].onmouseover=function(){
            rows[this.index].style.fontWeight="bold";
        }
        rows[j].onmouseout=function() {
           rows[this.index].style.fontWeight="normal";
        }
    }

骚年,你还没弄明白JavaScript函数的创建上下文执行上下文的区别。

不需要看代码,你直接搜索一下两个概念就可以了,顺便你也就弄明白闭包、执行环境对象、作用域链的问题了。

我这个回答虽然简单,但我觉得切中要害。

新手上路,请多包涵
// This doesn't work like you might think, because the value of `i` never
// gets locked in. Instead, every link, when clicked (well after the loop
// has finished executing), alerts the total number of elements, because
// that's what the value of `i` actually is at that point.
//以下的代码运行起来将不会像你想象中的那样,因为其中的‘i’值(在每一次循环中)没有被锁定。
//取而代之当你点击每一个连接的时候(就是其中的循环已经结束了),浏览器总会去提醒’总元素个数‘
//因为那就是’i‘的在这个时候的确切值。
  
var elems = document.getElementsByTagName( 'a' );
    
for ( var i = 0; i < elems.length; i++ ) {
    
   elems[ i ].addEventListener( 'click', function(e){
      e.preventDefault();
      alert( 'I am link #' + i );
   }, 'false' );
    
}
    
// This works, because inside the IIFE, the value of `i` is locked in as
// `lockedInIndex`. After the loop has finished executing, even though the
// value of `i` is the total number of elements, inside the IIFE the value
// of `lockedInIndex` is whatever the value passed into it (`i`) was when
// the function expression was invoked, so when a link is clicked, the
// correct value is alerted.
//以下的代码可以工作,因为有自调用函数(IIFE)的缘故,
//’i‘的值被锁定成为函数的传入参数’lockedInIndex‘,当循环运行完毕的时候,
//尽管’i‘的值还是总元素的个数,但是在IIFE中,
//’lockedInIndex‘的值永远是该函数表达式被调用的时候所传入的’i‘值。
//所以当一个连接被点击的时候,可以提示出正常的值。
    
var elems = document.getElementsByTagName( 'a' );
   
for ( var i = 0; i < elems.length; i++ ) {
    
  (function( lockedInIndex ){
    
    elems[ i ].addEventListener( 'click', function(e){
      e.preventDefault();
      alert( 'I am link #' + lockedInIndex );
    }, 'false' );
  
  })( i );
    
}

  
  

以上内容全文引用自Ben Alman Immediately-Invoked Function Expression 作者为Ben Alman

翻译是我自己写的,我也在学这些内容,碰巧看到了就引用一下,恩,应该不会被打。
特别鸣谢:Special Thanks: Ben Alman

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题