菜鸟小白-深入了解Javascript中的作用域

400 阅读4分钟

一 、 JS执行引擎

JavaScript 执行引擎是浏览器或运行环境(如 Node.js)中负责解析和执行 JavaScript 代码的核心组件。它的主要作用是将 JavaScript 代码转换为机器码并执行。

二、 JS执行过程

JS执行过程中代码要执行三个过程:词法分析、语法分析、生成代码

  1. 词法分析:将源代码字符串分解成一系列的标记(tokens)。这些标记是代码中的基本元素,例如 var a = 2; 这行代码被分解为:var、a、=、2、; 。
  2. 语法分析:语法分析器接收来自词法分析器的标记流,并根据语言的语法规则构建抽象语法树(AST)。AST 是源代码的层次结构表示,它反映了代码中的语法结构。如下图:

image.png

3.生成代码:一旦AST被构建,编译过程的最后一步是代码生成。代码生成器遍历AST,并根据AST的结构生成目标代码。目标代码可以是机器代码、字节码或者另一种高级语言的代码。

三、 作用域

作用域可以分为全局作用域和局部作用域

全局作用域(Global Scope):

  1. 全局作用域是指在任何函数之外定义的变量,这些变量可以在代码的任何地方访问。
  2. 在浏览器环境中,全局作用域的全局对象是window,在Node.js中是global
globaVar = 'I am a global variable';

function foo(){
  console.log(globaVar); // 可以访问到 global Var
  
}
foo();

局部作用域(Local Scope) :

  1. 函数作用域是指在函数内部定义的变量,这些变量只能在函数内部访问。
  2. 使用var关键字声明的变量具有函数作用域。
function base(){
  var localVar = 'I am a local variable';
  console.log(localVar); // 可以访问到 localVar
}

base();
// console.log(localVar);  // 报错,localVar 在函数外不可访问

四、var,let,const的区别

  1. var 声明的变量会存在声明提升(将变量的声明提升到当前作用域的顶部) let 不会
console.log(a); // 正常输出,输出underfined
var a = 1

console.log(a); //报错ReferenceError: Cannot access 'a' before initialization
let a = 1

2.重复声明

  • 可以在同一个作用域内多次声明同一个 var 变量,后面的声明会覆盖前面的声明。
  • 不能在同一个作用域内多次声明同一个 let 变量。否则会语法错误(SyntaxError)。

3.块级作用域

  • var 具有全局作用域和函数作用域,如果在任何函数外声明,则它为全局变量,即使在if或者for语句中也为全局变量。

  • let+{ }  拥有块级作用域,它们只在它们被声明的代码块(如 if 语句、for 循环等)内部有效。

if(true){
  var a = 1
  let b = 2
}
console.log(a); //1
console.log(b); //b is not defined
for(var i = 0; i < 10 ; i ++){
  console.log(i); //输出 0~9
  
}
console.log(i); //10
for(let i = 0; i < 10 ; i ++){
  console.log(i); //输出 0~9
  
}
console.log(i); // i is not defined

4.const 关键字用于声明一个只读的常量,这意味着一旦声明并初始化,它的值就不能再被改变。

const a = 1
a = 2
console.log(a); //TypeError: Assignment to constant variable.

5.暂时性死区 在代码块内,使用 let 声明变量之前,该变量是不可用的。也就是说,在变量声明之前,访问这个变量会抛出 ReferenceError。暂时性死区的存在是为了保证代码的执行顺序和逻辑。

let a = 1
if (true) {
  console.log(a); // 暂时性死区
  let a = 2
}

五、欺骗词法作用域

1.eval

eval( )可以将任意字符串当成代码执行,如以下,将字符串'var b = 3'当成代码执行,所以得到b=3,再传入实参1给形参a,最后得到正确结果1,3。

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

2.with

with( ){ } 当对象中没有属性 x 时,使用with修改 x 属性会导致 x 泄露到全局,会在全局作用域中查找或声明 a。

var o1 = {
  a:1
}
var o2 ={
  b:2
}
//  var a = 2
function foo(obj){
  with(obj){
    a=2
  }
}
foo(o2)
console.log(o2);  //输出{b:2}
console.log(a);   //输出 2 

六、总结

  1. js的执行引擎包括浏览器和node
  2. js的代码执行过程分为:词法分析、语法分析、生成代码
  3. js 有全局作用域和函数作用域, 作用域规则:内层作用域可以访问外层作用域,外层不能访问内层
  4. let 与 var
  • var 声明的变量会存在声明提升(将变量的声明提升到当前作用域的顶部) let 不会
  • var 可以重复声明变量,let 不行
  • var 在全局声明的变量会默认添加在 window 对象上, let 不会
  • const 声明的变量值无法修改
  1. 欺骗词法:1.eval( ) 将任意字符串当成代码执行。 2.with( ){ } 当对象中没有属性x时,with修改x属性会导致 x 泄露到全局。
  2. 作用域就是一个独立的地盘,让变量不会外泄、暴露出去。也就是说作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。