详解JavaScript匿名函数和闭包


Posted in Javascript onJuly 10, 2020

概述

在JavaScript前端开发中,函数与对其状态即词法环境(lexical environment)的引用共同构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在JavaScript,函数在每次创建时生成闭包。匿名函数和闭包可以放在一起学习,可以加深理解。本文主要通过一些简单的小例子,简述匿名函数和闭包的常见用法,仅供学习分享使用,如有不足之处,还请指正。

普通函数

普通函数由fucntion关键字,函数名,() 和一对{} 组成,如下所示:

function box(){
   return 'Hex';
 }
 alert(box());

匿名函数

顾名思义,匿名函数就是没有实际名字的函数。单独的匿名函数无法运行,如下所示:

function (){
   return 'Hex';
 }
 //以上,会报错:缺少标识符

如何解决匿名函数不能执行的问题呢?有如下几种方法:

1. 把匿名函数赋值给变量,如下所示:

//把匿名函数赋值给变量
 var box=function(){
   return 'Hex';
 }
 alert(box());

2. 通过自我执行来调用函数,格式如下:(匿名函数)()

(function(){
   alert('Hex');
 })();

3. 把匿名函数自我执行的返回值赋值给变量,如下所示:

var box=(function(){
   return 'Hex';
 })();
 alert(box);//注意:此处不带括弧

4. 或者省去变量,如下所示:

alert((function() {
   return 'Hex';
 })());

自我执行匿名函数如何传递参数呢?如下所示:

(function(age) {
   alert('Hex--' + age);
 })(30);

闭包(closure)

闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。简单理解:函数里面套函数,子函数可以访问父函数的作用域里面的变量。

1. 函数里面放匿名函数,如下所示:

function box(){
  //闭包
  return function(){
    return 'Hex';
  }
}
alert(box()());
//或者
var b=box();
alert(b());

2. 通过闭包返回局部变量,使用闭包可以有一个优点,和是它的缺点,可以是局部变量驻留在内存中。

function box(){
  var age=100;//此变量为函数的局部变量,外部无法访问
  return function(){
    return age;
  }
}
alert(box()());

闭包和全局变量相比较

1. 使用全局变量累加,如下所示:

var age=100;
function box(){
  age++;
}
alert(age);
box();
alert(age);
box();
alert(age);

2. 使用局部变量累加,如下所示:

function box(){
  var age=100;
  age++;
  return age;
}
alert(box());//无法实现累加
alert(box());//无法实现累加
alert(box());//无法实现累加

3. 使用闭包实现累加,如下所示:

function box(){
  var age=100;
  return function(){
    age++;
    return age;
  }
}
var b=box();//将返回值赋值给b
alert(b());//实现累加
alert(b());//实现累加
alert(b());//实现累加
b=null;//使用闭包在调用结束时不会立即销毁内存,导致性能下降,所以需要解除占用

差异:使用全局变量,容易引起命名冲突,且系统性能下降。

循环匿名函数取值问题

1. 循环里的匿名函数取值问题,如下所示:没有实现arr[0]=0,arr[1]=1 ...arr[4]=4的效果

function box(){
  var arr=[];
  for (var i=0;i<5;i++) {
    arr[i]=function(){
      return i;
    }
  }
  //函数返回之前,循环已经结束,i=5
  return arr;
}
var b=box();
for (var i=0;i<5;i++) {
  alert(b[i]()); //此时返回的都是5,没有实现arr[0]=0,arr[1]=1 ...arr[4]=4的效果
}

以上问题如何优化呢?

方法1,直接赋值,不采用闭包,如下所示:

function box(){
  var arr=[];
  for (var i=0;i<5;i++) {
    arr[i]=i; //直接赋值
  }
  //函数返回之前,循环已经结束,i=5
  return arr;
}
var b=box();
for (var i=0;i<5;i++) {
  alert(b[i]);
}

方法2,通过匿名函数的自我执行,如下所示:

function box(){
  var arr=[];
  for (var i=0;i<5;i++) {
    arr[i]=(function(num){
    //此处可以有其他一些逻辑
    
return num;
    })(i);
  }
  return arr;
}
var b=box();
for (var i=0;i<5;i++) {
  alert(b[i]);
}

方法3,将变量驻留在内存中,如下所示:

function box(){
  var arr=[];
  for (var i=0;i<5;i++) {
    arr[i]=(function(num){
      //此处可以有其他一些逻辑
      return function(){
      return num;
      };
    })(i);
  }
  return arr;
}
var b=box();
for (var i=0;i<5;i++) {
  alert(b[i]());
}

关于this的指向问题

对于对象内部,this指向对象本身,如下所示:

var box={
   getThis:function(){
     return this;
   }
 };
 alert(box.getThis());//输出[object Object] //此处this指box对象
var user='The window';
var box={
  user:'The box',
  getUser:function(){
    return this.user;
  }
}
alert(box.getUser());//输出:the box

this在闭包中,指示window对象,所以闭包在运行时指向window,如下所示:

var box1 ={
  getThis:function(){
    return function(){
      return this;
    }
  }
};
alert(box1.getThis()()); //输出[object Window]//此处this是window对象
var box1={
  user:'The box',
  getUser:function(){
    //此处的作用域是box1
    return function(){
      //此处的作用域是widow
      return this.user;
    };
  }
}
alert(box1.getUser()());//输出:the window ,表示闭包在运行时模拟this指向window

如何让闭包的this指向box呢?可以有如下两种方法,如下所示:

alert(box1.getUser().call(box1));//对象冒充
//可以将box的作用域对象传递给闭包
var box1={
  user:'The box',
  getUser:function(){
    var that=this;
    return function(){
      return that.user;
    };
  }
}
alert(box1.getUser()());

缺点:闭包无法释放对象,容易导致内存泄漏,如下所示:

function box(){
  var a1=document.getElementById('A01');
  var txt=a1.innerHTML;
  a1.onclick=function(){
    //如果a1为null,则会报错
    //alert(a1.innerHTML);//点击事件获取内容,
    alert(txt);
  }
  //如无下面一句,则会导致内存无法释放对象a1
  a1=null;//此处需要手动将a1释放,等待回收
}
box();

块级作用域

模仿块级作用域,面向对象的思想,封装变量。普通函数没有块级作用域的概念,如下所示:

function box(){
  for (var i=0;i<5;i++) {

  }
  alert(i);//输出:5,表示出了for语句块,i依然可以访问
}
box();

如何让i私有化,出了作用域,不可以访问呢?可以采用匿名函数的自我执行,则出了作用域就会访问不到,如下所示:

function box(){
  (function(){
    for (var i=0;i<5;i++) {

    }
  })();
  //alert(i);//报错:提示“i”未定义
}
box();

全局变量的私有作用域,减少变量的命名冲突,如下所示:

(function(){
   //此处就是全局作用域里面的私有作用域
   var age=100;
   alert(age);
 })();
 //alert(age);////报错:提示“age”未定义

普通函数和构造函数的区别:首字母大写。如下所示:对象的属性和函数都是public类型的

function Box(){
  this.age=100; //此处是公有属性,无法私有化
  //函数也是公有函数
  this.run=function(){
    return 'running....';
  }
}
var box=new Box();
alert(box.age); //通过对象可以访问
alert(box.run());//通过对象可以访问

如何将公有属性,私有化呢? 如下所示:

function Box(){
  var age=100;//私有变量,外部访问不到
  function run(){//私有函数,外部访问不到
    return 'running....';
  }
  //对外公布的访问接口,可以访问私有内容
  this.go=function(){
    return age+' '+run();
  }
}
var box=new Box();
alert(box.go());

通过构造函数传递参数,如下所示:

function Box(v){
  var user=v;
  this.getUser=function(){
    return user;
  };
  this.setUser=function(v){
    user=v;
  }
}
var box=new Box('Hex');
alert(box.getUser());
//对象方法可以在创建的时候,创建多次

注意:通过构造函数创建对象,在每次创建的时候,都会分配不同的地址。

静态私有变量

采用静态私有变量,可以实现数据的共享,如下所示:

(function(){
  var user=''; //私有变量
  Box=function(value){//必须全局构造函数,将匿名函数赋值给Box,否则外部无法访问
    user=value;
  }
  Box.prototype.getUser=function(){
    return user;
  };
  Box.prototype.setUser=function(value){
    user=value;
  };
})();
var box=new Box('AAAA'); //第一次实例化
alert(box.getUser());//输出AAAA
var box2=new Box('BBBB');//第二次实例化
alert(box.getUser());//输出BBBB

单例对象

单例即只有一个实例化的对象,可以有两种实现方式。

1. 通过字面量的方式实现,如下所示:

var box={
  user:'hex',
  go:function(){
    return user+' is running....';
  }
};
alert(box.go());

2. 通过匿名函数的自我执行返回对象的方式实现,如下所示:

var box=function(){
  var user='Hex'; //私有变量
  function run(){ //私有函数
    return ' is running....';
  }
  //返回一个对象
  var obj= {
    //公共特权方法
    going:function(){
      return user+run();
    }
  }
  return obj;
}();
alert(box.going());

以上就是详解JavaScript匿名函数和闭包的详细内容,更多关于JavaScript匿名函数和闭包的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
Jquery实现列表(隔行换色,全选,鼠标滑过当前行)效果实例
Jun 09 Javascript
解决JS中乘法的浮点错误的方法
Jan 03 Javascript
使用AngularJS创建单页应用的编程指引
Jun 19 Javascript
js判断手机端(Android手机还是iPhone手机)
Jul 22 Javascript
jQuery弹出div层过2秒自动消失
Nov 29 Javascript
AngularJS指令中的绑定策略实例分析
Dec 14 Javascript
详解AngularJS中$filter过滤器使用(自定义过滤器)
Feb 04 Javascript
Iscrool下拉刷新功能实现方法(推荐)
Jun 26 Javascript
ReactNative Image组件使用详解
Aug 07 Javascript
jqgrid实现简单的单行编辑功能
Sep 30 Javascript
Bootstrap modal只加载一次数据的解决办法(推荐)
Nov 24 Javascript
js对象简介与基本用法示例
Mar 13 Javascript
Vue watch响应数据实现方法解析
Jul 10 #Javascript
详解Vue之事件处理
Jul 10 #Javascript
jQuery开发仿QQ版音乐播放器
Jul 10 #jQuery
Element图表初始大小及窗口自适应实现
Jul 10 #Javascript
Vue路由切换页面不更新问题解决方案
Jul 10 #Javascript
简单了解Vue computed属性及watch区别
Jul 10 #Javascript
通过实例解析chrome如何在mac环境中安装vue-devtools插件
Jul 10 #Javascript
You might like
php mysql数据库操作类
2008/06/04 PHP
php单文件版在线代码编辑器
2015/03/12 PHP
php实现给一张图片加上水印效果
2016/01/02 PHP
PHP添加文字水印或图片水印的水印类完整源代码与使用示例
2019/03/18 PHP
JQuery中form验证出错信息的查看方法
2013/10/08 Javascript
jquery ajax的success回调函数中实现按钮置灰倒计时
2013/11/19 Javascript
window.print打印指定div实例代码
2013/12/13 Javascript
js行号显示的文本框实现效果(兼容多种浏览器 )
2015/10/23 Javascript
理解javascript定时器中的setTimeout与setInterval
2016/02/23 Javascript
JavaScript代码实现左右上下自动晃动自动移动
2016/04/08 Javascript
JS实现获取剪贴板内容的方法
2016/06/21 Javascript
JavaScript中的冒泡排序法
2016/08/03 Javascript
js浏览器滚动条卷去的高度scrolltop(实例讲解)
2017/07/07 Javascript
javascript 开发之网页兼容各种浏览器
2017/09/28 Javascript
AngularJS中table表格基本操作示例
2017/10/10 Javascript
微信小程序实现发红包功能
2018/07/11 Javascript
vue input标签通用指令校验的实现
2019/11/05 Javascript
Element PageHeader页头的使用方法
2020/07/26 Javascript
[03:40]DOTA2英雄梦之声_第01期_炼金术士
2014/06/23 DOTA
python fabric使用笔记
2015/05/09 Python
Python3.2中Print函数用法实例详解
2015/05/19 Python
Python实现以时间换空间的缓存替换算法
2016/02/19 Python
详解Django中六个常用的自定义装饰器
2018/07/04 Python
python实现手势识别的示例(入门)
2020/04/15 Python
python 实现客户端与服务端的通信
2020/12/23 Python
CSS3 实现弹跳的小球动画
2020/10/26 HTML / CSS
我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串?
2014/03/30 面试题
大学生关于奋斗的演讲稿
2014/01/09 职场文书
爱国演讲稿500字
2014/05/04 职场文书
跳蚤市场口号
2014/06/13 职场文书
2015秋季新学期开学寄语
2015/05/28 职场文书
首席执行官观后感
2015/06/03 职场文书
《浅水洼里的小鱼》教学反思
2016/02/16 职场文书
班级元旦晚会开幕词
2016/03/04 职场文书
用Python进行栅格数据的分区统计和批量提取
2021/05/27 Python
Python使用psutil库对系统数据进行采集监控的方法
2021/08/23 Python