重拾ECMAScript基础——闭包与匿名函数

ECMAScript中经常把闭包与匿名函数混用,所以很多时候会搞不清这两个概念

闭包是指有权访问另一个函数作用域中的变量的函数。——《JS高程》

Closures (闭包)是使用被作用域封闭的变量,函数,闭包等执行的一个函数的作用域。通常我们用和其相应的函数来指代这些作用域。(可以访问独立数据的函数)
闭包是一个函数和声明该函数的词法环境的组合。从理论角度来说,所有函数都是闭包。
——MDN https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

函数表达式

JavaScript中声明函数有两种方式

// 1.函数声明
function functionName(arg0, arg1, arg2) {
  //函数体
}
// 2.匿名函数赋值
var functionName = function(arg0, arg1, arg2){
  //函数体
}

作用域链

在函数执行过程中,需要在作用域链中查找变量:

function compare(value1, value2){
   if (value1 < value2){
     return -1;
   } else if (value1 > value2){
     return 1;
   } else {
     return 0;
   }
}
var result = compare(5, 10); 
重拾ECMAScript基础——闭包与匿名函数_第1张图片
作用域链.png

当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象);

闭包

而闭包有所不同

闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

function createComparisonFunction(propertyName) {

   return function(object1, object2){
     var value1 = object1[propertyName];
     var value2 = object2[propertyName];

     if (value1 < value2){
       return -1;
     } else if (value1 > value2){
       return 1;
     } else {
       return 0;
     }
   };
 } 

var compare = createComparisonFunction("name");
var result = compare({ name: "Nicholas" }, { name: "Greg" }); 

createComparisonFunction这个函数中,返回的匿名函数赋值给了compare这个变量,我们可以说compare是一个闭包
返回的匿名函数的作用域链中可以访问在createComparisonFunction中的所有变量,而且函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。
直到匿名函数被销毁后,createComparisonFunction()的活动对象才会被销毁;

//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
//解除对匿名函数的引用(以便释放内存)
compareNames = null; 

首先,创建的比较函数被保存在变量compareNames 中。而通过将compareNames 设置为等于null
解除该函数的引用,就等于通知垃圾回收例程将其清除。随着匿名函数的作用域链被销毁,其他作用域
(除了全局作用域)也都可以安全地销毁了。


重拾ECMAScript基础——闭包与匿名函数_第2张图片
作用域链.png

闭包与匿名函数

在开发中,我们常会用到这样的写法

(function () {
    // ... all vars and functions are in this scope only
    // still maintains access to all globals
}());

这里声明匿名函数立即执行,使得局部变量不会污染到全局变量,并可以访问全局变量(外部变量);

var num = 1;
(function () {
    var num = 2;
    console.log(num); // 2
}())
console.log(num); // 1

匿名函数通常与闭包一起使用,但并无必然联系;
因为闭包保存的是变量对象,所以我们往往要用匿名函数立即执行来保存过程中的值;(见需要注意)

闭包的实用

想了想,自己总结的不如直接看文档。
MDN 文档 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

  1. 在函数中给事件驱动型的变量添加函数;
  2. 模拟私有方法

需要注意

作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。 ——《JS高程》

因为闭包所保存的是整个变量对象;

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

//都是10

我们必须通过匿名函数的立即执行来进行保存

function createFunctions(){
   var result = new Array();
   for (var i=0; i < 10; i++){
     result[i] = function(num){
       return function(){
         return num;
       };
     }(i);
   }
   return result;
} 

//1-10

当然,ES6可以使用let来替代

function createFunctions(){
   var result = new Array();
   for (let i=0; i < 10; i++){
   result[i] = function(){
       return i;
     };
   }
   return result;
} 

你可能感兴趣的:(重拾ECMAScript基础——闭包与匿名函数)