详解JavaScript匿名函数与闭包:原理、应用及常见陷阱

作者:半夏时间:2026-10-23 19:49:02浏览:451

本文深入解析JavaScript中匿名函数与闭包的核心概念。详细阐述匿名函数的定义与自执行方式,剖析闭包的形成机制及其在变量持久化中的应用。通过对比全局变量与局部变量,说明闭包在避免命名冲突和实现数据封装上的优势。同时,针对循环中匿名函数取值错误、this指向丢失及内存泄漏等常见陷阱,提供了基于闭包和立即执行函数的优化方案,帮助开发者掌握高级JS编程技巧。

概述

概述

在前端JavaScript开发中,函数与其创建时的词法环境(lexical environment)引用共同构成了闭包(closure)。这意味着内部函数可以访问外部函数的作用域。在JavaScript中,每次创建函数时都会生成闭包。将匿名函数与闭包结合学习,有助于加深理解。本文通过几个简单的示例,简要介绍匿名函数和闭包的常见用法,旨在供学习参考,如有疏漏之处,欢迎指正。

普通函数与匿名函数基础

普通函数

普通函数由function关键字、函数名、圆括号()以及一对花括号{}组成,示例如下:


 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指向的问题

在对象内部,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对象,因此闭包运行时this指向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匿名函数和闭包的资料请关注本站其它相关文章!

作者最新文章

相关标签: