头疼的闭包

头疼的闭包

一.官网的解释

闭包(closure)是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

闭包就是一个函数把外部的那些不属于自己的对象也包含(闭合)进来了

 ·.· 在js中,只有函数内部的子函数才能读取局部变量

 ·.· 闭包就是能够读取其它函数内部变量的函数

 .·.闭包是个子函数

   在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

 二.如何理解

变量的作用域 

Js中变量的作用域分两种:全局变量和局部变量。

(1)函数内部可以直接读取全局变量。  

var n = ‘叼你啦!死机‘;
function f1() {
     console.log(n);
}
f1();

(2)在函数外部自然无法读取函数内的局部变量。

function f1() {
var n = ‘叼你啦!死机‘;

}
 console.log(n); //error

此外,函数内部声明变量时,一定要用var关键字来命名变量。否则,就声明了一个全局变量。

当我们需要从外部读取函数内的局部变量,可以在函数的内部再定义一个函数。

function f1(){
     n = ‘叼你啦!死机‘;
     function f2(){
       console.log(n);
     }
 }

如上,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2是可见的。(重点一)

这就是Javascript语言特有的“链式作用域”结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。

function f1(){
    n = ‘叼你啦!死机‘;
    function f2(){
        console.log(n);
    }
    return f2;
}
var result=f1();
result(); // 叼你啦!死机‘

如上,既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们就可以在f1外部读取它的内部变量(重点二)

三.用途

1.读取函数内部的变量

2.令这些变量的值始终保持在垃圾(内存)回收机制中。

(或者这样理解:让这些变量的值始终保持在内存中,不会在f1调用后被自动清除)

function f1(){
    var n = ‘叼你啦!死机‘;
    add=function(){
        n = ‘你知道就好‘;
    };
    function f2(){
        console.log(n);
    }
    return f2;
}
var result=f1();
result(); // 叼你啦!死机
add();
result(); // 你知道就好

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次是:叼你啦!死机,第二次是:你知道就好。

换另外一个例子会直接说明点

 function f1(){
    var n=999;
    add=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  add();
  result(); // 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。(重点三)

为什么会这样呢?

原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

前面两个例子中:在add前面没有使用var关键字,因此add是一个全局变量,而不是局部变量。其次,add的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以add相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

引用其他文章

作者:大水
链接:https://www.zhihu.com/question/34547104/answer/59515735
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

每次定义一个函数,都会产生一个作用域链(scope chain)。当JavaScript寻找变量varible时(这个过程称为变量解析),总会优先在当前作用域链的第一个对象中查找属性varible ,如果找到,则直接使用这个属性;否则,继续查找下一个对象的是否存在这个属性;这个过程会持续直至找到这个属性或者最终未找到引发错误为止

看个简单版的例子:

(function(){
    var hello="hello,world";
    function welcome(hi){
        alert(hi);        //解析到作用域链的第一个对象的属性
        alert(hello);    //解析到作用域链的第二个对象的属性
    }
    welcome("It‘s easy");
})();

运行结果很简单,一个弹窗It‘s easy.一个弹窗hello,world。
分析过程如下:
对于函数welcome(),定义welcome的时候会产生一个作用域链对象,为了表示方便,记作scopechain。scopechain是个有顺序的集合对象。

  • scopechain的第一个对象:为了方便表示记作sc1, sc1有若干属性,引用本函数的参数和局部变量,如sc1.hi ;
  • scopechain的第二个对象:为了方便表示记作sc2,sc2有若干属性,引用外层函数的参数和局部变量,如sc2.hello;
  • ...
  • scopechain的最后一个对象:为了方便表示记作scn,scn引用的全局的执行环境对象,也就是window对象!,如scn.eval();

这里之所以可以弹出hello,world,原因就是变量解析时在welcome函数作用域链的第一个对象上找不到hello属性,然后就去第二个对象上找去了(结果还真找到了)。

所以,JavaScript中的所谓的高大上的闭包其实很简单,根本上还是变量解析。而之所以可以实现,还是因为变量解析会在作用域链中依次寻找对应属性的导致的。

时间: 2024-10-26 01:24:50

头疼的闭包的相关文章

WEB前端笔试题(2)——狼厂

1.CSS属性position的属性值有哪些,描述他们的作用 值 描述 absolute 绝对定位,相对于static定位以外的第一个父级元素进行定位 元素的位置通过left right top bottom属性进行规定 fixed 绝对定位,相对于浏览器进行定位 元素的位置通过left right top bottom属性进行规定 relative 相对定位,相对于其他正常位置(即不用relative时在正常流中的的位置) 默认值 没有定位,元素出现在正常流中 忽略left right top

原生js简单轮播图 代码

在团队带人,突然被人问到轮播图如何实现,进入前端领域有一年多了,但很久没自己写过,一直是用大牛写的插件,今天就写个简单的适合入门者学习的小教程.当然,轮播图的实现原理与设计模式有很多种,我这里讲的是用面向过程函数式编程去实现,相对于面向对象设计模式,代码难免会显得臃肿冗余.但没有面向对象的抽象却很适合新手理解与学习.已经在BAT的同学看到希望少喷点.另外可以多提意见. 轮播图的原理: 一系列的大小相等的图片平铺,利用CSS布局只显示一张图片,其余隐藏.通过计算偏移量利用定时器实现自动播放,或通过

Javascript学习日志(三):闭包

说实话,前面一节的原型和原型链在当初学的时候并没有很头疼,对着高级编程第三版撸了几遍就理解透了,闭包这一节真的挺头疼的,很惭愧,看了差不多十来遍吧,还翻看了网上的其他博客和解释文档,五花八门的表达方式,虽然核心思想都一致,但是实在是不能做到自己的理解,后来结合函数作用域链,好不容易有点开窍,趁着热乎劲儿,赶紧写下来,感兴趣的可以参考一下. 闭包:高级编程上面的解释是指有权访问另一个函数作用域中的变量的函数,(是一个函数): 创建闭包的常见方式,就是在一个函数内部创建另一个函数. 在理解闭包之前,

说说Python中的闭包 - Closure

转载自https://segmentfault.com/a/1190000007321972 Python中的闭包不是一个一说就能明白的概念,但是随着你往学习的深入,无论如何你都需要去了解这么一个东西. 闭包的概念 我们尝试从概念上去理解一下闭包. 在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包.闭包可以用来在一个函数与一组"私有"变量之间创建关联关系.在给定函数被多次调用的过程中,这些私有变量能够保持其持久性.-- 维基百科)

举例详细说明javascript作用域、闭包原理以及性能问题(转)

这可能是每一个jser都曾经为之头疼的却又非常经典的问题,关系到内存,关系到闭包,关系到javascript运行机制.关系到功能,关系到性能. 文章内容主要参考自<High Performance JavaScript>,这本书对javascript性能方面确实讲的比较深入,大家有空都可以尝试着阅读一下,我这里有中英电子版,需要的话QQ317665171或者QQ邮箱联系. 复习,笔记,更深入的理解. 欢迎拍砖指正. 作用域: 下面我们先搞明白这样几个概念: 函数对象的[[scope]]属性.S

关于golang闭包

闭包不是必报,睚眦必报,这种事咱不干,咱要干的是程序上所谓的闭包. 在讲闭包之前呢?我们先看一个程序 func add(a, b int) int { return a + b } 乍一看,就感觉想骂人,这是啥?这是在考验我的智商?不,咱是那意思嘛,消消气,先听我说,此功能就是两个数相加得到和,但是我们要用它干一票大的!玄机来了,怎么做,我给他传一个数字,等我想用时,我再传入一个数字,和就出来了! 听起来是不是感觉牛x了 好,开始额的表演..... 我们先这样做 func fadd(a, b,

Python 闭包函数

一.定义: 1. 定义在函数内部的函数 2. 包含对外部作用域名字的引用,而不是对全局作用域名字的引用那么该内部函数就称为闭包函数 x=1 def f1(): x=11111111111 def f2(): print(x) return f2 func=f1() 二.闭包函数的应用:惰性计算 def index(url): # url='https://www.python.org' def get(): # return requests.get(url).text print(reques

学习Javascript闭包(Closure)

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 下面就是我的学习笔记,对于Javascript初学者应该是很有用的. 一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量. var n=999; function f1(){ alert(n); } f1(); // 999 另一方面,在函数外

Python 闭包

闭包 1.注意:返回的函数内部不要使用后续会发生变化的变量. def f(): gs = [] for k in range(1, 4): def g(i): return i + k gs.append(g) return gs 例如这段代码感觉上应该返回三个函数分别return i+1.return i+2.return i+3(i为新函数参数),但事实却是得到了三个return i+3. >>> from test import f >>> g1, g2, g3