Javascript 中变量的作用范围引发的bug

调试一个别人的 js 程序,代码不长,不到 100 行。功能是轮显图片,4张缩略图,一个放大的图片,程序自动在小图片中循环显示,鼠标移动到缩略图上,显示相应的大图。

页面中还有其他的轮显,鼠标经过标题,改变内容的区域。

Bug 现象:鼠标经过其他轮显的区域,或者页面加载过一段时间之后,就会提示 js 有错误。

打开调试,错误内容:“SCRIPT5007: 无法设置未定义或 null 引用的属性“className”  foucs.js, 行13 字符9”

相应的 js 代码如下:

var foucsbox = function (time) {
    var time = time || 3500
    , $ = function (id) { return document.getElementById(id); }
    , topCon = $('divimgplay')
    , big = $('divimginfog_imgPlayer')
    , samll = $('divpageinfog_imgPlayer')
    , tip = $('ptitleg_imgPlayer')
    , bigimgs = big.getElementsByTagName('li')
    , samllimgs = samll.getElementsByTagName('li')
    , imglink = tip.getElementsByTagName('p')[0]
    , slide = function (z) {
        samllimgs[lastIndex].className = '';
        samllimgs[z].className = 'current';
        bigimgs[lastIndex].style.display = 'none';
        bigimgs[z].style.display = 'block';
        try {
            imglink.innerHTML = samllimgs[z].getElementsByTagName('img')[0].alt;
        }
        catch (e) {
            imglink.innerText = samllimgs[z].firstChild.firstChild.alt;
        }
        lastIndex = i = z;
    }
    , helper = function (z) {
        return function (e) {
            var na;
            if (!e) {
                e = window.event;
                na = e.srcElement.nodeName;
            }
            else {
                na = e.target.nodeName;
            }
            if (na === 'IMG') {
                slide(z);
            }
        }
    }
    , lastIndex = i = 0, x, y = bigimgs.length
    , getPrevI = function (q) { return i - q < 0 ? y - q : i - 1; }
    , getNextI = function (q) { return i + q >= y ? i + q - y : i + 1; }
    var s = setInterval(function () {
        slide(i);
        i = getNextI(1);
    }, time);
    try {
        imglink.innerText = samllimgs[0].getElementsByTagName('img')[0].alt;
    }
    catch (e) {
        imglink.innerText = samllimgs[0].firstChild.firstChild.alt;
    }
    for (x = 1; x < y; x += 1) {
        bigimgs[x].style.display = 'none';
    }
    for (x = 0; x < y; x += 1) {
        samllimgs[x].onmouseover = helper(x);
    }
    topCon.children[2].onclick = function (e) {
        i = lastIndex;
        var t;
        if (!e) {
            e = window.event;
            t = e.srcElement;
        } else {
            t = e.target;
        }
        switch (t.className) {
            case 'icon_prev':
                slide(getPrevI(1));
                break;
            case 'icon_next':
                slide(getNextI(1));
                break;
        }
    };
    topCon.onmouseover = function () {
        clearInterval(s);
    };
    topCon.onmouseout = function () {
        s = setInterval(function () {
            slide(i);
            i = getNextI(1);
        }, time);
    };
};

问题出现在引用了超界的数组成员的一个属性。

跟踪代码,发现该数组应拥有的元素应该是4个,但是超界时给出的下标却是7或者9。

找到获取上一个、下一个下标的代码:

getNextI = function (q) { return i + q >= y ? i + q - y: i + 1; }
对越界也有判断,而且跟踪了很多次,返回值一直在 0 到 3,其他的地方也没有改变,可是在页面里面运行一段时间,又会出现那个问题。

结合出现 bug 的现象,鼠标移过其他的轮显区域就会报这个错误,开始怀疑是不是有的变量作用域不对,被函数外面的变量值给干扰。

而且这个变量很有可能出现在当做下标的循环变量中。

逐步排查,发现在神明变量时,有这样一句:

lastIndex = i = 0, x, y = bigimgs.length
lastIndex,x,y 都没有问题,但是这里的 i 并不是申明变量,而是直接引用了。

js 引擎会在当前的作用域内找 i 这个变量的申明,如果没有找到,会默认在上一级的作用域内搜寻,直到搜寻全局变量,如果全局变量里面也没有 i ,那么就会将这个 i 作为全局变量来处理。

如果恰巧在其他的代码中也引用了这个全局变量 i ,对 i 的值做了操作,那么灾难就来了,就会出现上面的问题。

将上面申明变量的语句改为

lastIndex = 0,
i = 0,
x = 0,
y = bigimgs.length,

将 i 也定义为函数内部的变量,不受外部程序的影响。就此不再报错,bug 消除。

总结:

可见变量的作用域是何等的重要,特别是类似于 i, j 之类的,会经常用到的循环变量,很容易搞串,而循环变量在程序逻辑中又是非常重要的,所以在程序开始时,一定要做好变量的申明。

在找这个 bug 时,一开始也走了不少弯路,因为总是在鼠标经过其他的轮显块的时候出现这个 bug ,所以以为造成 bug 的原因是其他的轮显块造成的,再加上没有代码注解,分析代码的逻辑都用了很长的时间,实际上这个 bug 不用太了解代码的逻辑,只要能够准确定位 bug 出现的位置,就能够找到 bug ,并消除。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值