JavaScript闭包

简介:

前言

每个函数都有自己的执行环境。当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链,并把作用域链赋值给一个特殊的内部属性(即[[Scope]])。然后使用this、arguments和函数参数、内部变量、内部函数引用来初始化函数的活动对象(activation object)。作用域链(堆栈)是指向活动对象的指针列表,该函数的活动对象在栈顶,全局变量对象在栈底。

PS:在 JavaScript 的执行中会一直存在一个Execute Context Stack , 最下面一个是Global Context,创建的execution context会被压入这个栈。

这里面提到几个关键字:

1. 执行环境(execution context)

声明该函数有权访问的变量和函数。

2. 作用域链(scope chain)

作用域链的创建规则是复制上一层环境的作用域链,并将指向本环境变量对象的指针放到链首。本质上是一个指向变量对象的指针列表,它只是引用,实际上不包含变量对象。另外JavaScript是函数作用域的,并没有像Java、C那样有块级作用域。

PS:JS主要是词法作用域(lexical scope,也即是静态作用域),在词法解析阶段既确定了。但是有两个特例,就是eval和with可以构成动态作用域。

3. 活动对象(activation object)

保存该函数arguments和函数参数、内部变量和内部函数引用。

例子:

复制代码
function compare(value1, value2){
     if(value1 < value2){
          return -1;
     } else if(value1 > value2){
          return 1;
     } else{
          return 0;
     }
}
var result = compare(5, 10);
复制代码

相应的执行环境图示:

闭包概念

JavaScript高级程序设计里面对闭包的描述是,“闭包是指有权访问另一个函数作用域中的变量的函数”。我觉得严格上讲,这个“另一个”是闭包函数的外部函数。

ECMAScript中,闭包指的是:

1. 从理论角度:所有的函数。因为它们都在执行的时候就将上层上下文的数据保存起来了(体现在作用域链)。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。

2. 从实践角度:以下函数才算是闭包:

(1). 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)。

(2). 在代码中引用了自由变量。

PS:自由变量是指在函数中使用的,但既不是函数参数也不是函数的局部变量的变量。

闭包使用场景

1. 循环遍历中的延迟使用变量

我们可能在循环中为某些元素注册事件,或setTimeout执行一些代码,这段代码使用到循环部分的变量(我称这个变量为延迟使用变量),可能需要使用闭包保证变量的准确。

例子:

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script type="text/javascript">
          function onMyLoad(){
            var arr = document.getElementsByTagName("p");
            //有问题写法
            for(var i = 0; i < arr.length;i++){
                arr[i].onclick = function(){
                    alert(i);
                }
            }
            //闭包写法
            for(var i = 0;i<arr.length;i++){
               (function (arg) {
                  arr[i].onclick = function () {
                     alert(arg);
                  }
               })(i);
            }
        }
    </script></head><body onload="onMyLoad()">
    <p>产品一</p>
    <p>产品二</p>
    <p>产品三</p>
    <p>产品四</p>
    <p>产品五</p>
</body>
</html>
复制代码

2. 私有变量

JavaScript没有私有变量这东西,只是用闭包模拟而已。

例子:

复制代码
function MyObject(){
     var privateVar = 10;
     this.getPrivateVar = function(){
          return privateVar;
     }
}
复制代码

3. 模块模式

为单例创建私有变量和特权方法(有权访问私有变量和私有函数的公有方法)就是模块模式。

例子:

复制代码
var singleton = (function(){
     var privateVar = 10;
     var getPrivateVar = function(){
          return privateVar;
     }
     return {
         getPrivateVar : getPrivateVar
     }
})();
复制代码

4. 块级作用域

可以用来模拟块级作用域。

闭包注意地方

1. 闭包会使函数中的变量常驻内存,所以如果滥用闭包,会导致内存回收不回来,影响脚本性能。

例子:

复制代码
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };
  this.getMessage = function() {
    return this.message;
  };
}
复制代码

上面的代码并未利用到闭包的益处,因此,应该修改为如下常规形式:

复制代码
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};
复制代码

PS:前者每次实例化时候,每个对象里面都存在getName 、getMessage方法,后者则不会。

Q&A

1. 闭包是函数还是一种代码行为?所有函数都是闭包吗?

参考闭包概念

2. 变量查找顺序?

当在函数中访问一个变量的时候,搜索顺序是先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索作用域链上的活动对象,依次查找,直到找到为止。如果整个作用域链上都无法找到,则返回undefined。如果函数存在prototype原型对象,则在查找完自身的活动对象后,再查找自身的原型对象,再继续查找。

参考文献

1. JavaScript高级程序设计 第7章

2. https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures (MDN)

3. http://www.cnblogs.com/TomXu/archive/2012/01/31/2330252.html (汤姆大叔)

 

本文为原创文章,转载请保留原出处,方便溯源,如有错误地方,谢谢指正。

本文地址 :http://www.cnblogs.com/lovesong/p/5468475.html

转载:http://www.cnblogs.com/lovesong/p/5468475.html

目录
相关文章
|
24天前
|
前端开发 JavaScript 开发者
Web前端开发中的JavaScript闭包应用
JavaScript闭包是Web前端开发中常见的概念,它可以帮助开发者解决作用域问题,提高代码的可读性和可维护性。本文将介绍JavaScript闭包的基本概念和应用,以及如何在Web前端开发中使用闭包。
19 3
|
28天前
|
JavaScript 前端开发 安全
闭包治愈“全局变量恐惧症”,利用闭包实现JavaScript私有变量(三)
闭包治愈“全局变量恐惧症”,利用闭包实现JavaScript私有变量
|
28天前
|
存储 JavaScript 前端开发
闭包治愈“全局变量恐惧症”,利用闭包实现JavaScript私有变量(一)
闭包治愈“全局变量恐惧症”,利用闭包实现JavaScript私有变量
|
1天前
|
JavaScript 前端开发 Java
深入理解JavaScript中的闭包机制
闭包是JavaScript中一个重要且常用的概念,能够帮助我们更好地管理变量和函数作用域。本文将深入探讨JavaScript中闭包的原理及应用,帮助读者全面理解闭包在实际开发中的作用。
14 6
|
1天前
|
存储 自然语言处理 JavaScript
深入理解JavaScript中的闭包机制
闭包是JavaScript中一个重要且常见的概念,它能够帮助我们更好地管理变量作用域和实现数据封装。本文将深入探讨JavaScript中闭包的原理、应用场景以及优缺点,帮助读者更全面地理解和运用闭包机制。
|
1天前
|
JavaScript 前端开发 开发者
深入理解JavaScript中的闭包
闭包是JavaScript中一个重要且常用的概念,它能够帮助开发者有效地管理变量作用域并实现更灵活的编程功能。本文将深入探讨JavaScript中闭包的原理、使用方法以及常见应用场景,帮助读者更好地理解和运用闭包。
9 2
|
18天前
|
缓存 自然语言处理 JavaScript
10分钟带你深入理解JavaScript的执行上下文和闭包机制
JavaScript中的闭包源于计算机科学中的一种理论概念,称为“λ演算”(Lambda Calculus)。λ演算是计算机科学的基础之一,1930年由Alonzo Church提出,它是一种用于描述计算过程的数学抽象模型,也是函数式编程语言的基础。
|
18天前
|
缓存 自然语言处理 前端开发
JavaScript 闭包
JavaScript 闭包,是指函数可以访问其定义时所在的词法环境(lexical environment)中的变量,即使这个函数在其定义的词法环境之外执行。闭包是 JavaScript 中一个重要的概念,它使得
13 1
|
24天前
|
缓存 前端开发 JavaScript
深入理解JavaScript中的闭包机制
在JavaScript编程中,闭包是一个重要的概念。本文将深入探讨闭包的原理和用法,帮助读者更好地理解和应用闭包机制。我们将介绍闭包的定义、工作原理以及在前端开发中的实际应用案例。
8 0
|
24天前
|
自然语言处理 JavaScript 前端开发
JavaScript中的闭包和作用域链
本文将介绍JavaScript中的闭包和作用域链的概念及其在编程中的应用。闭包是指函数与其相关的词法环境的组合,通过闭包我们可以实现变量的私有化和封装性,提高代码的安全性和可维护性。作用域链则是指在嵌套函数中查找变量的一种机制,它决定了变量的可见性和访问顺序。深入理解闭包和作用域链对于提升JavaScript编程技巧至关重要。

相关产品

  • 云迁移中心