JavaScript学习日记 : Day8 - 作用域(Scope)

作用域即函数或变数的可见区域,白话点就是,函数或变数不在这个区域内,就无法获取到。

1. 函数作用域

用函数形式 function() {…}类似的代码包起来的部分,即函数作用域。

与函数作用域相对应的概念是全局作用域,也就是定义在最外层的变数或函数,可以在任何地方访问他们。

let a = 123 // 全域作用域

function func() {
    var b =  456
    console.log(a) 
}

console.log(a) // apple

console.log(b) // Uncaught ReferenceError: b is not defined

func() //123

另一个例子 :

// 全域作用域

function func() { //作用域A
    let a = "coffee"
    
    function func1() { //作用域B
        let a = "apple"
        let b = "banana"
        // 这里可放许多要对外隐藏的变数
        
        console.log(a);
    }
    
    console.log(a) // coffee
    console.log(b) // Uncaught ReferenceError: b is not defined
    func1() //apple
}

func();

等於有外层函数func的作用域A内嵌了函数func1的作用域B。在func里面的console.log(a)访问变数a时,JS引擎会先从离自己最近的作用域A查找变量a,找到就不再继续查找,找不到就去上层作用域(此例中上层作用域是全域作用域)继续查找,此例中a已经找到且值为"coffee",所以打印输出coffee。依此类推,执行func1(),会执行func1函数内部的console.log(a),随即会在作用域B查找里面a,而作用域B里面存在一个a的声明和赋值语句let a = “apple” ,所以最先找到a的值是apple,找到便不再继续查找,最终func1()输出apple而不是coffee。

图解上面程序码的作用域:

2. ES6带来的Object Scope

在{}内用let关键字声明的变数与函式(表达式)属於块级作用域(Object scope)。

但是这对ES6以前的代码显然产生很大的影响,出於兼容性的问题,在块级声明的函数依然可以在外部取用,如果需要函数只在块级中取用,应该使用let关键字写成函数表达式。以下例子:

 function test() {
        {
            function inner() {
                alert('inner function')
            } 
        }
        inner()
    }
    
    test() // inner function

上面例子证明JS引擎为了兼容在ES6实现中做了变通的处理,在看以下例子:

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar); // Foo Bar

  {
    var moo = "Mooo"
    let baz = "Bazz";
    console.log(moo, baz); // Mooo Bazz
  }

  console.log(moo); // Mooo
  console.log(baz); // ReferenceError
}

run();

上面的例子用let声明的函数,才是真正的块级作用域。

3. 为什麽要新增Object scope?

3.1 var声明存在副作用 --- Hoisting

之前文章也有提到过,常理是先声明後使用,而var却允许先使用後声明:

console.log(a); // undefined
console.log(b); // ReferenceError
var a = "car"; // 声明提前
let b = "123"; //由let声明的不存在提前特性

3.2 var声明变数有污染疑虑

for(var i = 0; i < 100; i++) {
        // many code
    }
    
    // many code
    console.log(i) // 100

循环里面的i在循环结束後,并没有回收掉,而是一直存在的垃圾变数,污染了当前的环境。

for(let i = 0; i < 100; i++) {
        // many code
    }
    
    // many code
    console.log(i) // ReferenceError

所以应该使用let,避免使用var,除非目的是想要定义一个全域变数。

参考资料:
函式与作用域
Scope 作用域
Javascript 的作用域 (Scope) 与范围链 (Scope Chain)


<<:  Day 18-制作购物车系统之产品架构与描述

>>:  Day.12 Queue

20210208-台湾菁英圆桌分享会 (Elite Round Table in Taiwan)

这是我个人的考上Cissp的分享会,其中分享如何有效的念书,提供想考Cissp的朋友一些参考。 ...

[常见的自然语言处理技术] 文本相似度(IV): 建立自己的Word2vec模型

前言 原本以为文本相似度这个主题两天就可以结束了,没想到花了四天来讲。今天将会是介绍自然语言处理基础...

[Day 11] Read取得资料

假设现在有资料在ProductController.php中,想要将资料显示给前端 public f...

Day 04 「树头顾乎哉」测试金字塔 之 Unit Test v.s. Integration Test

今天先来聊聊测试的规模与边界。 测试金字塔 说到单元测试,那就一定要提到 Mike Cohn 在书中...

Angular 深入浅出三十天:表单与测试 Day03 - Reactive Forms 实作 - 以登入为例

今天要来用 Reactive Forms 的方式实作一个简单的登入系统,撇开 UI 不谈,具体的功...