JavaScript作用域和闭包

作用域

JavaScript的作用域限定了你可以访问哪些变量。有两种作用域:全局作用域,局部作用域。

工具/原料

  • JavaScript

方法/步骤

  1. 1

    全局作用域

    在所有函数声明或者大括号之外定义的变量,都在全局作用域里。

    不过这个规则只在浏览器中运行的JavaScript里有效。

  2. 2

    可以在全局作用域定义变量,并不推荐这样做。因为可能会引起命名冲突,两个或更多的变量使用相同的变量名。在定义变量时使用了const或者let,那么在命名有冲突时,就会收到错误提示。这是不可取的。

  3. 3

    所以,应该尽量使用局部变量,而不是全局变量

    局部作用域

    在代码某一个具体范围内使用的变量都可以在局部作用域内定义。这就是局部变量。

    JavaScript里有两种局部作用域:函数作用域和块级作用域。

    从函数作用域开始。

    函数作用域

    在函数里定义一个变量时,它在函数内任何地方都可以使用。在函数之外,就无法访问它了。

    比如下面这个例子,在sayHello函数内的hello变量:

  4. 4

    块级作用域是函数作用域的子集,因为函数是需要用大括号定义的,(除非你明确使用return语句和箭头函数)。

    函数提升和作用域

    当使用function定义时,这个函数都会被提升到当前作用域的顶部。因此,下面的代码是等效的:

  5. 5

    因为这里有两个变量,函数提升可能会导致混乱,因此就不会生效。所以一定要在使用函数之前定义函数。

    函数不能访问其他函数的作用域

    在分别定义的不同的函数时,虽然可以在一个函数里调用一个函数,但一个函数依然不能访问其他函数的作用域内部。

    下面这例,second就不能访问firstFunctionVariable这一变量。

  6. 6

    嵌套作用域

    如果在函数内部又定义了函数,那么内层函数可以访问外层函数的变量,但反过来则不行。这样的效果就是词法作用域。

    外层函数并不能访问内部函数的变量。

  7. 7

    如果把作用域的机制可视化,可以想象有一个双向镜(单面透视玻璃)。能从里面看到外面,但是外面的人不能看到你。

  8. 8

    函数作用域就像是双向镜一样。可以从里面向外看,但是外面看不到你。

    嵌套的作用域也是相似的机制,只是相当于有更多的双向镜。

  9. 9

    多层函数就意味着多个双向镜。

    理解前面关于作用域的部分,就能理解闭包是什么了。

    闭包

    在一个函数内新建另一个函数时,就相当于创建了一个闭包。内层函数就是闭包。通常情况下,为了能够使得外部函数的内部变量可以访问,一般都会返回这个闭包。

  10. 10

    因为闭包可以访问外层函数的变量,因此他们通常有两种用途:

    减少副作用

    创建私有变量

    使用闭包控制副作用

    当你在函数返回值时执行某些操作时,通常会发生一些副作用。副作用在很多情况下都会发生,比如Ajax调用,超时处理,或者哪怕是console.log的输出语句:

  11. 11

    当使用闭包来控制副作用时,实际上是需要考虑哪些可能会混淆代码工作流程的部分,比如Ajax或者超时。

    要把事情说清楚,还是看例子比较方便:

    比如说你要给为你朋友庆生,做一个蛋糕。做这个蛋糕可能花1秒钟的时间,所以写了一个函数记录在一秒钟以后,记录做完蛋糕这件事。

    为了让代码简短易读,使用了ES6的箭头函数:

  12. 12

    但这里的问题是,并不想立刻知道蛋糕的味道。只需要知道时间到了,蛋糕做好了就行。

    要解决这个问题,可以写一个prepareCake的功能,保存蛋糕的口味。然后,在返回在内部调用prepareCake的闭包makeCake。

    从这里开始,就可以在你需要的时调用,蛋糕也会在一秒后立刻做好。

  13. 13

    这就是使用闭包减少副作用:可以创建一个任你驱使的内层闭包。

    私有变量和闭包

    前面已经说过,函数内的变量,在函数外部是不能访问的既然不能访问,那tc 就可以称作私有变量。

    确实是需要访问私有变量的。这时候就需要闭包的帮助了。

  14. 14

    这个例子里的saySecretCode函数,就在原函数外暴露了secretCode这一变量。因此,它也被成为特权函数。

    使用DevTools调试

    Chrome和Firefox的开发者工具都能很方便的调试在当前作用域内可以访问的各种变量一般有两种方法。

    第一种方法是在代码里使用debugger关键词。这能让浏览器里运行的JavaScript的暂停,以便调试。

    下面是prepareCake的例子:

  15. 15

    打开Chrome的开发者工具,定位到Source页下(或者是Firefox的Debugger页),你就能看到可以访问的变量了。

  16. 16

    使用debugger调试prepareCake的作用域。

    也可以把debugger关键词放在闭包内部。注意对比变量的作用域:

  17. 17

    通过断点调试作用域

    闭包和作用域并不是那么难懂。一旦使用双向镜的思维去理解。

    在函数里声明一个变量时,只能在函数内访问。这些变量的作用域就被限制在函数里了。

    如果你在一个函数内又定义了内部函数,那么这个内部函数就被称作闭包。它仍可以访问外部函数的作用域。

  • 发表于 2018-02-06 00:00
  • 阅读 ( 720 )
  • 分类:其他类型

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
联系我们:uytrv@hotmail.com 问答工具