闭包

我在写信息管理系统的前端页面,决定使用 jquery 来实现前后端分离,具体的方法是在前端 script 里面使用 ajax 来异步带着数据访问后端,然后根据后端返回的 json 来渲染前端的页面,至于我为什么会遇到闭包这个东西呢?听我慢慢道来 ...

当时写的代码是传给后端 token,然后后端根据 token 对应的用户的权限返回要加载的选项卡,后端功能很好实现,直接这么写就行了

token, isOk := c.GetPostForm("token")
if !isOk {
    c.JSON(200, map[string]interface{}{
        "msg": "fail",
    })
    return
}
if !service.CheckToken(&token) {
    c.JSON(200, map[string]interface{}{
        "msg": "fail",
    })
    return
}
if service.CheckRoot(&token) {
    c.JSON(200, map[string]interface{}{
        "msg": "ok",
        // TODO
    })
    return
}
infoBacks := []pojo.InfoBack{{
    ButtonId: "info",
    ObjUrl: "/info",
    ButtonName: "个人信息",
}, {
    ButtonId: "room",
    ObjUrl: "/room",
    ButtonName: "寝室",
}, {
    ButtonId: "clean",
    ObjUrl: "/clean",
    ButtonName: "卫生检查",
}, {
    ButtonId: "break",
    ObjUrl: "/break",
    ButtonName: "报修",
}, {
    ButtonId: "lost",
    ObjUrl: "/lost",
    ButtonName: "失物招领",
},
                            }
c.JSON(200, map[string]interface{}{
    "msg": "ok",
    "infos": infoBacks,
})

实现接口之后我就把视线转到前端了,先 F12 里的 console 里测试了下,可以接收到 json 数据,然后就开始写 js 了,大致思路的根据传回的数据,遍历所有元素然后创建 li 标签加按钮,最后把创建出来的按钮绑定上 click 事件的函数

初版代码是这样的

<script charset="utf-8">
    $(function() {
        $("#quit").click(function() {
            var token = localStorage.getItem("token");
            localStorage.removeItem("token");
            var tks={"token":token}
            $.post(
                "/quit",
                tks,
                function() {
                    location.assign("/login");
                }
            );
        });
        var token = localStorage.getItem("token");
        $.post(
            "/index",
            {"token": token},
            function(data) {
                var indexs = data
                for(var i = 0; i < data.infos.length; i++) {
                    $("#menu").append("<li class=\"nav-item\"><button class=\"my-2 btn btn-secondary btn-block w-100\" type=\"button\" id=\"" + data.infos[i].button_id + "\">" + data.infos[i].button_name + "</button></li>");
                    $("#" + data.infos[i].button_id).click(function() {
                        $.get(
                            indexs.infos[i].obj_url,
                            function() {
                                console.log("6")
                            }
                        )
                    });
                }
                // TODO
            }
        );
    });
</script>

既然是初版代码,那必然是有问题的,运行结果是标签渲染出来了,但是按钮按下去无事发生,console 里没显示 6,后端的日志也没有显示 404 的 get 请求(那些端口我都没写好

可喜可贺的是我在 console 里仔细看了下发现了在按下按钮之后他报错了,说indexs.infos[i] is undefined ,然后我大概知道了是怎么一回事,绑定函数的过程不包括执行,所以再次运行的时候它是独立出来运行的,这时候 i 就不见了,所以就报错了

那么怎么解决这个问题呢?凭我的经验肯定不能解决这种问题,借助了强大的搜索引擎,我找到了和我遇到类似问题的人 🔗

下面是能够 work 的代码

<script>
function(data) {
    var indexs = data;
    for(var i = 0; i < data.infos.length; i++) {
        $("#menu").append("<li class=\"nav-item\"><button class=\"my-2 btn btn-secondary btn-block w-100\" type=\"button\" id=\"" + data.infos[i].button_id + "\">" + data.infos[i].button_name + "</button></li>");
        (function(i) {
        $("#" + data.infos[i].button_id).click(function() {
            $.get(
                indexs.infos[i].obj_url,
                function() {
                    console.log("6")
                }
            )
        });})(i)
    }
}
</script>

一个人同时写前后端真的挺累的,不过成就感倒是有的,web 也就这回事