许多书上闭包过于复杂讲解难懂,自己理解了一下并总结啦~

讲闭包之前,需要先明白以下几个概念。

总之,函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。

1、执行上下文(execution context)

 每创建一个函数同时就会创建一个执行环境,也就是执行上下文。全局执行上下文就是global环境,一个函数内部的当前执行环境就是当前执行上下文。

 执行上下文定义了变量或函数有权访问其他数据,决定了他们各自的行为 。

2、执行上下文堆栈

 活动的执行上下文在逻辑上组成一个堆栈。堆栈底部永远都是全局上下文(globalContext),而顶部就是当前(活动的)执行上下文。

当一个函数被创建且被调用时,在函数内部,当前的函数执行上下文被压入栈,若内部还有函数,则继续压入栈顶。栈底部永远是全局执行上下文

3、变量对象

每个执行上下文中有会有与之关联的变量对象,在上下文中定义的所有变量和函数都会放在这里面。如果在函数中,我们称之为活动对象

可以说变量对象是与执行上下文相关的数据作用域(scope of data) 。它是与执行上下文关联的特殊对象,用于存储被定义在执行上下文中的变量(variables)、函数声明(function declarations) 。

4、作用域链

 作用域链是用来指向变量对象的,作用域的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域的最前端,始终都是当前执行的代码所在环境的变量对象

 子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

var f1 = function() {
function f2() {
function f3() {}
}
}
//f3中 作用域链就是 f3活动对象 -> f2活动对象 -> f1活动对象 -> global活动对象

可以开始讲闭包啦

定义:闭包是指有权访问另一个函数作用域中的变量的函数

是不是有点过于抽象了?举个例子

var outter = function() {
var a = 1;
function inner() {
return a;
}
return inner; //返回里面这个函数
}
var result = outter(); //外部得到了返回的里面那个函数
console.log(result()); //1

由JavaScript的作用域链特性可知,在函数里面可以访问到外部的变量,但反过来是不行的。但上面这个例子做到了,这就是闭包。

既然inner可以读取outter中的局部变量,那么只要把inner作为返回值,我们不就可以在outter外部读取它的内部变量了吗!

上一节代码中的inner函数,就是闭包。

各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。

由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁

使用闭包时常出现的一些问题

1、闭包只能取得包含函数中任何变量的最后一个值

好像有点抽象。不要忘记,闭包所保存的是整个变量对象,而不是某个特殊的变量。其实这里可以把它想象成类似于一个引用。

function createArray() {
var result = new Array(); for(var i = 0;i<10;i++) {
result[i] = function() {
return i;
};
}
return result;
}
var r = createArray();
console.log(r);
for(var i=0;i<10;++i)
console.log(r[i]()); //10个10

原因在于闭包保存的是整个变量对象,因此每个函数中都保存一样的变量对象。它们引用的都是同一个变量i。所以在执行闭包时,i已经执行到10,返回的i自然便都是10。

2、关于this对象

我们知道,this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数作为某个对象的方法调用时,this等于那个对象

不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window

var name = "The Window";
var object = {
name: "My object",
getNameFunc: function() {
return function() {
return this.name;
};
}
}
console.log(object.getNameFunc()()); //The Window

为什么最后的结果是"The Window"而不是object里面的name"My object"呢?

 首先,要理解函数作为函数调用和函数作为方法调用。

 我们把最后的一句拆成两个步骤执行:

var first = object.getNameFunc();
var second = first

其中

  • 第一步,获得的first为返回的匿名函数,此时的getNameFunc()作为object的方法调用,如果在getNameFunc()中使用this,此时的this指向的是object对象。
  • 第二步,调用first函数,可以很清楚的发现,此时调用first函数,first函数没有在对象中调用,因此是作为函数调用的,是在全局作用域下,因此first函数中的this指向的是window。

    为什么匿名函数没有取得其包含作用域(外部作用域)的this对象呢?

     每个函数被调用时,其活动对象都会自动取得两个特殊变量:this和arguments。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。



    那么,怎么获得外部作用变量中的this呢?

    把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了。
var name = "The Window";
var object = {
name: "My object",
getNameFunc: function() {
var name = 19;
var that = this;
return function() {
return that.name;
};
}
}
console.log(object.getNameFunc()()); //My object

此时调用,getNameFunc执行时的活动变量有哪些?name that function。在执行匿名函数时,同时引用了getNameFunc()中的活动对象,因此可以获取that和age的值。但是由于是在全局环境中调用的匿名函数,因此匿名函数内部的this还是指向window

闭包用途总结

1、可以读取函数内部的变量,建立函数内部与外部的桥梁;

2、让一些变量的值始终保持在内存中。

使用闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

《JavaScript高等程序设计第3版》

Javascript函数闭包详解(通俗易懂的更多相关文章

  1. JavaScript的闭包详解

    (1)定义: 函数内部返回一个函数,返回出来的这个函数叫做被我们称之为闭包(个人理解的最简单的表现形式,) (2)为什么要使用闭包呢? 局部变量在函数执行完之后就会被GC回收,有时候我们想在外部访问内 ...

  2. javascript:function 函数声明和函数表达式 详解

    函数声明(缩写为FD)是这样一种函数: 有一个特定的名称 在源码中的位置:要么处于程序级(Program level),要么处于其它函数的主体(FunctionBody)中 在进入上下文阶段创建 影响 ...

  3. 从mixin到new和prototype:Javascript原型机制详解

    从mixin到new和prototype:Javascript原型机制详解   这是一篇markdown格式的文章,更好的阅读体验请访问我的github,移动端请访问我的博客 继承是为了实现方法的复用 ...

  4. JavaScript严格模式详解

    转载自阮一峰的博客 Javascript 严格模式详解   作者: 阮一峰 一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:"严格模式"(strict m ...

  5. eval()函数用法详解

    eval()函数用法详解:此函数可能使用的频率并不是太高,但是在某些情况下具有很大的作用,下面就介绍一下eval()函数的用法.语法结构: eval(str) 此函数可以接受一个字符串str作为参数, ...

  6. Python闭包详解

    Python闭包详解 1 快速预览 以下是一段简单的闭包代码示例: def foo(): m=3 n=5 def bar(): a=4 return m+n+a return bar >> ...

  7. JavaScript运行机制详解

    JavaScript运行机制详解   var test = function(){ alert("test"); } var test2 = function(){ alert(& ...

  8. [转]JavaScript异步机制详解

    原文: https://www.jianshu.com/p/4ea4ee713ead --------------------------------------------------------- ...

  9. js课程 1-3 Javascript变量类型详解

    js课程 1-3  Javascript变量类型详解 一.总结 一句话总结:js对象点(属性方法),json对象冒号(属性方法).属性和方法区别只有一个括号. 1.json对象中的函数的使用? 函数名 ...

随机推荐

  1. Codeforces Round #637 (Div. 2) 题解

    A. Nastya and Rice 网址:https://codeforces.com/contest/1341/problem/A Nastya just made a huge mistake ...

  2. 题目分享E 二代目

    题意:一棵点数为n的树,每个节点有点权,要求在树中中找到一个最小的x,使得存在一个点满足max(该点点权,该点相邻的点的点权+1,其他点的点权+2)=x 分析:首先要能把题目转化为上述题意 首先题目让 ...

  3. ubuntu 下修改MySQL 的root用户密码

    环境:Ubuntu 16.04  :  Mysql 5.7问题:mysql root登录密码 忘记了..忘记了..忘..了 1.登录MySQL $ mysql -u root -p 输入密码,如果登录 ...

  4. Python爬虫(二)爬百度贴吧楼主发言

    爬取电影吧一个帖子里的所有楼主发言: # python2 # -*- coding: utf-8 -*- import urllib2 import string import re class Ba ...

  5. (Lineup the Dominoes筛子)三维状压

    传送门 描述:\(一堆筛子,每个筛子两个面,上面有1-6之间的数字.后一个筛子与前一个筛子的接触面的点数必须相等.\) \(求,有多少种方案堆完筛子.(方案只关心筛子的位置,不关心是否翻转)\) \( ...

  6. java基础篇 之 foreach探索

    我们看下这段代码: public class Main { public static void main(String[] args) { List list = new ArrayList(); ...

  7. k近邻法(一)

    简介 k近邻法(k-nearest neighbors algorigthm) 是一种最基本的用于分类和回归的方法之一,当没有关于训练数据的分布时,首先最容易想到的就是采用k近邻法. k近邻法输入为实 ...

  8. 自动化测试po模式是什么?自动化测试po分层如何实现?-附详细源码

    一.什么是PO模式 全称:page object model  简称:POM/PO PO模式最核心的思想是分层,实现松耦合!实现脚本重复使用,实现脚本易维护性! ​ 主要分三层: 1.基础层BaseP ...

  9. 设计模式之GOF23桥接模式

    桥接模式 当一个问题违反单一职责原则时,及控制该产品的有多个维度,为了扩展时减少类的膨胀个数,可以选用桥接模式 避免多重继承时 例如买电脑时                  桥接模式

  10. 黑马程序员_毕向东_Java基础视频教程——三元运算符(随笔)

    三元运算符:三个元素参与运算的符号 [三元运算符:简略版的 if(){} else() {}语句] class Text { public static void main(String[] args ...