作用域、执行环境、闭包(四)

本文也同步发表在我的公众号“我的天空

上一期我们已经介绍了闭包,由于闭包可以延长函数内部的变量的生存周期,因此我们可以将不需要暴露在全局的变量封装成函数的内部变量,从而避免代码污染。

譬如要实现一个简单的累加器,为了保存每次累加的结果,因此声明了一个全局变量total,代码如下:

var total=0;
function add(t){
    total+=t;
    alert(total);
}
total=2;
add(3);        //显示5
add(5);        //显示10
add(1);        //显示11

但是在实际开发中,应尽量避免全局变量,因为全局变量可以在代码的任何地方被调用,假设在其他地方,不小心更改了total的值的话,我们这个累加器就会出问题了。由于total值是只供函数add()使用的,因此希望total能被封闭在函数add()的内部,这样,就无法从外部来改写它了,我们使用闭包来实现,代码如下:

function add(s){
    var total=s;
    return function(t){
        total+=t;
        alert(total);
    }
}
var a=add(2);
a(3);        //显示5
a(5);        //显示10
a(1);        //显示11

通过闭包,将变量total封闭在函数add()中,外部无法访问到,这样就避免了被其他代码随意改写的可能性。

由于闭包会将封闭在函数内部的局部变量赋予类似于全局变量的效果,因此在有些场景下需要特别注意,尤其是涉及到循环遍历,来看以下代码:

function createArray(){
    var result=new Array();
    for (var i=0;i<3;i++){
        result[i]=function(){
            return i;
        }
    }
}

这是一个创建数组数组的函数,从表面上看,每个函数应该都返回自己的索引值,因此创建的数组中,每个元素应该包含如下函数:

result[0]:function(){return 0}
result[1]:function(){return 1}
result[2]:function(){return 2}

但是实际上,数组中的每个元素只是包含:function(){return i},也就是说,当该函数执行完毕后,返回的是这样一个数组:

{
    function(){return i},
    function(){return i},
    function(){return i}
}

而变量i由于存在于一个返回函数中,形成了闭包,所以当createArray()执行完毕后,其执行环境不会被销毁,变量i得以保留,并且其值为3(这点很重要)。

因此,当我们使用createArray()来创建数组时,得到的效果就不是我们的预期,弹出的都为“3”:

var a=createArray();
for(var z=0;z<a.length;z++){
    alert(a[z]());    //均显示为3
}

实际上该代码无非就是重复执行三遍以下代码:

alert(function(){return 3}());

那么为了达到我们的预期,应该将createArray()函数做如下修改:

function createArray(){
    var result=new Array();
    for (var i=0;i<3;i++){
        result[i]=function(z){
            return function(){
                return z;
            };
        }(i)
    }
}

分析以上代码,我们将一个自执行函数返回给了数组元素,在赋值的时候,变量z就是在赋值的那个时刻的i值,那么返回的数组中的元素便包含我们预期的函数:

result[0]:function(){return 0}
result[1]:function(){return 1}
result[2]:function(){return 2}

再一次执行以下代码,显示就正常了:

var a=createArray();
for(var z=0;z<a.length;z++){
    alert(a[z]());    //依次显示0、1、2
}

一定要注意的是,我们是把一个函数赋予了数组中的元素,而不是单个的值。因为在实际的应用中,返回函数的话,我们就可以在函数内做更多的事情。

看以下的实现,html有四个p标签和4个div标签,当单击div标签时相应的p标签更改颜色,请注意这是一个面试中非常容易遇到的题目,代码如下:

<body>
  <p>p1</p><p>p2</p><p>p3</p><p>p4</p>
  <div>div1</div><div>div2</div><div>div3</div><div>div4</div>  
 </body>

<script>
     var d=document.getElementsByTagName("div");
       for(var i=0;i<d.length;i++){
         d[i].onclick=function(num){
             return function(){
                document.getElementsByTagName("p")[num].style.color="red";
             };
         }(i);
       }
 </script>

如果直接写成以下代码的话,那么无论你单击哪个div,程序总是会报错,因为此时i的值为4,所以document.getElementsByTagName("p")[4]这个元素并不存在,导致引用错误。

for(var i=0;i<d.length;i++){
        d[i].onclick=function(){
          document.getElementsByTagName("p")[i].style.color="red";
      };
   }

最后我们要注意的是当需要返回函数内部的多个变量时,便不能采用返回匿名函数的方式了,可以采用以下的形式:

function setpepole(){
    var name="李四";    
    var age=31;
    return {
        getname:funcion(){
            return name;    
        },
        getage:function(){
            return age;
        }
    }
}
var a=setpepole();
alert(a.getname());     //显示“李四”
alert(a.getage());      //显示31

闭包系列就到此全部结束了!

时间: 2024-10-08 17:14:48

作用域、执行环境、闭包(四)的相关文章

4.1-4.2 基本类型及引用类型,执行环境及作用域

一.理解基本类型和引用类型的值 ECMAScript变量包含两种不同数据类型的值:基本类型值 和 引用类型值. 基本类型值指的是简单的数据段,而引用类型的值指那些可能由多个值构成的对象 引用类型的值是保存在内存中的对象,javascript不允许直接访问内存中的位置,也就是不能直接操作对象的内存空间.在操作对象时,实际上是在操作对象的引用而不是实际的对象.引用类型的值是按引用访问的,基本类型是按值访问的,因为可以操作保存在变量中的实际的值. 动态的属性 定义基本类型值和引用类型值的方式是类似的:

《JavaScript高级程序设计》读书笔记 ---执行环境及作用域

执行环境及作用域 执行环境(execution context,为简单起见,有时也称为“环境”)是JavaScript 中最为重要的一个概念.执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为.每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中.虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它.全局执行环境是最外围的一个执行环境.根据ECMAScript 实现所在的宿主环境不同,表示执行环境的

2017-5-26执行环境及租用域

执行环境及作用域 执行环境及作用域 执行环境及作用域 执行环境:定义了变量和函数有权访问的其他数据,决定了它们各自的行为,每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中 *全局执行环境:是最外围的一个执行环境,全局执行环境直到应用程序退出时才会被销毁; 1.每个函数都有自己的执行环境 2.在局部作用域中定义的变量可以在局部环境中与全局变量互换使用 4.2.1 延长作用域链 执行环境的类型总共只有两种:全局和局部(函数) 延长作用域链:有些语句可以在作用域链的

执行环境、作用域、作用域链、调用对象、闭包

执行环境 : 每调用一个函数时(执行函数时),系统会为该函数创建一个封闭的局部的运行环境,即该函数的执行环境.函数总是在自己的执行环境中执行,如读写局部变量.函数参数.运行内部逻辑.创建执行环境的过程包含了创建函数的作用域,函数也是在自己的作用域下执行的.从另一个角度说,每个函数执行环境都有一个作用域链,子函数的作用域链包括它的父函数的作用域链.关于作用域.作用域链请看下面. 作用域.作用域链.调用对象: 函数作用域分为词法作用域和动态作用域. 词法作用域是函数定义时的作用域,即静态作用域.当一

执行环境 作用域 作用域链 闭包的理解

1.首先 当一个变量或者函数被声明的时候 它的执行环境便被确认 , 执行环境定义了变量和函数有权访问的其他数据,决定了他们各自的行为, 而作用域就是变量和函数的可访问范围,控制着变量和函数的可见性与生命周期 每次进入一个新的执行环境,都会创建一个用于搜索变量和函数的作用域链.作用域链是函数被创建的作用域中对象的集合.作用域链可以保证对执行环境有权访问的所有变量和函数的有序访问. 作用域链的最前端始终是当前执行的代码所在环境的变量对象(如果该环境是函数,则将其活动对象作为变量对象),下一个变量对象

javascript 作用域链及闭包,AO,VO,执行环境

下面的文章内容会根据理解程度不断修正. js变量作用域: 定义:变量在它申明的函数体以及函数体内嵌套的任意函数体内有定义. function AA(){ var bb='我是AA内部变量'; function TT(){ alert(bb); } alert(bb); TT(); } AA(); 如上图,两次弹出的都是“我是AA内部变量”. JS的变量作用域是函数级的,也就是在AA内部申明的变量,在AA内部任意位置,包括它嵌套的函数内也是有定义的. 在函数AA外面,bb就是没有定义的.当然如果去

JS复习—执行环境及作用域

执行环境及作用域 一.定义 (1)执行环境:执行环境定义了变量或函数有权访问其他数据,决定了它们各自的行为. (2)作用域链:保证对执行环境有权访问的所有变量和函数的有序访问.作用域链本质是一个指向变量对象的指针列表,它只是引用但不实际包含变量对象 (3)变量对象:环境中定义的所有变量和函数都保存在这个对象中 (4)活动对象:如果当前的环境是函数,则这个变量对象是活动对象 (5)作用域:程序代码中定义这个变量的区域 二.标示符的搜索过程 当某个函数被调用时,会创建一个执行环境及相应的作用域链.然

高程(4):执行环境、作用域、上下文执行过程、垃圾收集、try...catch...

高程三 4.2.4.3 一.执行环境 1.全局执行环境是最外层的执行环境. 2.每个函数都有自己的执行环境,执行函数时,函数环境就会被推入一个当前环境栈中,执行完毕,栈将其环境弹出,把控制器返回给之前的执行环境. 二.作用域 1.每个执行环境,都对应一个自己的作用域. 2.每个执行环境,都能访问上一级的父级.父父级....作用域,称为 "作用域链" 3.执行代码时,会按照标识符沿着作用域链一级一级向上查找.如果找不到,则会报错 *标志符:执行时,使用到的变量或函数 三.上下文执行过程

javascript的apply和call,执行环境,垃圾回收,闭包

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getS