前言
前置知识:轻松理解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中被使用
作用域的类型
又三种类型作用与:全局作用域,函数作用域,块级作用域。
全局作用域
什么时候是全局作用域:
- 如果一个变量或函数是在任何函数之外声明的,那么它就具有全局作用域。
- 如果一个变量没有使用
var
,let
, 或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都具有函数作用域。可以在函数中任何地方使用
//参数也具有函数作用域
块级作用域
什么时候是块级作用域:
块级作用域是指使用
let
和const
声明的变量的作用域限制在它们被声明的代码块{}
内部
块级作用域的规则:
- 使用
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()
//只要用的不是在自身作用域里的变量,那么这个变量就是机子有变量
注意
作用域是创建时就决定的不会改变,作用域链则不一样,作用域链是调用的时候被创建。每次函数被调用时,都会创建一个新的执行上下文,其中包括一个作用域链。作用域链的构建基于函数的定义位置以及调用它的上下文。