JavaScript 作用域和作用域链

132 阅读3分钟

一、作用域

1-1 作用域基本概念

  • 作用域是指程序源代码中定义变量或函数生效(能被访问)的区域

  • 作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限

  • 作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突

  • ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6的到来,为我们提供了块级作用域,可通过新增命令 let 和 const 来体现。

1-2 全局作用域和函数作用域

  1. 全局作用域

定义:在代码中任何地方都能访问到的变量拥有全局作用域

弊端:污染全局命名空间,容易引起命名冲突

// 1.最外层函数和在最外层函数外面定义的变量拥有全局作用域

// 最外层变量
var outVariable = "我是最外层变量";

// 最外层函数
function outFun() { 
    var inVariable = "内层变量";
    function innerFun() { //内层函数
        console.log(inVariable);
    }
}

// 2.所有末定义直接赋值的变量自动声明为拥有全局作用域

function outFun2() {
    variable = "未定义直接赋值的变量";
    var inVariable2 = "内层变量2";
}
outFun2(); // 要先执行这个函数,否则根本不知道里面是啥
console.log(variable);  // 未定义直接赋值的变量
console.log(inVariable2); // inVariable2 is not defined

// 3.所有window对象的属性拥有全局作用域
  1. 函数作用域

声明在函数内部的变量拥有函数作用域,函数作用域同时也是一种局部作用域。和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部

1-3 var 类型变量在块语句(注意!!!)

块语句(大括号“{}”中间的语句),如 if 和 switch 条件语句或 for 和 while 循环语句,不像函数,它们不会创建一个新的作用域。在块语句中定义的变量将保留在它们已经存在的作用域中。

if (true) {
    // 'if' 条件语句块不会创建一个新的作用域
    var name = 'Hammad'; // name 依然在全局作用域中
}
console.log(name); // logs 'Hammad'

1-4 块级作用域

概念:通过 let 和 const 声明的变量拥有块级作用域 ;变量必须声明在一个函数内部或在一个代码块(由一对花括号包裹)内部

优点:块级作用域可以解决由于过多全局变量和函数产生的命名冲突的问题

二、作用域链

2-1 自由变量

如下代码中,console.log(a) 要得到 a 变量,但是在当前的作用域中没有定义a变量。当前作用域没有定义的变量,则成为自由变量 。自由变量的取值要到创建这个函数或变量的作用域

var a = 100
function fn() {
    var b = 200
    console.log(a) // a 在这里就是一个自由变量
    console.log(b)
}
fn()
var x = 10
function fn() {
  console.log(x)
}
function show(f) {
  var x = 20
  (function() {
    f() //10,而不是20
  })()
}
show(fn)

2-2 作用域链

变量的查找是从本身作用域开始,再一层一层向上寻找,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链

var a = 100
function F1() {
    var b = 200
    function F2() {
        var c = 300
        console.log(a) // 自由变量,顺作用域链向父作用域找
        console.log(b) // 自由变量,顺作用域链向父作用域找
        console.log(c) // 本作用域的变量
    }
    F2()
}
F1()