• 理解js中的作用域,作用域链以及闭包


    作用域
    变量作用域的类型:全局变量和局部变量
    全局作用域
    对于最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的

    <script>
    var outerVar = "outer";
    function fn(){
    console.log(outerVar);
    }
    fn();//result:outer
    </script>

    局部作用域
    和全局用域相反,局部作用域一般只在固定的代码片段内可访问到,对于函数外部是无法访问的

    <script>
    function fn(){
    var innerVar = "inner";
    }
    fn();
    console.log(innerVar);// ReferenceError: innerVar is not defined
    </script>


    注意
    需要注意的是,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

    <script>
    function fn(){
    innerVar = "inner";
    }
    fn();
    console.log(innerVar);// result:inner
    </script>

    作用域链
    我的理解就是,根据在内部函数可以访问外部函数变量的这种机制,用链式查找哪些数据能被内部函数访问
    执行环境
    每个函数在运行时都会产生一个执行环境。js为每个执行环境关联了一个变量对象。环境中定义的所有变量和函数都保存在这个对象中
    作用域链理解

    <script>
    var scope = "global"; 
    function fn1(){
    return scope; 
    }
    function fn2(){
    return scope;
    }
    fn1();
    fn2();
    </script>

    当某个函数第一次被调用时,就会创建一个执行环境(execution context)以及相应的作用域链,并把作用域链赋值给一个特殊的内部属性([scope])。然后使用this,arguments(arguments在全局环境中不存在)和其他命名参数的值来初始化函数的活动对象(activation object)。当前执行环境的变量对象始终在作用域链的第0位。
    以上面的代码为例,当第一次调用fn1()时的作用域链如下图所示:

     可以看到fn1活动对象里并没有scope变量,于是沿着作用域链(scope chain)向后寻找,结果在全局变量对象里找到了scope,所以就返回全局变量对象里的scope值。

    闭包

    闭包有两个作用: 
    第一个就是可以读取自身函数外部的变量(沿着作用域链寻找) 
    第二个就是让这些外部变量始终保存在内存中 

    关于第二点,来看一下以下的代码:

    <script>
          function outer(){
             var result = new Array();
             for(var i = 0; i < 2; i++){//注:i是outer()的局部变量
                result[i] = function(){
                   return i;
                }
             }
             return result;//返回一个函数对象数组
             //这个时候会初始化result.length个关于内部函数的作用域链
          }
          var fn = outer();
          console.log(fn[0]());//result:2
          console.log(fn[1]());//result:2
       </script>

     返回结果很出乎意料吧,你肯定以为依次返回0,1,但事实并非如此 
    来看一下调用fn[0]()的作用域链图: 

    可以看到result[0]函数的活动对象里并没有定义i这个变量,于是沿着作用域链去找i变量,结果在父函数outer的活动对象里找到变量i(值为2),而这个变量i是父函数执行结束后将最终值保存在内存里的结果。 
    由此也可以得出,js函数内的变量值不是在编译的时候就确定的,而是等在运行时期再去寻找的。

    那怎么才能让result数组函数返回我们所期望的值呢? 

    <script>
          function outer(){
             var result = new Array();
             for(var i = 0; i < 2; i++){
                //定义一个带参函数
                function arg(num){
                   function innerarg(){
                      return num;
                   }
                   return innerarg;
                }
                //把i当成参数传进去
                result[i] = arg(i);
             }
             return result;
          }
          var fn = outer();
          console.log(fn[0]());
          console.log(fn[1]());
       </script>

    由上图可知,当调用innerarg()时,它会沿作用域链找到父函数arg()活动对象里的arguments参数num=0. 
    上面代码中,函数arg在outer函数内预先被调用执行了,对于这种方法,js有一种简洁的写法

    function outer(){
             var result = new Array();
             for(var i = 0; i < 2; i++){
                //定义一个带参函数
                result[i] = function(num){
                   function innerarg(){
                      return num;
                   }
                   return innerarg;
                }(i);//预先执行函数写法
                //把i当成参数传进去
             }
             return result;
          }

    使用闭包的注意点

    1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

    2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。


  • 相关阅读:
    C#编程利器之二:结构与枚举(Structure and enumeration)
    解读设计模式模板方法模式(Template Method),电脑就是这样造出来的
    清空mysql一个库中的所有表
    在执行并行程序工程中,突然弹出 connection closed 窗口,随后 ssh 与服务器的连接断开,并行程序也中断
    菜鸟求救 myeclipse安装flex3插件的问题
    linux 下 将 shell script 与 一个桌面图标联系在一起 (2)
    MYSQL EXPLAIN语句的extended 选项学习体会
    MySQL 性能跟踪语句
    Flex Flash
    Flex Builder 3 正式版
  • 原文地址:https://www.cnblogs.com/15fj/p/8416687.html
Copyright © 2020-2023  润新知