登录

请教《javascript语言精粹》一书中关于闭包的一个例子

下面的文字摘自《javascript:The Good Parts》的closure一节,没看懂,秦歌翻译的那本中文版也看了,还是没明白这段文字说的啥?希望各位SF的朋友能用你自己的理解帮我解释一下:

  1. 为什么第一段代码没有达到目的,不是说内部变量可以读取函数外部变量的吗?读取不到正确的标号,那怎么会弹出节点的数量呢?这个节点数量是怎么获取到的?
  2. 为什么第二段代码可以修正第一段代码的错误,加一个匿名函数它到底起到了什么作用?

============================

It is important to understand that the inner function has access to the actual variables of the outer functions and not copies in order to avoid the following problem:

Code View:

// BAD EXAMPLE

// Make a function that assigns event handler functions to an array of nodes the wrong way.
// When you click on a node, an alert box is supposed to display the ordinal of the node.
// But it always displays the number of nodes instead.

var add_the_handlers = function (nodes) {
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function (e) {
            alert(i);
        }
    }
};

// END BAD EXAMPLE
The add_the_handlers function was intended to give each handler a unique number (i). It fails because the handler functions are bound to the variable i, not the value of the variable i at the time the function was made:

Code View:

// BETTER EXAMPLE

// Make a function that assigns event handler functions to an array of nodes the right way.
// When you click on a node, an alert box will display the ordinal of the node.

var add_the_handlers = function (nodes) {
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function (i) {
            return function (e) {
                alert(i);
            };
        }(i);
    }
};
Now, instead of assigning a function to onclick, we define a function and immediately invoke it, passing in i. That function will return an event handler function that is bound to the value of i that was passed in, not to the i defined in add_the_handlers. That returned function is assigned to onclick.
# JavaScript
PHPz PHPz 2544 天前 789 次浏览

全部回复(6) 我要回复

  • 数据分析师

    数据分析师2017-10-01 01:15:56

    请教《javascript语言精粹》一书中关于闭包的一个例子-PHP中文网问答-请教《javascript语言精粹》一书中关于闭包的一个例子-PHP中文网问答

    围观一下哦,学习一下。

    回复
    0
  • 迷茫

    迷茫2017-04-10 12:43:58

    需要明白 JS 里面的变量范围, 闭包解决的一个基本问题也是变量范围!

    1. 在全局下面直接 var i = 1; 就属于全局变量, 全局变量可以在整个 window 范围内是可见的;
    2. 在闭包里面 var c = 1; 就只有闭包内部可见;
    3. 注意在哪里都行, 如果声明变量的时候不加 var 的话, 就是全局变量了;
    4. 也就是回答你这个问题的答案, 闭包让这些变量的值始终保持在内存中 。 下面是个例子:

    function funcOut(){
    		var n=1;
    		// 注意: 没加 var , 全局可以访问
    		funcAdd=function(){
    			n+=1; 
    		}
    		function funcIn(){
    			alert(n);
    		}
    		return funcIn;
    	}
    	var result=funcOut();
    	result(); // 1
    	funcAdd(); // 始终保持在内存中哦。
    	result(); // 2
    	funcAdd(); // 始终保持在内存中哦。
    	result(); // 3

    顺便说下面这种写法 js 里面是允许的, 至于全局变量为什么不好呢? 在做开发的时候, 应该尽量避免全局变量, 涉及到设计模式, 还有一些最佳实践, 可以 Google 下更多资料:

    undefined = true;

    把下面的两段代码写个 test.html 页面做加深印象.

    看下这个很有名气的循环问题
    function addLinks () {
        for (var i=0, link; i<5; i++) {
            link = document.createElement("a");
            link.innerHTML = "Link " + i + "<br>";
            link.onclick = function () {
                alert(i);       // 都为 5 
            };
            document.body.appendChild(link);
        }
    }
    window.onload = addLinks;
    // 这样写的话就可以解决问题了
    function addLinks () {
        for (var i=0, link; i<5; i++) {
            link = document.createElement("a");
            link.innerHTML = "Link " + i + "<br>";
            link.onclick = function (num) {
                return function () {
                    alert(num);  // num 保存着这一块的值, 在内存里面
                };
            }(i);  // 注意这里的 "();" 是函数直接就执行了, 然后把 i 作为参数进去
            document.body.appendChild(link);
        }
    }
    window.onload = addLinks;

    回复
    0
  • ringa_lee

    ringa_lee2017-04-10 12:43:58

    我的理解是这样:

    代码1
    ---

    nodes[i].onclick = function (e) {
         alert(i);
    }

    这里 alert 接受的变量 i, 在循环结束后已经变成 nodes.length -1, 所以无论你点击的是哪个 node, 执行的都是 alert(nodes.length -1)

    代码2
    ---

    nodes[i].onclick = function (i) {
        return function (e) {
            alert(i);
        };
    }(i);

    通过闭包传递的是当前变量 i+=1, 所以 alert(i) 是对的

    回复
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-10 12:43:58

    先理解 "()" 小括号操作符,见 http://dancewithnet.com/2008/05/07/ja...

    回复
    0
  • 天蓬老师

    天蓬老师2017-04-10 12:43:58

    nodes[i].onclick = function (e) {
                alert(i);
            }

    刚刚也是从这本书上看到这个例子, 不太理解。 搜到了这里。

    我觉得这样理解会不会好点, 将function (e) {...} 看做一个局部对象, 在每次执行的时候, 这个对象被重新定义, 直到最后一次。

    而正确代码里 function (e) {...}(i) 这相当于将事件绑定到 object(i);对象上。 故不会被覆盖。

    不过这个错误真的很容易被忽略, 一不小心就掉到坑里。

    回复
    0
  • 黄舟

    黄舟2017-04-10 12:43:58

    nodes[i].onclick = function (e) {
        alert(i);
    }

    这里的点击事件触发时执行的操作是弹出变量i,而点击时整个循环操作已经完成,变量i的值已经是nodes.length,

    nodes[i].onclick = function (i) {
        return function (e) {
            alert(i);
        };
    }(i);

    这里的点击事件绑定时创建了一个闭包,它保存了绑定时变量i的值在内存中,当触发点击时弹出的是之前保存的i。

    回复
    0
  • 取消 回复 发送