zoukankan      html  css  js  c++  java
  • JavaScript 函数之 ------------------ 闭包

          谈到闭包,人们常常会把匿名函数和闭包混淆在一起。闭包是指由权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数,仍以前面的

    createComparisonFunction()函数为例

      

     1           function createComparisonFunction(propertyName){
     2                     return function(object1,object2){
     3                         var value1 = object1[propertyName];
     4                         var value2 = object2[propertyName];   
     5                         if(value1<value2){
     6                             return -1;
     7                         }else if(value1>value2){
     8                             return 1;
     9                         }else{
    10                             return 0;
    11                         }
    12                     }
    13                 };

      在标识的部分,它访问了外部的变量 propertyName 即使这个函数被返回后,仍然可以访问到这个变量(propertyName);这是应为 匿名函数作用域链中包含  createComparisonFunction() 的作用域。

        要理解必须掌握作用域的几个概念

      作用域链:

        什么时候会生成?:当代码在一个执行环境中执行时,就会创建变量对象的一个作用域链。

        作用:保证对执行环境有权访问的所以函数和变量的有序访问。作用域的前端永远是当前的执行环境所对应的变量对象;

      执行环境:定义了变量或函数有权访问其他数据,决定它们各自的行为。 每个环境都有一个与之对应的变量对象(variable object)。环境中定义的变量和函数都保存在这个对象中。 当函数执行完成后其执行环境就会被销毁

      变量对象:保存执行环境中的变量和函数。

      活动对象:如果执行环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量。

       

       在函数执行过程中,为读取和写入变量的值,就需要的作用域链中查找对应的变量。先看看一个实例来了解下每个概念对应部分: 

       

     1  function compare(value1,value2){
     2  
     3     if(value1<value2){
     4         return -1;
     5     }else if(value1>value2){
     6         return 1;
     7     }else{
     8         return 0;
     9     }
    10   }   
       var result = compare(5,10);
      

       

      先定义了 compare 函数,后又在全局作用域中调用,在这个compare 执行环境为函数的时候,当前活动对象则为变量对象。 活动对象默认是只有 arguments 但是此时却不一样(this,arguments,value1,value2);

    而全局作用域中对应变量对象(compare 、result);来看看下图吧!

      <1>在定义compare函数的时候,会创建一个预先包含全局变量对象作用域链,这个作用域链保存在内部的[[Scope]]属性中。

      <2>当在调用 compare 函数的时候,会为函数创建一个执行环境,然后通过赋值函数的[[Scope]]属性中的对象构建起执行环境的作用域链。此后,又有一个活动对象被创建并推入执行环节作用域链的前端。

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

    所以无论什么时候,访问一个变量时都会从作用域链中搜索具有相应名字的变量。一般来讲当函数执行完毕后。局部活动对象就会被销毁,内存中仅保存全局作用域。但是闭包所有不同。

      在闭包中,当函数内部定义一个内部函数,该内部函数会把外部函数的活动对象添加到自己的作用域链中;因此createComparisonFunction()定义的匿名函数,其作用于域会包含外部函数的作用域。

     1           function  createComparisonFunction(propertyName){
     2                     return function(object1,object2){
     3                         var value1 = object1[propertyName];
     4                         var value2 = object2[propertyName];   
     5                         if(value1<value2){
     6                             return -1;
     7                         }else if(value1>value2){
     8                             return 1;
     9                         }else{
    10                             return 0;
    11                         }
    12                     }
    13                 };    

    当下面代码执行时,外部函数与内部函数包含的作用域链, 如下图

    1 var compare = createComparisonFunction("name");
    2 var result = compare({name:"Nicholas"},{name:"Greg"});

    在代码执行过程中,用变量 compare 保存了其返回的内部匿名函数,而作用域链中包含了三个变量对象,(全局变量对象,外部函数-活动对象,匿名函数-活动对象);

    所以即使返回后,也还是可以访问到对应的变量; 因为外部函数(createComparisonFunction) 执行完成时,其活动对象依然存在,并不会销毁,因为其被内部函数作用域链中引用了;

    虽然 外部函数返回到,其执行环境对应作用域链被销毁,但是其活动对象依然存在。直到匿名函数被销毁,其createComparisonFunction对应的活动对象才会被销毁。

    1 var compare = createComparisonFunction("name");
    2 var result = compare({name:"Nicholas"},{name:"Greg"});
    3 compare = null;  //主要是手动赋值为空,通知系统 进行垃圾处理; 随着其作用域链被销毁(除全局作用域链之外)。

    由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内部占用过多。

        

  • 相关阅读:
    基于.net mvc 的供应链管理系统(YB-SCM)开发随笔1-开篇
    基于.net mvc 的供应链管理系统(YB-SCM)开发随笔
    asp.net http to https
    html嵌入音频
    语义化练习分区域
    html文档引用css使用外部样式表
    字体样式 圆角边框
    HTML-标签
    前端基础—客户端
    html初识form表单
  • 原文地址:https://www.cnblogs.com/czhyuwj/p/5618680.html
Copyright © 2011-2022 走看看