JS作用域与作用域链

203 阅读4分钟

作用域

作用域种类分为 全局作用域、函数作用域、块级作用域

作用域定义:

作用域是在代码运行时的某些特定部分中变量,函数和对象的可访问性。

简单说:作用域决定了代码区块中变量和其他资源的可见性。

作用域用处:

隔离变量,不同作用域下同名变量不会有冲突。

全局作用域

全局作用域:代码在程序的任何地方都能被访问,window对象的内置属性都拥有全局作用域

在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:

  • 最外层函数和在最外层函数外面定义的变量拥有全局作用域
var outVariable = "good" // 最外层变量
function test() { // 最外层函数
    var inVariable = "goodData"  // 内层变量
    function test1(){
        console.log(inVariable)
    }
    test1()
}
console.log(outVariable); //good
test(); //goodData
console.log(inVariable); //inVariable is not defined
test1(); //innerFun is not defined
  • 所有末定义直接赋值的变量自动声明为拥有全局作用域
function test(){
    name ="good"
    var name = "food"
}

test();
console.log(name) // good

  • 所有 window 对象的属性拥有全局作用域

一般情况下,window 对象的内置属性都拥有全局作用域,例如 window.name、window.location、window.top 等等。

例子:

作用域有上下级关系,上下级关系的确定就看函数是在哪个作用域下创建的。如上,fn作用域下创建了bar函数,那么“fn作用域”就是“bar作用域”的上级。

函数作用域

函数作用域:是指声明在函数内部的变量,在固定的代码域中可以访问。最常见的例如函数内部。

function test(){
    var name = "good"
    function test1(){
      console.log(name)  // undefined
    }
}

test()
块级作用域

块级作用域可通过新增命令 let 和 const 声明,所声明的变量在指定块的作用域外无法被访问。

静态作用域

静态作用域是指函数的作用域在函数定义时就已经确定了。

// 静态作用域:
var a = 10;
function fn() {
  var b = 1;
  console.log(a + b);
}
fn(); // 11
动态作用域

动态作用域是指函数的作用域在运行时才确定。

// 动态作用域:
function foo() {
  console.log(a);
}
function bar() {
  var a = 3;
  foo();
}
var a = 2;
bar(); // 2;

作用域链

作用域链的意义:查找变量(确定变量来自于哪里,变量是否可以访问)

当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链

简单来说,查找一个变量来自哪里,能否被访问,需要以下四步:

- 1.查看当前作用域,如果当前作用域声明了这个变量,可以直接访问

- 2.查找当前作用域的上级作用域,也就是当前函数的上级函数,看看上级函数中有没有声明,有就返回变量,没有继续下一步

- 3.再查找上级函数的上级函数,直到全局作用域为止,有则返回,无则继续

- 4.如果全局作用域中也没有,我们就认为这个变量未声明(xxx is not defined)

这四步操作就描述了整个作用域链及作用域链如何查找变量的过程。

var x = 10
function fn() {
  console.log(x)
}
function show(f) {
  var x = 20
  (function() {
    f() //10,而不是20
  })()
}
show(fn)

用处:保证对执行环境有权访问的所有变量和函数的有序访问。

额外扩展

自由变量

当前作用域没有定义的变量,这成为自由变量。自由变量的值如何得到 —— 向父级作用域寻找

var a = 10 
function test(){
    console.log(a) // 10
}

要到创建这个函数的那个作用域中取值,这里强调的是“创建”,而不是“调用”,切记切记——其实这就是所谓的"静态作用域"

作用域与执行上下文

区别:执行上下文在运行时确定,随时可能改变;作用域在定义时就确定,并且不会改变。