【JS】函数作用域和块作用域

170 阅读3分钟

标识符(变量、函数)

  1. 函数作用域

    属于这个函数的全部变量,都可以在整个函数的范围内使用及复用。(在嵌套的作用域中也可以使用)

    JavaScript变量可以根据需要改变值类型的“动态”特性。

  2. 隐藏内部实现

    可以把变量和函数包裹在一个函数的作用域中,然后用这个作用域来“隐藏”他们。

    (最小特权原则/最小授权/最小暴露原则)

    规避冲突

    隐藏作用域可以避免同名标识符之间的冲突。

    1. 全局命名空间

      第三方库:在全局作用域中声明一个足够独特的变量,通常是一个对象。这个对象被用作库的命名空间。

      所有需要暴露给外界的功能都会成为这个对象的属性,而不是将自己的标识符暴露在顶级的词法作用域。

    2. 模块管理

      使用模块管理器,通过依赖管理器的机制将库的标识符显式的导入到另外一个特定的作用域中。

  3. 函数作用域

    //标准的函数声明,foo被绑定在所在作用域中
    function foo(){
    	...
    }
    //函数表达式,foo被绑定在函数表达式自身的函数中而不是所在作用域中,不会飞必要的污染外部作用域
    (function foo(){
    	...
    })()
    //第一个()将函数变成表达式,第二个()执行了这个函数
    //函数表达式
    (function foo(){
    	...
    }())
    

    function关键词出现在声明中的位置,如果function是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。

    两者的区别是:它们的名称标识符将会绑定在何处。

    1. 匿名和具名

    匿名函数表达式(函数声明不可以省略函数名)

    setTimeout(function(){
    ...
    },1000)
    

    匿名函数表达式缺点:

    • 匿名函数在栈追踪中不会显示出有意义的函数名,使得调试很困难。

    • 如果没有函数名,当函数需要引用自身时,只能使用已经过期的arguments.callee引用。例:在递归中,或者事件触发后事件监听器需要解绑自身。

      calleearguments 对象的一个属性。它可以用于引用该函数的函数体内当前正在执行的函数。这在函数的名称是未知时很有用,例如在没有名称的函数表达式 (也称为“匿名函数”)内。

    • 匿名函数省略了对于代码的可读性/可理解性很重要的函数名。

    1. 立即执行函数表达式(IITE)
      • 可把他们当作函数调用,并传递参数进去。
      • 解决undefined 标识符的默认值被错误覆盖导致的异常。
      • 倒置代码的运行顺序。
  4. 块作用域

    当使用var声明变量时,最终都会属于外部作用域。

    1. with

      用with从对象中创建出的作用域仅在with声明中而非外部作用域中有效。

    2. try/catch

      catch分句会创建一个块作用域,其声明的变量仅在catch内部有效。

    3. let

      可将变量绑定到所在的任意作用域。(例:某个代码块通常是{...}内部)

      可为变量显式声明块作用域。

      使用let进行的声明不会在块作用域中进行提升。声明的代码被运行前,声明并不存在。

      1. 垃圾收集

        块作用域可以让引擎知道是否需要回收内存垃圾。

      2. let循环

        当代码中存在对于函数作用域中var声明的隐式依赖时,用let来代替var则需要在代码重构的过程中付出额外的精力。

    4. const

      可创建块作用域变量,但其值是固定的,之后任何试图修改值的操作都会引起错误。