乐趣区

关于javascript:学废了JavaScript-中的作用域与作用域链

什么是作用域?

作用域定义了变量的可见性或可拜访性。大白话来说,就是一个变量能不能被拜访或援用,是由它的作用域决定的。

在 JavaScript 中有三种作用域。

  • 全局作用域
  • 函数作用域(部分作用域)
  • 块作用域
let globalVariable = "我是全局作用域下的变量"
function func() {let localVariable = "我是部分作用域下的变量"}

if (true) {let blockVariable = "我是块作用域下的变量"}

全局作用域 Global Scope

一个在最外层定义的变量便处于全局作用域,全局作用域内的变量能够在程序的任意中央拜访。

var globalVariable = "全局作用域变量"
function func() {
    // 在函数内拜访全局作用域的变量
    console.log("函数内拜访:", globalVariable)
}

func()
console.log("函数外拜访:", globalVariable)

输入:

 函数内拜访:全局作用域变量
函数外拜访:全局作用域变量 

应用 var 关键字 在大括号内(包含纯正的大括号、if、while、for)定义的变量依然 ` 属于全局作用域。

if (true) {var globalVariable = "全局作用域变量"}
console.log("内部拜访:", globalVariable)

输入:

 内部拜访:全局作用域变量 

函数作用域(部分作用域)Function Scope(Local Scope)

在函数内定义的变量则属于函数作用域,又称部分作用域。部分作用域内的变量只能在本身作用域内被拜访。

function func(params) {
    var localVariable = "部分作用域变量"
    console.log("函数内拜访:", localVariable)
}

func()
console.log("内部拜访:", localVariable) // Uncaught ReferenceError: localVariable is not defined

输入:

 函数内拜访:部分作用域变量
Uncaught ReferenceError: localVariable is not defined

例子中,咱们尝试在内部拜访部分作用域中定义的变量,报了 变量未定义 的谬误。

块作用域 Block Scope

ES6 中引入了 let 与 const,与 var 不同的是。之前的例子中,在大括号(包含纯正的大括号、if、while、for)间用 var 定义的变量处在全局作用域。如果咱们用 let 与 const 在大括号中定义,变量将处于块作用域。

块作用域内的变量只能在本身作用域内被拜访。

let 与 const 的不同点在于,const 定义的是一个常量,无奈批改定义后的值。

{
    let blockVariable = "块作用域变量"
    console.log("块内拜访:", blockVariable)
}
console.log("内部拜访:", blockVariable) // Uncaught ReferenceError: blockVariable is not defined

输入:

 块内拜访:块作用域变量
Uncaught ReferenceError: blockVariable is not defined

例子中,咱们尝试在内部拜访块作用域中定义的变量,报了 变量未定义 的谬误。

什么是作用域链?Scope Chain

当一个变量在以后作用域无奈找到时,便会尝试寻找其外层的作用域,如果还找不到,再持续往外寻找(只会往外寻找,不会寻找兄弟作用域,更不会往内寻找)。 这种如同链条一样的寻找规定便被称为作用域链。

let variable1 = "我是变量 1,内部的"
let variable2 = "我是变量 2"
function func() {
    let variable1 = "我是变量 1,外部的"
    {let variable3 = "我是变量 3"}
    {
        // 往外寻找,在上一层函数内找到了
        console.log(variable1)
        // 往外寻找,直到全局作用域
        console.log(variable2)
        // 找不到,报错
        console.log(variable3) // Uncaught ReferenceError: variable3 is not defined
    }
}
func()

输入:

 我是变量 1,外部的
我是变量 2
Uncaught ReferenceError: variable3 is not defined

在例子中,打印 variable1 变量时,因为在下层作用域也就是函数中就找到了 variable1 变量,便进行了寻找,不会找到全局作用域下的 variable1 变量。

寻找 variable2 变量时,在下层作用域中未找到,便始终找到了上下层作用域,也就是全局作用域下的 variable2 变量。

寻找 variable3 变量时,因为 variable3 变量被定义在兄弟作用域中,并不会被寻找到,因为作用域链的规定是只会往下层作用域寻找,并不会寻找兄弟作用域。因而这里报了变量未定义的谬误。

函数的作用域是它定义时的作用域,而不是调用时

function func() {
    let variable = "我是 func 内的变量"
    function func2() {console.log(variable)
    }
    return func2
}

{
    let variable = "我是大括号内的变量"
    let func2 = func()
    func2()}

输入:

 我是 func 内的变量 

在例子中,执行 func2 函数时往上寻找的作用域是在 func2 定义时的作用域,而不是调用时的作用域。

如果找不到变量会怎么?

如果一个变量直到全局作用域也找不到便会执行以下操作。

  1. 非严格模式:隐式申明全局变量
  2. 严格模式:报错

非严格模式

非严格模式下,尝试赋值一个变量时,如果找不到则会隐性申明成全局域的变量。

{variable = "我是一个隐性申明的变量"}
console.log(variable)

输入:

 我是一个隐性申明的变量 

以上的例子中,variable 因为未声明,因而被隐性申明成了全局作用域下的变量,这使得在最内部也能打印出 variable 变量的值。

非严格模式下,尝试应用一个变量的值时,如果找不到同样会报错。

{console.log(variable) // Uncaught ReferenceError: variable is not defined
}

输入:

Uncaught ReferenceError: variable is not defined

以上的例子中,因为应用 variable 时未定义,因而报了未定义的谬误。

严格模式

退出 "use strict" 表明是严格模式,严格模式下不管赋值还是应用未当时申明的变量都会报错。

"use strict"
{variable = "我是一个隐性申明的变量" // Uncaught ReferenceError: variable is not defined}

输入:

Uncaught ReferenceError: variable is not defined

作用域的益处?

  1. 避免命名抵触 :你写了一万行的代码文件,如果没有作用域,你要给每个变量取举世无双的名字,屁股想想也晓得是种折磨。
  2. 安全性 :变量不会被内部拜访,保障了变量值不会被随便批改。你定义在函数内的变量,如果能在几千行之后不小心被批改,脚趾头想想也晓得是种折磨。
  3. 更高级的语法 :封装、面向对象等的实现离不开对变量的隔离,这是依附作用域所达到的。

说人话!

写代码时不必辨别它什么全局应用域、部分作用域、块作用域啥的概念。只用记得大括号就是一个作用域,寻找变量永远是从内往外找。当初咱们的编辑器根本都有缩进格式化, 从以后代码块的地位一层一层往左,就是它所能援用到的所有变量。

打个比方,就像咱们每个家庭就是一个作用域,当咱们须要一笔手术费掏不出钱的时候,必定是先在家里找,问问父母兄弟姐妹啥的,不会去求助其余生疏的家庭。还没有的话就往外到熟人关系这个作用域里问问。还不行就向街道居委会求助。居委会也没方法再向国家求援。从最亲热的关系找起,一层一层圈子往外,这就是作用域与作用域链。

最初强烈建议大家应用 let 命名变量,放弃 var!

退出移动版