再说闭包——鞭尸愚蠢的自己:(

2019-08-15 admin

因为自己的网站还没弄好能被百度引擎搜索到,所以转载到SF上

原文链接:https://kiznaiver1998.cn/2019…

前情提要

之前裸面北森实习遭遇惨败…闭包本来是自己仔仔细细看过几次的内容也没答好,经过自己仔细的感悟,然后看各大佬的资料,终于又有了一些新的体会(看了之前说的闭包…感觉自己真是一个睿智…🐷)

什么是闭包

之前我一直听标准答案是:

函数记住并访问其所在的词法作用域,叫做闭包现象,而此时函数对作用域的引用叫做闭包。

什么是词法作用域

  • JavaScript 采用词法作用域 -> 函数的作用域在函数定义的时候就决定了。
  • 当然,与之相对的还有动态作用域 -> 函数的作用域是在函数调用的时候决定了。

下面有个经典的面试题用于理解 JavaScript 词法作用域的特性:

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

bar();
//结果为1
//因为词法作用域,函数的作用域在函数定义就决定了,所以foo的value只能在foo定义的作用域向上查找,找到全局变量value = 1    

利用这个特性,我们在 foo 外再加一个函数。因为词法作用域的关系,foo 的 value 从 foo 定义的作用域向上查找,就能找到 fn 的 value 为 3.

var value = 1;
function fn(){
  var value = 3;
  function foo() {
    console.log(value);
  }
  foo();
}
function bar() {
    var value = 2;
    fn();
}    
bar();//3

通过 chrome 调试,我们可以看到运行过程大致为这样

//bar执行上下文入栈
value声明为undefined
value赋值为2
//fn执行上下文入栈
value声明为undefined(这里因为词法作用域的关系,在fn的作用域是没有value的)
value赋值为3
//可以看到结果,fn是一个闭包,产生的引用是对value: 3的引用
Closure(fn) value: 3

所以之前我看到《实战 ES2015》上说的闭包的原理是利用高阶函数的特性(函数里面可以声明函数),产生穿越作用域的引用(就是说 foo 函数能向上查找)

什么是执行上下文

虽说上面已经说了,闭包的产生是因为词法作用域,但是如果了解了执行上下文,才能感受到“记住”的特性。

var value = 1;
function fn(){
  var value = 3;
  function foo() {
    console.log(value);
  }
  return foo;
}
function bar() {
    var value = 2;
    return fn();
}    
var result = bar();
result();//3

对于这个例子,这个函数的执行上下文栈会经过以下过程

ECStack.push(globalContext)//全局global入栈
ECStack.push(barContext)//bar被执行入栈
ECStack.push(fnContext)//fn入栈
ECStack.pop(fnContext)//fn出栈
ECStack.pop(outContext)//bar出栈
ECStack.push(result)//result入栈
ECStack.pop(result)//result出栈
ECStack.pop(globalContext)//global出栈
/* 以上过程都可以使用调试通过Scope看到 */

可以看到 result 执行上下文入栈的时候,fn 已经出栈了,但是 result 仍然能够“记住” fn 的 value 值。所以,通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量。

鞭尸环节

来源:https://www.cnblogs.com/kizna…

之前一直觉得这个闭包没啥好说的,实际…当时根本看闭包的视角不一样

for (var i = 1 ; i <= 5 ; ++i) {
    (function(i){
        setTimeout( function timer(){
            console.log(i);
        } , i*1000);
    })(i);
}

​ 从现在自己的视角来看这里呢,因为 timer 在匿名的立即执行函数中定义,所以对于词法作用域来说,timer 能够向上访问到匿名函数的 i。而我们知道 setTimeout 作为任务分发器,它的第一个参数(也是回调函数)会进入任务队列,而当它执行的时候,即使匿名函数的执行上下文已经出栈,但它依然能够访问 i,从而产生了闭包。

闭包的作用

  • 函数柯里化
  • 延长变量生命周期
  • 创建模块(实现面向对象的公有方法抛出私有变量的设计)

[转载]原文链接:https://segmentfault.com/a/1190000020080950

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-72569.html

文章标题:再说闭包——鞭尸愚蠢的自己:(

相关文章
如果我实现了自己的OS,我算开发者中的精英吗?
相信有很多Linux爱好者心中都怀有编出一个自己的操作系统的理想,一位外国网友在quora上提出了这个问题。本期#linuxstory 说#节目翻译节选了一个令人启迪的回答,让我们从另一个角度思考个人重新发明轮子的意义。以下是网友的答案: ...
2015-12-25
JavaScript 闭包深入理解(closure)
一、什么是闭包? “官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。 相信很少有人能直接看懂这句话,因为他描述的太学术。其实这句话通俗的来说就是:JavaScript...
2017-03-23
javascript 闭包详解
看了一下网上闭包的概念及文章,对于这个问题,自己做一个梳理吧。 问:闭包是什么? 答:闭包是指在 JavaScript 中,内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。 这个是我自身第一...
2017-03-26
javascript基本包装类型介绍
为了便于操作基本类型值,ECMAScript 提供了 3 个特殊的引用类型:Boolean、Number和 String。这些类型与其他引用类型相似,但同时也具有与各自的基本类型相应的特殊行为。实际上,每当读取一个基本类型值的时候,后台就会...
2017-03-22
JS实现定时自动关闭DIV层提示框的方法
本文实例讲述了JS实现定时自动关闭DIV层提示框的方法。分享给大家供大家参考。具体分析如下: 这里用JS设定时间去控制指定ID的DIV层是否显示,可以实现一个自动关闭的提示框,时间一到,马上关闭,这样会使你的网页更人性一点,代码其实比你想像...
2017-03-23
浅谈js 闭包引起的内存泄露问题
在js闭包中,可以定义“局部变量”,但是外部去调用的话,尤其是反复调用赋值,会造成内存的大量开销。如何防止这种现象的发生?关于闭包还有没有类似的内存或效率问题需要注意?如何去规避? 内存问题可能是如下原因造成: 1. 循环引用导致了内存泄漏...
2017-03-24
自己编写的支持Ajax验证的JS表单验证插件
自己编写了一个表单验证插件,支持ajax验证,使用起来很简单。 每个需要验证的表单元素下面有一个span标签,这个标签的class有一个valid表示需要验证,如果有nullable则表示可为空;rule表示验证规则,msg表示错误提示信息...
2017-03-23
JavaScript包装对象使用详解
JavaScript对象是一种复合值:它是属性和已命名值的集合。通过&quot;.&quot;符号来引用属性值。当属性值是一个函数时,称为方法。 ①一段你常用但却未必明白其真正底层原理的代码: var s = &quot;hello wor...
2017-03-27
JavaScript 匿名函数和闭包介绍
匿名函数:没有名字的函数; 闭包:可访问一个函数作用域里的变量的函数; 一 匿名函数 &#x2F;&#x2F; 普通函数 function box(){ &#x2F;&#x2F; 函数名是box; retu...
2017-03-22
配置webpack中dev.env.js、prod.env.js,解决不同命令下项目启动和打包到指定的环境
前后端分离的项目开发中,我们有开发环境、测试环境、预生产环境和生产环境。 1、开发环境下调试接口的时候,一般都会有好几个接口地址(开发服务器上的,本地的,接口开发人员的,七七八八的接口地址),要根据情况手动来切换接口地址。 2、打包时要部...
2018-09-08
回到顶部