你不知道的JavaScript(上)3

121 阅读4分钟

函数作用域和块作用域

我司因工作需求,把我这个实习生的戴尔换成了Mac,属实有点兴奋。今天本来休息,但本着珍惜时间的想法,还是来公司充实一下自己,整个公司就我一个人,特别安静,就我一个人键盘声,哈哈。用了两个小时配置了基础的开发环境,又回顾了一下第三章,吃饭前写一篇笔记。

1.函数中的作用域和隐藏内部实现

我认为这部分的关键有以下几点:

  • javaScript具有基于函数的作用域,这是毋庸置疑的,这也意味着每声明一个函数都会为自身创建一个气泡(词法作用域中提到过)。
  • 对函数的传统认知就是先声明一个函数,然后再向里面添加代码。但是我们如果逆向思维去思考,会有不一样的启示:我们从所写的代码中挑出了一个任意的片段,然后用函数声明对它进行包装,实际上就是把这些代码“隐藏”起来了。(外部函数永远无法获取内部函数定义的变量,内部函数将其变量私有化。)
  • 规避冲突,防止污染。
    • 全局命名空间
      var eatFood = {
          food : 'apple',
          eatIt(){
              // ...
          },
          getFood(){
              // ...
          }
      }
      
      通常我们创建一个对象,这个对象便是一个命名空间,我们可以在其中写一些方法,之后点调用即可。
    • 模块化开发

2.函数作用域

```
var a = 2;
function foo(){
    var a = 3;
    console.log(a) //3
}
console.log(a) // 2
foo()
```
在上述代码中,我们利用函数作用域的特性,实现了“隐藏”了foo函数中的a属性,但是,
我们必须用函数将其包裹,给定一个函数名,之后在调用它。如果函数不需要函数名,
并且能够自动运行,那就太理想啦!而JS为我们提供了解决方案————立即执行函数。
  • 函数表达式是啥玩意儿?

    个人理解:如果声明中function是第一个单词,那就是函数声明,否则就是函数表达式

      (function foo(){ //... }) 函数表达式
    
      +function foo(){ //... }  函数表达式
      
      function(){ //... }  函数声明
    

    务必区分函数声明和函数表达式,这是十分重要的

  • 具名和匿名

    • 匿名函数表达式

      (function(){ //... })

      注意点:函数表达式可以匿名,而函数声明坚决不可以!

    • 匿名的优缺点:

      优点:

      • 不需要为函数名创建一个变量,不会“污染”所在作用域。

      缺点:

      • 匿名函数在栈中不会显示出有意义的函数名,很难调试与调用。
      • 如果没有函数名,当函数需要引用自身时只能使用arguments.callee。
      • 被省略的函数名往往可以大幅增加代码可读性。

      始终给函数表达式命名是一个最佳实践

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

    写法: (function(){ //... }(param)) 或者 (function(){ //... })(param)

3.块作用域

  • 啥是块作用域呢???
{
    //...
}
{
    //...
}
这俩玩意儿,就是俩单独的块作用域!
  • 其实在ES6到来之前呢,JS已经有了使用块作用域的情景。

    with和try/catch

with在之前我们讨论过它,它可以从对象中创建出一个新的作用域,而这个作用域仅仅在with声明中有效。

try{ ... }catch(error){ a️ }
说出来你可能不相信,如果你能捕获到error,那么这个error只能在之后的{}中使用,在其他地方,你根本获取不到!

  • let
随着ES6的到来,引入了新的let关键字,提供了除了var以外的另一种变量声明的方式。
这个let,特殊的很,它可以为其声明的变量劫持(识别)所在块级作用域,但是,let声明的变量不会进行提升,并且在同一作用域中不能重复声明同一个变量,之后我们会讨论它的更多特性,不要着急。
{
    var a = 'a';
    let b = 'b';
}
console.log(a) // 'a'
console.log(b) // b is not defined
var声明的变量无法识别块级作用域,而let声明的变量是ok的。
当for循环碰到let,会发生什么呢?
for(let i = 0;i<10;i++){
    let i = 1  // 不会报错
    // 在for循环中,存在父子作用域
    // for循环头部的let不仅将i绑定到for循环的块中
    // 事实上,它将其重新绑定到循环的每一个迭代中
}
下面通过另一种方式来说明每次迭代时进行重新绑定的行为:
{
    let j;
    for(j=0;j<10;j++){
        let i = j;
        console.log(i)
    }
}
  • const

除了let之外,ES6还引入了另一种声明变量的方式:const,同样可以用来创建块作用域变量,但它的值是固定的,准确的来说,地址是固定的。

第三章 End

一写就停不下来,赶快吃饭去了,饿死啦!