zoukankan      html  css  js  c++  java
  • Javascript——闭包、作用域链

    1、闭包:是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式:在一个函数内部创建另一个函数

     1 function f(name){
     2 
     3   return function(object){
     4 
     5     var value = object[name];
     6 
     7     ...
     8 
     9   }
    10 
    11 }

    加粗代码是内部函数(一个匿名函数)中的代码,代码中访问了外部函数中的变量name。

    在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域中。

    闭包会携带包含它的函数的作用域,所以会比其他函数占用更多的内存。

     

    2、作用域链

    1 function compare(v1,v2){
    2 if(v1<v2) return -1;
    3 else if(v1>v2) return 1;
    4 else return 0;        
    5 }
    6 
    7 var result = compare(1,2);

    以上代码先定义了compare()函数,然后又在全局作用域中调用它。后台的每个执行环境都有一个表示变量的对象——变量对象。全局环境的变量对象始终存在,但是像compare()这样的局部环境中的变量对象,则只有再函数执行的过程中存在。

    创建compare()函数时,会先创建一个预先包含全局变量对象的作用域链,这个作用域链被保存再内部的[[Scope]]属性中。

    当调用compare()函数的时候,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。

    compare()函数的执行环境而言,其作用域包含连个变量对象:本地活动对象(arguments,v1,v2)和全局变量对象(compare,result)。作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。

    无论什么时候在函数中访问一个变量的时候,就会从作用域链中搜索相应名字的变量。当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域。

    3、闭包与变量

    1 function c(){
    2     var result = new Array();
    3     for(var i=0;i<10;i++){
    4          result[i] = function(){
    5               return i;
    6          };
    7     } 
    8     return result;  
    9 }

    每个函数都返回10.

    
    
    1 1 function c(){
    2 2     var result = new Array();
    3 3     for(var i=0;i<10;i++){
    4 4          result[i] = function(num){
    5 5               return function(){ return num; };
    6 6          }(i);
    7 7     } 
    8 8     return result;  
    9 9 }
    
    

    这个函数就可以返回各自不同的值.

     

    4、关于this对象

    在全局函数中,this等于window,当函数被当作某个对象的方法调用时,this等于那个对象。

     

    5、模仿块级作用域

    js中没有块级作用域的概念。匿名函数可以用来模仿块级作用域。

    1 (function (){
    2      //这里是块级作用域
    3 })();

    以上代码定义并立即调用了一个匿名函数。将函数声明包含在一对圆括号里面,表示它实际是一个函数表达式。而紧随其后的另一对圆括号会立即调用这个函数。在匿名函数中定义的任何变量都会在结束时被销毁。

    1 function s(count){
    2 (function(){
    3 for(var i=0;i<count;i++) alert(i);
    4 })();
    5 alert(i);
    6 }

    变量i只能在循环中使用,使用后即被销毁。这种做法可以减少闭包占用的内存问题。因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链。

    6、静态私有变量——实现特权方法

    通过在私有作用于定义私有变量或函数,同样也可以创建特方法。

     1 (function(){
     2      //私有变量和私有函数
     3      var v = 10;
     4      function f(){
     5          return false;
     6      }
     7      //构造函数
     8      MyObject = function(){};
     9      //特权方法
    10      MyObject.prototype.method = function(){
    11          v++;
    12          retrun f();
    13      }
    14 })();

    ***:初始化未经声明的变量,总是会创建一个全局变量。
    因此MyObject就是一个全局变量,能够在私有作用域之外被访问到。在这个模式中,私有变量和函数是由实例共享的,由于特权方法是在原型上定义的,因此所有的实例都是用同一个函数。

    7、模块模式——为单例创建私有变量和特权方法

    所谓单例,指的就是只有一个实例的对象。

    var singleton = {
    name:value,
    method:function(){...}
    }

    模块模式:

    var singleton = function(){
        var v= 10function f(){ return false; }
        return {
            publicProperty:true,
            publicMethod:function(){
                v++;
                return f();
            }
        };
    }();

    8、小结

    函数表达式的特点:

    • 函数表达式不同于函数声明。函数声明要求有名字,函数表达式不需要。没有名字的函数表达式也叫做匿名函数。
    • 在无法确定如何引用函数的情况下,递归函数会变得比较复杂。
    • 递归函数应该始终使用arguments.callee来递归的调用自身,不要使用函数名。

    当在函数内部定义了其他函数,就创建了闭包,闭包有权访问包含函数内部的所有变量。使用闭包可以在javascript中模仿块级作用域,要点如下:

    • 创建并立即调用一个函数,这样既可以执行其中的代码,又不会在内存中留下对该函数的引用。
    • 结果就是函数内部的所有变量都会被销毁——除非某些变量赋值给了包含作用域中的变量。

    闭包还可以用于再对象中创建私有变量,要点如下:

    • 即使javascript中没有正式的私有对象属性的概念,但可以使用闭包来实现公有方法,而通过公有方法可以访问在包含作用域中定义的变量。
    • 有权访问私有变量的公有方法——特权方法。
    • 可以使用构造函数模式、原型模式来实现自定义类型的特权方法,也可以使用模块模式。

     

     

     

     

     

     

     

     

     

     

     



  • 相关阅读:
    python __init__.py
    估算小结
    ssh vim中不小心按下ctrl+s
    估算方法
    SSH 下使vim语法高亮显示 && 终端下vim配置
    not enough arguments for format string搞死人
    函数的形参 实参
    Linux 共享内存机制
    select函数参数及其使用
    找出两个字符串最长公共子串
  • 原文地址:https://www.cnblogs.com/shuqicui/p/day161201.html
Copyright © 2011-2022 走看看