js作用域:小白探索之旅

179 阅读4分钟

昨天我写了一个函数,内部定义的变量在外部怎么也访问不到!就像是变魔术一样消失了,原来是‘作用域’在搞鬼。

1. 变量是什么

在 JavaScript 中,变量是用于存储数据的容器。我们可以使用 varlet 和 const 关键字来声明变量。

2 .js代码的执行过程

  1. 词法分析--> 拆解代码为'词语'

    -例let age = 18 --> [let, age, =, 18]

  2. 语法分析--> 构建抽象语法树

    -检查语法错误,构建代码结构

  3. 生成可执行代码--> 转化为机器指令

3.作用域是什么

在JavaScript中,作用域是指变量和函数的可访问性,即在代码中可以访问这些变量和函数的部分。

JavaScript主要有两种作用域类型:全局作用域局部作用域。但是在es6版本之后,又引入了块级作用域

  • 全局作用域--> 程序的顶层空间
  • 函数作用域--> 函数内部的独立王国
  • 块级作用域--> 包裹着的领土

访问规则:全局作用域 <-- 函数作用域 <-- 块级作用域

内层作用域可以访问外层作用域,外层不能访问内层

根据上诉的规则我们来举几个例子

 var a = 1
 function foo(){
   console.log(a)
 }
 foo()

运行结果:1

在函数作用域中找不到变量a, 于是访问外层作用域,也就是全局作用域,找到了变量a, 并且值为1 ,于是打印a的值。

那大家想一想,下面这段代码是否会报错

 console.log(a)
 var a = 1

运行结果:undefiend

哎呀!不对呀!其实输出结果就是undefined。其实这就得牵扯到 var let const三者的区别

4. 变量声明进阶对比

1.var 声明的变量会存在声明提升(将变量的声明提升到当前作用域的顶部)

就是这个例子,用 var 声明的变量会声明提升,所以当执行输出时,全局有变量a, 但是此时还未赋值,所以打印结果为undefiend

console.log(a)
 var a = 1

其实这份代码就等同于:

var a
console.log(a)
a = 1

运行结果:undefiend

letconst声明的变量不会声明提升

console.log(a)
let a =1

运行结果:报错

2. var可以重复声明变量
var a = 1
function(){
    var a = 2
    console.log(a)
}

运行结果:2

因为a在函数作用域已经找到了, 所以的值为2。甚至在js里面可以不进行声明,直接写a = 1都是可以的,js会默认给你声明成var

letconst不可以重复声明变量,否则报错

var a = 1
let a = 2 

运行结果:

image.png

3. letconst 的区别

letconst的区别就在于其定义的变量的可变性:const定义的变量是不可变的,而let可以.

5.块级作用域

直接拿例子说话:

{
let a = 1
}
console.log(a)

运行结果:a is not defiend

其实就是let 与 {} 就形成了一个块级作用域,你要打印a, 即使全局没有声明,也不能从外往内找,相当于a 没有声明。换成一个简单的函数作用域作为参考:

function foo()
{
    var a = 1;
}
console.log(a);

运行结果:a is not defiend

6.欺骗词法

aval()案例:

function foo(str, a) {
    eval(str); //var b = 3;
  console.log(a, b);
}

foo('var b = 3',1);

大家对eval()这个函数肯定非常陌生,其实这行代码就是把 var b =3 这行代码从全局作用域搬到了函数作用域当中,起到了一个欺骗作用。

with(obj){}案例:

var obj = {
    a: 1,    
    b: 2,
    c: 3,
}
 with(obj){
     a =2
     b =3
     c =4
 }
 console.log(obj)

运行结果:

image.png 这个函数作用是批量修改对象的键值对,但是我们发现,要是with(){}出现了对象中没有的属性呢?那么with修改其属性会导致其泄露到全局,但是不会增加在此对象中。

var o2 = {
    b: 2,
}

// var a = 2
function foo(obj) {
    with(obj){
        a = 2; //在全局声明了
    }
}
foo(o2);
console.log(o1);
console.log(o2);
console.log(a);

运行结果:

image.png

结语

最后的魔法咒语
"不是变量在消失,而是作用域在生效;
不是引擎太玄妙,而是规则未参透。
var的迷雾走向let的清明,
便是从小白到法师的飞升之路!"

[ 点击下方❤️让更多小伙伴获得这份「破界指南」]