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

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

 

 

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

 

譬如要实现一个简单的累加器,为了保存每次累加的结果,因此声明了一个全局变量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].οnclick=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].οnclick=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

 

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

 


更多专业前端知识,请上 【猿2048】www.mk2048.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/363896.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php static_castunsigned int,C++ static_cast、dynamic_cast、const_cast和reinterpret_cast(四种类型转换运算符)...

上节讲到&#xff0c;隐式类型转换是安全的&#xff0c;显式类型转换是有风险的&#xff0c;C语言之所以增加强制类型转换的语法&#xff0c;就是为了强调风险&#xff0c;让程序员意识到自己在做什么。但是&#xff0c;这种强调风险的方式还是比较粗放&#xff0c;粒度比较大&…

AngularJS(九):路由

本文也同步发表在我的公众号“我的天空” AngularJS路由 AngularJS路由可以让我们通过不同的URL访问不同页面&#xff08;似乎是废话&#xff09;&#xff0c;其价值主要体现在单页面的web应用中&#xff08;single page web application&#xff0c;SPA&#xff09;&#xff0…

[C3W2] Structuring Machine Learning Projects - ML Strategy 2

第二周&#xff1a;机器学习策略&#xff08;2&#xff09;&#xff08;ML Strategy&#xff08;2&#xff09;&#xff09; 误差分析&#xff08;Carrying out error analysis&#xff09; 你好&#xff0c;欢迎回来&#xff0c;如果你希望让学习算法能够胜任人类能做的任务&a…

mysql语句执行顺序图示

转载于:https://www.cnblogs.com/whalesea/p/10382227.html

AngularJS(三):重复HTML元素、数据绑定

本文也同步发表在我的公众号“我的天空” 重复HTML元素 在前端的页面编写中&#xff0c;我们会经常遇到重复HTML元素&#xff0c;譬如绘制表格、菜单等&#xff0c;如以下代码显示一个简单的li列表&#xff1a; <body> <ul id"ul_cities"> </ul…

使用表中的数组数据类型

在这篇文章中&#xff0c;我想跟进我以前关于Oracle集合数据类型的文章 &#xff0c;并且我将集中精力使用af&#xff1a;table组件中的oracle.jbo.domain.Array属性。 因此&#xff0c;在我的数据库中&#xff0c;我具有以下SQL类型&#xff1a; create or replace type var…

linux 文件inode,linux文件系统-inode学习整理

linux文件系统-inode学习整理介绍linux文件系统可讲的模块有很多&#xff0c;包括文件系统整体架构、文件系统分类、虚拟文件系统以及文件系统存储结构等等&#xff0c;本文主要介绍的是文件系统的存储结构&#xff0c;也就是本文的重点-inode。文件存储结构首先从开天辟地开始…

pygame-KidsCanCode系列jumpy-part8-记录历史最高分

通常在多玩家的游戏中&#xff0c;每个玩家都会有自己的得分&#xff0c;最高分数会成为该游戏的最佳记录。这一篇&#xff0c;学习下如何记录最高得分&#xff1a;&#xff08;为了简化代码&#xff0c;本文采用文件方式&#xff0c;仅记录本机得分&#xff0c;明白原理后&…

linux下查看进度命令,在Linux系统中使用Coreutils Viewer显示命令运行进度

Coreutils Viewer(cv)是一个简单的程序&#xff0c;它可以用于显示任何核心组件命令(如&#xff1a;cp、mv、dd、tar、gzip、gunzip、cat、grep、fgrep、egrep、cut、sort、xz、exiting)的进度。它使用文件描述信息来确定一个命令的进度&#xff0c;比如cp命令。cv之美在于&…

[EffectiveC++]item34:区分接口继承和实现继承

[EffectiveC]item34&#xff1a;区分接口继承和实现继承 转载于:https://www.cnblogs.com/jeanschen/p/3363569.html

REST-framework快速构建API--四部曲

代码目录结构&#xff1a; 一、使用原生APIView 使用rest-framework原生的APIView实现过程&#xff1a; 以url(r^books/$, views.BookView.as_view(),name"books")为例进行流程分析&#xff0c; 1、views.BookView.as_view()>APIView的as_view方法>父类【View】…

JavaFX技巧2:使用Canvas API进行清晰绘图

当我最初开始使用Canvas API时&#xff0c;我注意到渲染代码的结果有些模糊&#xff0c;甚至更糟&#xff0c;不一致。 有些线条模糊&#xff0c;有些线条清晰。 来自Swing&#xff0c;我花了一些时间才意识到这是由JavaFX的坐标系引起的&#xff0c;该坐标系允许双精度渲染。 …

HTML5 Inline SVG

这是效果图&#xff1a; 1 <!DOCTYPE html>2 <html>3 <head>4 <meta content"text/html; charsetutf-8" http-equiv"Content-Type" />5 <title>geovindu svg</title>6 </head>7 <body>8 9 <svg width&…

前端开发网络——Ajax(GET、POST)

ajax请求的过程 我们平时输入的网址&#xff0c;比如www.baidu.com&#xff0c;就会被解析成14.215.177.39这一串数字&#xff0c;然后发送请求给后台服务器&#xff08;客户端发送http请求&#xff09;。 服务器会确认你发送的是什么请求&#xff0c;需要请求什么东西&#xf…

通过Spring将继承树加载到List中

我注意到有趣的Spring功能。 我的一位同事使用它将Spring Bean的整个继承树加载到列表中。 在学习Spring文档时错过了这一点。 让我们来看看Spring bean的继承树&#xff1a; 下面的代码片段是通过构造函数注入将该豆树加载到列表中的&#xff1a; Component public class N…

在VS2003下把一个DataTable Update 到数据库

假设一个常见的场景先吧----实际也是我当前的场景-----把一个excel 文件导入到数据库. 这实在是一个常见的功能,但是,没想到的是, 我着实费了一把劲. 实际上,我以前写的有现成的函数来完成这个工作, 但是, 可惜那函数只能在VS2005 下工作, 在2003下面无效,无效的原因是,vs2003 …

linux 挂载有数据硬盘分区,linux下磁盘分区、挂载知多少

0x01 Linux 分区简介主分区 vs 扩展分区硬盘分区表中最多能存储四个分区&#xff0c;但我们实际使用时一般只分为两个分区&#xff0c;一个是主分区(Primary Partion)一个是扩展分区(extended partition)&#xff0c;主分区可以马上被使用但不能再分区&#xff0c;扩展分区必须…

Java 8中的HashMap性能改进

HashMap<K, V>是每个Java程序中快速&#xff0c;通用且无处不在的数据结构。 首先是一些基础知识。 您可能知道&#xff0c;它使用键的hashCode()和equals()方法在存储桶之间拆分值。 存储桶&#xff08;箱&#xff09;的数量应略高于映射中的条目数&#xff0c;以便每个…

css 汉字注音,日本语片假名

代码 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">2 <html xmlns"http://www.w3.org/1999/xhtml">3 4 <head>5 <meta content"te…

Microsoft .NET 框架资源基础 ---摘自:msdn

Chris SellsSells Brothers Consulting 摘要&#xff1a;Chris Sells 讨论无类型清单资源和有类型资源&#xff0c;它们是受 Microsoft .NET 框架支持的两种资源。他定义了这两种资源&#xff0c;并介绍了如何在您自己的应用程序中使用它们。 下载 winforms02202003.exe 示例文…