zoukankan      html  css  js  c++  java
  • 【CSON原创】 和大家一起分析js闭包中一个不常见的陷阱以及一个不常见的技巧

    一个不常见的闭包陷阱:

    var test1 = { name: 'Cson' };
    var name = 'noName';
    with (test1) {
    function setName() {
    name
    = 'xiaoc';

    }
    setName();

    }

    alert(test1.name);
    alert(name);

    我们常常在资料中看到类似这样的描述:with打开对象闭包,在对象闭包里设置属性和方法等于为该对象添加属性以及方法。按这种思路理解,上面的结果应该分别是:'xiaoc'以及'noName'.

    但是实验结果是:'Cson'以及'xiaoc'。(IE chrome)'xiaoc'以及'noName' (firefox)。

    分析:

    其实“在with对象闭包里设置属性和方法等于为该对象添加属性以及方法”这种说法并不完美,例外的情况就是当对象闭包里嵌套函数闭包的时候,这时除了firefox还是认同上面那句话之外,ie和chrome会用另一种方法处理。

    我们知道javascript的有两个运行周期:解析周期以及执行周期。当处理上面过程的时候,firefox和IE chrome采用了不同的处理方式:

    firefox认为:对象闭包内的声明,作用与对象闭包本身(也就是当作test1的属性处理),因此在执行周期的时候才把setName作为test1的一个方法属性创建,所以里面处理的name也就是test1的name属性了。

    但是,ie和chrome认为:对象闭包内的声明,不应该作用与对象闭包。在js的解析周期,由于with还没起作用,因此它们把setName声明解析在全局闭包域里,这时就使setName和with成为了"同级",因此setName里设置的name就成为了全局的name变量。

    解决方法:

    综上所述,我们只需要把对象闭包内的函数声明改成赋值,就可以避免ie和chrome在js解析周期把setName解析在全局闭包里。

    function setName() {
    name
    = 'xiaoc';

    }

    修改为

    var setName=function() {
    name
    = 'xiaoc';

    }

    这样一来,“声明”变成“赋值”,所有浏览器引擎都会在js的执行周期处理该函数。

    修改后firefox ie chrome输出均为:'xiaoc'以及'noName'

    一个不常见的关于闭包的技巧:

    大家应该对这个闭包的经典问题不陌生了:

    <div id="a0">a0</div>
    <div id="a1">a1</div>
    <div id="a2">a2</div>
    <div>
    function test2() {
    for (var i = 0; i < 3; i++) {
    document.getElementById(
    'a'+i).onclick= function() { alert(i); }
    }
    }
    test2();

    结果会是a0 a1 a2三个元素点击后alert的值均为3,因为闭包使它们使用了同一个i的值。

    经典的解决方法是: 

    function test2() {
    for (var i = 0; i < 3; i++) {
    (
    function() {
    var j = i;
    document.getElementById(
    'a' + i).onclick = function() { alert(j); }
    })();
    }
    }
    test2();

    但是这样带来了个问题,只有外部的dom元素不解除引用,闭包使整个作用域链上的变量对象被保存在内存,代来了性能上的损耗,有没有更好的方法,不使用闭包解决该问题呢?

    

    一个更好的方法如下: 

    function test2() {
    for (var i = 0; i < 3; i++) {

    (document.getElementById(
    'a' + i).onclick = function() { alert(arguments.callee.j); }).j = i;

    }
    }
    test2();

    很巧妙地通过为function设置自定义属性的方法,不用闭包,就完美解决了问题。

    

    参考书籍:《javascript高级程序设计》《javascript语言精髓与编程实践》

    欢迎转载,请标明出处:http://www.cnblogs.com/Cson/archive/2011/03/31/2001631.html

  • 相关阅读:
    mybatis专题(一)-----入门、配置
    如何在抽象类中注入bean
    spring是如何解决循环依赖的?
    Java 程序员必须了解的计算机底层知识!
    数据库底层到底是怎么设计的?
    关于Rigidbody,Collider和CharacterController三者之间的关系和用法的总结
    泛型c#(深入理解c#)
    协程的NullReferenceException 错误
    进度条的制作unity
    Rigidbody.Is Kinematic和碰撞体
  • 原文地址:https://www.cnblogs.com/Cson/p/2001631.html
Copyright © 2011-2022 走看看