下层基础决定上层建筑基础篇2-js作用域

484 阅读4分钟

下层基础决定上层建筑2-js作用域

下层基础决定上层建筑,今天认识一下JavaScript的作用域

先有鸡还是先有蛋?

我们先来看两段代码

a = 2
var a;
console.log(a);//是否会报错,如果不报错那输出什么?


console.log(a);//是否会报错,如果不报错那输出什么?
var a = 2;

答案在结尾,等你看完文章自会明白。

变量和函数在声明前就调用,以我们熟悉的java,python,C++,C#来说它是会报错的,我们认为代码是由上而下一行一行执行的,但实际上这并不完全正确没有一种特殊情况导致这个假设是错误的。

到底发生了什么?看来我们面对的是一个先有鸡还是先有蛋的问题。到底是先声明(蛋)在前还是赋值(鸡)在前?

编译的步骤

1.词法分析
2.声明提升
    1.变量声明,声明提升
    2.函数声明,整体提升
3.解析
4.生成代码

我们逐个分析

// 1.词法分析,声明提升
var a = 1 ->var , a , = , 1

//2.声明提升
    1.变量声明,声明提升
    2.函数声明,整体提升
    
    //声明提升前
    console.log(a)
    var a = 1
    foo()
    function foo(){
    console(1)
    }

    //声明提升后
    var a;
    function foo(){
    console(1)
    }
    console.log(a) //会打印出undefined
    var a = 1
    foo()
    
  // 3.  **解析**:这一步骤通常涉及构建语法树或抽象语法树(AST),以便更好地理解代码的结构和逻辑。
  // 4.  **生成代码**:基于解析的结果,生成目标代码,例如机器码或字节码,以便计算机能够执行。
  

1.全局作用域:在整个程序中都可以访问到的变量和函数处于全局作用域。例如在页面中直接声明的变量通常是全局变量。
2.函数作用域:在函数内部定义的变量和函数,只能在该函数内部访问。
3.作用域的规则:内部作用域可以访问外部作用域,反之则不行

1716729783887.png

1716730319722.png

整个程序就是一个全局作用域,这个全局作用域里面的函数就可以形成一个函数作用域,内部作用域可以访问外部作用域,反之则不行,这就是一个外部不能访问内部,所以打印结果为1.

1716730551431.png

1716730319722.png

这就是内部作用域可以访问外部作用域的例子

image.png

1716731831647.png

这是一个三层的域,最外面是全局作用域,第二层是函数作用域,第三层是函数作用域里面的作用域,a,b都在第二层的函数作用域里面,根据规则是可以访问到的。

词法作用域

变量声明的地方,也可以说是上一层的域

image.png 以这个为例,bar的词法作用域就是foo的作用域

词法欺骗作用域

1.eval() 让原本不属于这里的代码,变得好像天生就定义在这里一样,它的主要作用是将字符串作为 JavaScript 代码进行动态求值执行。

function foo(a, str){
    eval(str)
    console.log(a,b)//输出5  2
}
foo(5,'var b = 2')

2.with() {} 用于临时扩展作用域链。 缺点:当修改的对象中不存在的属性时,这个属性会被泄漏到全局,变成全局变量

   let person = { name: 'John', age: 30 }; 
   with (person) { 
       console.log(name); //打印John
       console.log(age); //打印30
   }

缺点的例子

function foo(obj){
    with(obj){
        a = 2;
    }
}

var ol = {
    b: 3
}

foo(ol);
console.log(a); //2
console.log(ol.a);//undefind

var vs let varlet

变量提升

  • var存在变量提升,即可以在声明之前使用,值为undefined

  • let不存在变量提升,在声明之前使用会报错。 重复声明

  • var可以重复声明同一个变量。

  • let不允许在同一作用域内重复声明。

块级作用域

  • var不具备块级作用域,在块内声明的变量在块外也可访问。
  • let具有块级作用域,只在其所在的块内有效。

暂存死区

  • let存在暂存死区,在其声明之前访问会引发错误。

    // var 的情况 
    console.log(a); // undefined 
    var a = 10; 
    
    // let 的情况 
    console.log(b); // 报错 
    let b = 10; 
    
    // var 重复声明 
    var c = 1; 
    var c = 2; 
    
    // let 不允许重复声明 
    let d = 1; 
    // let d = 2; // 报错
    
    

块级作用域

1.{} + let

2.函数内部又有一对花括号 {}

3.const(const声明的是常量,不允许修改值)

案例1 -({} +  let):

{
let blockVar = '块级变量';
}
console.log(blockVar)//报错




案例2 - ({} +  let)-特殊例子:

{ 
let a = 1;
var b = 2;
}
console.log(b);//打印2,`var` 定义的变量 `b` 并不受块级作用域严格限制。




案例3 - (函数内部又有一对花括号 `{}`):

function testFunc() {
{ 
function blockFunction() {
console.log('块级函数执行'); 
    } 
}
// 无法直接在这里调用 blockFunction 
 //解释:在 `testFunc` 函数内部又有一对花括号 `{}`,这就构成了一个块级区域。
} 
testFunc();




案例4 - (const): 

 console.log(a) //报错
 const a = 2 ;