轻松理解js的作用域和作用域链

前言

前置知识:轻松理解js执行上下文

function fun1() {
    let value = "变量";
    console.log(value)
}

fun1()//输出 "变量"
//value只在fun1中能够被访问,value的作用域就只在fun1函数内

作用域和作用域链我觉得是必须要理解的。变量的值是多少根作用域(scope)和作用域链([[scope]])脱不了关系。本文从作用域是什么,有什么用来理清作用域和作用域链。不过要记住最重要的一点,作用域在函数定义(创建的时候)就确定不会因为调用关系而改变

一 作用域

作用域是什么

JavaScript中的作用域(Scope)是指变量和函数的可访问范围,即变量和函数可以在何处被引用。

function fun1(){
    let value = '变量'
    console.log(value)
}
fun1()//输出 “变量”

//value的作用域就是fun1函数内部,value就只能在fun1中被使用

 作用域的类型

又三种类型作用与:全局作用域,函数作用域,块级作用域。

全局作用域

什么时候是全局作用域:

  • 如果一个变量或函数是在任何函数之外声明的,那么它就具有全局作用域。
  • 如果一个变量没有使用 varlet, 或 const 关键字声明,而直接赋值,那么这个变量会被隐式地提升到全局作用域。

全局作用域规则:

  • 在任何地方都能访问到的变量具有全局作用域。
  • 全局作用域中的变量在整个脚本执行期间都是可用的。
let a=1
function fun1(){
    return ()=>{
        b=2
        console.log(a)
        console.log(b)
    }
}
fun1()()
//输出1和2
//b=2没有使用let、const、和var。直接复制就是全局变量

函数作用域 

什么时候是函数作用域:

        在函数内部声明的变量仅在该函数内部可见或者说是用var定义的变量

函数作用域规则:

  • 用 var 声明的变量具有函数作用域。
  • 这些变量只能在其声明的函数内部访问。
  • 即使在函数的不同部分声明了相同名称的变量,它们也是同一个变量。
  • 函数参数也具有函数作用域。
function fun1(a){
      if(true){
        if(true){
            var b=2
        }
      }
    console.log(b,a)
}
fun1(1)

//输出2 1
//在函数中任何位置定义的var都具有函数作用域。可以在函数中任何地方使用
//参数也具有函数作用域

 块级作用域

什么时候是块级作用域:

        块级作用域是指使用 letconst 声明的变量的作用域限制在它们被声明的代码块 {} 内部

块级作用域的规则:

  • 使用 let 和 const 声明的变量只在其所在的代码块内{}可见。
  • 这些变量在代码块的开始处声明,并且只在该代码块内部有效。
  • 与 var 不同,使用 let 和 const 声明的变量不会被提升到整个函数的顶部。
  • 这意味着在声明之前使用这些变量会导致 ReferenceError。这个区域被称为“暂时性死区”(TDZ)。
  • let 允许在同一个作用域内重复声明同一个变量,但不能在同一个块级作用域内多次声明同一个变量。
  • const 不允许在同一作用域内重复声明同一个变量。

 

function fun1(){
    console.log(a,b)
    let a=1
    if(true){
        const b=2
    }
    console.log(a,b)
}
fun1()
//第一个console.log(a,b)报错,let和const不会变量提升,提前打印会直接报错
//打印出a等于1,b报错
//因为b只有块级作用域{},所以b只在if的{}中有效

对于最后两条: 

{
  let x = 10;
  let x = 20; // 抛出 SyntaxError: Identifier 'x' has already been declared
}

// 或者
{
  let x = 10;
  if (true) {
    let x = 20; // 这里是合法的,因为是在不同的块级作用域内声明
  }
}

// 或者
{
  let x = 10;
  for (let i = 0; i < 5; i++) {
    let x = 20; // 这里是合法的,因为是在不同的块级作用域内声明
  }
}

 

二 作用域链

什么是作用域链

作用域链顾名思义,就是一条链子,当前作用域找不到就顺着链子往父级作用域找,最总找到全局作用域,在找不到就则会抛出一个 ReferenceError

构成:当一个函数被调用时,JavaScript 引擎会创建一个新的执行上下文(execution context),这个执行上下文包含了一个作用域链。作用域链是由一系列的作用域对象组成的链表,它决定了 JavaScript 引擎如何查找变量和函数。个人理解:一个函数被定义在另一个函数内部,那么内部函数的作用域链就会包含外部函数的作用域。这种结构允许内部函数访问外部函数的作用域中的变量

var a = 100
function Fun1() {
    var b = 200
    function Fun2() {
        var c = 300
        console.log(a) 
        console.log(b) 
        console.log(c) 
    }
    Fun2()
}
Fun1()//输出100,200,300
var a = 100
function fn() {
  let b = 20
  function bar() {
    console.log(a + b) //30
  }
  return bar
}
let c = fn(),
b = 200
c() //bar()

//b变量的作用域取决于创建时也就是bar函数内部
//b变量在函数内部找不到就找创建的外部函数fn内,fn没有的话在找外部,最后找到全局作用域
//b在这里找到fn函数停止了,输出120

 

自由变量 

由变量是在函数作用域中使用但不在该函数作用域内定义的变量。自由变量实际上是从函数的作用域链中查找得到的,它们存在于函数外部的作用域中,但可以在函数内部访问。个人理解:你用的是别的作用域里的变量,这个变量就是自由变量

var a = 100
function Fun1() {
    console.log(a) //a就是自由变量
}
Fun1()
//只要用的不是在自身作用域里的变量,那么这个变量就是机子有变量

注意

作用域是创建时就决定的不会改变,作用域链则不一样,作用域链是调用的时候被创建。每次函数被调用时,都会创建一个新的执行上下文,其中包括一个作用域链。作用域链的构建基于函数的定义位置以及调用它的上下文。

 

  • 18
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值