理解Closure--记一次JS代码Debug过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaoyaohuqijun/article/details/79506250

刚入门前端开发, 写一个页面时遇到一次Closure 导致的问题, 花了一些功夫才解决, 这里记录下。

问题描述:

JS 代码根据后台返回的数据列表来渲染form 呈现, 每一条Item 对应一个form , form 里面有link , 绑定了click 事件, 事件响应函数要接收Item 里面的数据:

function displayDevice(data) {
    devicenum = data.length;
    var devices = $('#edit_devices');


    for (i = 0; i < devicenum; i++) {
        var id = i + 1;
        var devid = "device" + id;
        var deviceinfo = data[i];

        devices.append('<div id ="' + devid + '">' +
            '<form>' +
            '<label for="orig_deviceid" class = "control-label col-md-2" >DeviceSN</label>' +
            '<input type="text" id="orig_deviceid"  class = "form-control" value="' + deviceinfo.DeviceSN + '"  readonly >' +

            '<label for="orig_devicetype" class = "control-label col-md-2" >DeviceType</label>' +
            '<select class = "form-control" id="orig_devicetype" > ' +
            '<option value="ASIA">ASIA</option>' +
            '<option value="ABIK">ABIK</option>' +
            '<option value="ASIK">ASIK</option>' +
            '<option value="ABIL">ABIL</option>' +
            '<option value="Airframe">Airframe</option>' +
            '<option value="CloudCU">CloudCU</option>' +
            '</select>' +

            '<label for="orig_location" class = "control-label col-md-2" >DeviceLocation</label>' +
            '<input type="text" id="orig_location" class = "form-control" value = "' + deviceinfo.Location + '" >' +

            '<label for="orig_devicehwversion" class = "control-label col-md-2" >HWVersion</label>' +
            '<input type="text" id="orig_devicehwversion" class = "form-control" value= "' + deviceinfo.HWVersion + '" >' +

            '<label for="orig_deviceip" class = "control-label col-md-2" >IP</label>' +
            '<input type="text" id="orig_deviceip" class = "form-control" value = "' + deviceinfo.IP + '" > ' +

            '<label for="orig_deviceremark" class = "control-label col-md-2" >Remark</label>' +
            '<textarea id="orig_deviceremark" rows="5" class = "form-control" maxlength="150" >' + deviceinfo.Remark + '</textarea > ' +

            '<a href="javascript:void(0);"  class="col-lg-offset-1 col-lg-2 " id="' + devid +'"  >Delete this device</a>' +

            '</form>' +

            '<hr />' +

            '<div/>');

        var select = $('#' + devid + ' #orig_devicetype').val(deviceinfo.DeviceType);

            //pay attention to Closure here
            $('a#' + devid).click(  function ()    
                rmvDevice(devid, deviceinfo.DeviceSN);
        );    

    }

}

注意最后注册click 事件的代码, 里面使用了和具体的条目相关的变量, 在循环中会改变的。

然后在调试时就出现比较诡异的现象, 如果有5个item , 则5个form 里面的 < a > 元素触发click动作时, 调用revDevice 函数时传入的数据永远是最后一个item 对应的数据。

这里不得不感叹Chrome 调试工具很强大, 在审查element 可以看到对应元素的event listener , 在里面可以看到5个 < a > 元素的click 事件处理函数的Closure 都是一样的, 可以直接定位到是closure 相关的问题。
Closure 相关的内容,材料网上有很多, 这里就不涉及了。

closure

JS 为html 元素注册事件时, 函数作为一个参数在传递, 而不会立即执行, 对应事件才会触发函数执行。JS 中函数作为参数或者返回值时, 是作为Closure的, 会保存相关的参数和变量在Closure 中。

传递函数(Closure) 时千万要注意不要引用循环变量,或者后续会发生变化的变量。如果一定要引用会变化的量, 则应该再创建一个函数, 用该函数的参数绑定变量当前的值,确保传递到函数参数的值不变。

最后代码为再创建一个函数, 用该函数参数绑定变量当前的值, 问题解决:

//pay attention to Closure here
$('a#' + devid).click((function (id, sn) { 
    return function () {
        rmvDevice(id, sn);
    }
})(devid, deviceinfo.DeviceSN)
);    

一次内存溢出debug

12-10

源码如下rn[code=c]rnvoid GETFILEDLG::OnClickedButton1()rnrn // TODO: 在此添加控件通知处理程序代码rn CString str;rn BROWSEINFO bi;rn rn ZeroMemory(&bi, sizeof(BROWSEINFO));rnrn bi.hwndOwner = GetSafeHwnd();rn bi.lpszTitle = "将文件下载到";rnrn LPITEMIDLIST idl;rnrn idl = SHBrowseForFolder(&bi);rnrn if (idl == NULL)rn rn return;rn rn //ASSERTE( _CrtCheckMemory( ) );rn //char temp[MAX_PATH+1];rn SHGetPathFromIDList(idl, str.LockBuffer()); //溢出rn //SHGetPathFromIDList(idl, temp);rn //str = temp;rn GetDlgItem(IDC_EDIT_DIR)->SetWindowText(str.LockBuffer());rnrnrn CRemoteContrlDlg::SetPubDir(str);//bug:heap corruption detected rnrn[/code]rnrn主要功能为通过点击button->弹出浏览对话框,选择->获得选择项->转换为路径rn报错如下:rn[img=http://img.bbs.csdn.net/upload/201512/10/1449752241_665103.png][/img]rnrn经查询为内存溢出错误,使用断言ASSERTE( _CrtCheckMemory( ) );rn确定错误发生在SHGetPathFromIDList(idl, str.LockBuffer()); rnMSDN中对第二个参数的描述为rnPointer to a buffer to receive the file system path. This buffer must be at least MAX_PATH characters in sizern改正见源码注释rnrn说明:这段代码时间比较久了,其在vc6.0中并没有崩溃rnrn现有问题:rn1关于Cstring::LockBuffer我并没有在MSDN中找到说明,在CstringT中也一样rn2我猜测Cstring::LockBuffer返回实际值:str.lockbuffer()返回一个字符串指针,但是大小为0,所以赋值的时候会溢出,我的猜测对吗?rnrnrn新手一枚,欢迎讨论[img=http://forum.csdn.net/PointForum/ui/scripts/csdn/Plugin/003/monkey/27.gif][/img]rn 论坛

没有更多推荐了,返回首页