javascript如何定义闭包

  

这篇文章主要介绍javascript如何定义闭包,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

前言

闭包 永远都是前端开发者绕不过去的一个坎,不管你喜欢与否,在工作和面试中,都会遇到。每个人对闭包的理解都不尽相同,这里笔者谈谈自身对闭包的理解。(如果与您的理解有出入,请以您自己为准  )

如何定义闭包

在给出定义之前,不妨看看别人是如何定义闭包的:

函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包” -- JavaScript权威指南(第六版)

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。 -- JavaScript高级程序设计(第三版)

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。 -- 你不知道的JavaScript(上卷)

虽然上面的几段话描述起来并不一样,但是您细细品味后还是能找出一些共同点。其中最重要的是不同作用域之间的联系。当然了,您可以直接引用上面的定义(毕竟上面几个定义还是比较权威的),这里笔者比较喜欢最后一段的定义,同时力推《你不知道的JavaScript(上卷)》这本书,值得反复细读。

闭包涉及哪些知识点

光给出定义是远远不够的,还必须探讨内部涉及了哪些知识点。下面是笔者认为有用到的知识点。

作用域与作用域链

嗯,其实笔者知道你们都想到了这点(不会吧,不会有人没想到这点吧)。既然大家都了解作用域。这里就简单描述一下,过一下场即可。

作用域:根据名称查找变量的一套规则。分为三种类型:全局作用域;函数作用域;块作用域。

需要注意的是块作用域,ES6新增的规范。 在花括号{}里面使用let,const定义的变量,都会绑定到该作用范围内,花括号以外的地方无法访问。注意:在花括号开始 到 let变量声明之前,存在暂时性死区(该点不在本文讨论范围)。

作用域链:当不同的作用域 (混~淆~在~一~起~ 呸,不小心出戏了) 圈套在一起时,就形成了作用域链。注意的是,查找方向是从内到外的。

为什么作用域的查找方向是从内到外的呢?这是个很有趣的问题。个人觉得是跟js执行函数的入栈方式决定的(感觉有点偏题了,有兴趣的小伙伴可以去查一下资料)。

词法作用域

函数之所以 可以访问另一个函数作用域的变量(或者说记住当前的作用域并在当前以外的地方访问)的关键点就是词法作用域在起作用。这一点很重要,但不是所有人都知道这个知识点,这里简单探讨一下。

在编程界中,存在两种作用域工作模式,一种是被大多数编程语言所采用的词法作用域;另一种就是与其相反的动态作用域(这个不在本文的讨论范围)。

词法作用域: 变量和块的作用域 在 您编写代码的阶段 就已经确定好了,不会随着调用的对象或者地方的不同而改变(感觉跟this相反)。

要不,举个栗子看看吧:

让=1;函数fn(){让=2;函数fn2 () {console.log(一个);
  }返回fn2;
  }让fn3=fn ();
  fn3();复制代码

从上面的定义可以知道,<代码> fn> fn3> fn2> fn3> fn2> 变量,根据作用域链的查找规则,找到的是<代码> fn> ,所以最终的输出是2,不是1。(可以看下图)

题外话,如何欺骗词法作用域?

虽然词法作用域是静态的,但依然有办法可以欺骗它,达到动态的效果。

第一种方法是使用<强> eval 强。eval可以把字符串解析成一个脚本来运行,由于在词法分析阶段,无法预测eval运行的脚本,所以不会对其进行优化分析。

第二种方法是<强> 强。与通常被当作重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身;本身比较难掌握,使用不当容易出现意外情况(如下例子),不推荐使用。——

javascript如何定义闭包