函数作用域和块作用域

1,263 阅读4分钟

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

函数作用域和块作用域

函数中的作用域

考虑下面代码:

function foo(a){
    var b = 2;
    // 一些代码
    function bar(){
        // ...
    }
    // 更多的代码
    var c = 3;
}

由于标识符a、b、c和bar都附属于foo(...),因此都无法从foo(...)的外部对它们进行访问

bar();//失败
console.log(a,b,c);//全都失败

函数作用域的含义是指:属于这个函数的全部变量都可以在整个函数的范围内使用及复用(事实上在嵌套的作用域中也可以使用)。能充分利用JavaScript变量可以根据需要改变值类型的“动态”特性

隐藏内部实现

最小授权或最小暴露原则:是指在软件设计中,应该最小限度的暴露必要内容,而将其他内容都“隐藏”起来,比如某个模块或对象的API设计

function doSomething(a) {
   b = a + doSomethingElse(a * 2);
   console.log(b * 3);
}

function doSomethingElse(a) {
   return a - 1;
}

var b;
doSomething(2);//15

内部私有化

function doSomething(a) {
   function doSomethingElse(a) {
      return a - 1;
   }
   var b;
   b = a + doSomethingElse(a * 2);
   console.log(b * 3);
}
doSomething(2);//15

函数作用域

区分函数声明和表达式最简单的方法是看function关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。如果function是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。它们最重要的区别就是它们的名称标识符将会绑定在何处。

匿名和具名

1、函数表达式可以使匿名的而函数声明则不可以省略函数名 2、匿名函数表达式的几个缺点

  • 匿名函数在栈追踪中不会显示出有意义的函数名,使得调试很困难(不方便调试
  • 如果没有函数名,当函数需要引用自身时只能使用已经过期的arguments.callee引用,比如在递归中。另一个函数需要引用自身的例子,是在事件触发后事件监听器需要解绑自身。(引用自身复杂
  • 匿名函数省略了对于代码可读性/可理解性很重要的函数名。一个描述性的名称可以让代码不言自明。(降低了代码的可读性

行内函数表达式非常强大且有用,始终给函数表达式命名是一个最佳实践

立即执行函数表达式(IIFE)

三种用法:

  • 把它们当做函数调用并传参进去

var a = 2;
(function IIFE(global) {
   var a = 3;
   console.log(a);//3
   console.log(global.a);//2
})(window)
console.log(a);//2
  • 解决undefined标识符的默认值被错误覆盖导致的异常(虽然不常见)
undefined = true;//绝对不要这样做
(function IIFE(undefined) {
  var a;
  if (a === undefined) {
    console.log("Undefined is safe here");
  }
})();
  • IIFE还有一种变化的用途是倒置代码的运行顺序,将需要运行的函数放在第二位,在IIFE执行之后当做参数传递进去
(function IIFE(def) {
  def(window);
})(function def(global) {
  var a = 3;
  console.log(a);//3
  console.log(global.a);//2
})

块作用域

块作用域是一个用来对之前的最小授权原则进行扩展的工具,将代码从在函数中隐藏信息扩展为在块中隐藏信息。

1、with with是块作用域的一种形式,用with从对象中创建出的作用域仅在with声明中而非外部作用域中有效.

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

try {
  undefined();//执行一个非法操作来强制制造一个异常
} catch (err) {
  console.log(err);//能够正常执行
}
console.log(err);//err not found

err仅存在catch分句内部,当试图从别处引用它时会抛出错误

3、let let关键字可以将变量绑定到所在的任意作用域中(通常是{..}内部),let为其声明的变量隐式地了所在的块作用域。

4、const 同样可以用来创建作用域变量,但其值是固定的(常量)