详解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 相关文章推荐
javascript中巧用“闭包”实现程序的暂停执行功能
Apr 04 Javascript
javascript中利用数组实现的循环队列代码
Jan 24 Javascript
jquery获取div宽度的实现思路与代码
Jan 13 Javascript
jquery设置控件位置的方法
Aug 21 Javascript
用js模拟struts2的多action调用示例
May 19 Javascript
JavaScript判断undefined类型的正确方法
Jun 30 Javascript
Angular 理解module和injector,即依赖注入
Sep 07 Javascript
BootStrap 动态添加验证项和取消验证项的实现方法
Sep 28 Javascript
如何使用Bootstrap 按钮实例详解
Mar 29 Javascript
5分钟学会Vue动画效果(小结)
Jul 21 Javascript
微信小程序顶部导航栏滑动tab效果
Jan 28 Javascript
原生js实现商品筛选功能
Oct 28 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 输出缓存详解
2009/06/20 PHP
php遍历目录输出目录及其下的所有文件示例
2014/01/27 PHP
php smarty模板引擎的6个小技巧
2014/04/24 PHP
Zend Framework教程之Zend_Controller_Plugin插件用法详解
2016/03/07 PHP
基于php实现的php代码加密解密类完整实例
2016/10/12 PHP
js 禁用浏览器的后退功能的简单方法
2008/12/10 Javascript
js计算页面刷新的次数
2009/07/20 Javascript
JS分割字符串并放入数组的函数
2011/07/04 Javascript
JS调用CS里的带参方法实例
2013/08/01 Javascript
jQuery模拟点击A标记示例参考
2014/04/17 Javascript
JavaScript实现在标题栏上显示当前日期的方法
2015/03/19 Javascript
Node.js中的process.nextTick使用实例
2015/06/25 Javascript
jquery实现适用于门户站的导航下拉菜单效果代码
2015/08/24 Javascript
JavaScript编写带旋转+线条干扰的验证码脚本实例
2016/05/30 Javascript
浅谈JavaScript前端开发的MVC结构与MVVM结构
2016/06/03 Javascript
ion content 滚动到底部会遮住一部分视图的快速解决方法
2016/09/06 Javascript
微信js-sdk预览图片接口及从拍照或手机相册中选图接口用法示例
2016/10/13 Javascript
基于JavaScript实现淘宝商品广告效果
2017/08/10 Javascript
Angularjs之ngModel中的值验证绑定方法
2018/09/13 Javascript
Vue动画事件详解及过渡动画实例
2019/02/09 Javascript
js屏蔽退格键(backspace或者叫后退键与F5)
2019/02/10 Javascript
vue实现选中效果
2020/10/07 Javascript
简单介绍Python中的struct模块
2015/04/28 Python
python监控文件或目录变化
2016/06/07 Python
Python+request+unittest实现接口测试框架集成实例
2018/03/16 Python
Python实现的远程登录windows系统功能示例
2018/06/21 Python
python 内置模块详解
2019/01/01 Python
python实现简单图书管理系统
2019/11/22 Python
用于ETL的Python数据转换工具详解
2020/07/21 Python
秘书岗位职责
2013/11/18 职场文书
初中音乐教学反思
2014/01/12 职场文书
税务干部个人整改措施思想汇报
2014/10/10 职场文书
防汛通知
2015/04/25 职场文书
导游词之介休绵山
2019/12/31 职场文书
使用JS实现简易计算器
2021/06/14 Javascript
Python 游戏大作炫酷机甲闯关游戏爆肝数千行代码实现案例进阶
2021/10/16 Python