简单理解JS中的作用域和声明提升

前言

在讲作用域之前,我们先来聊聊js的编译和执行

JavaScript是弱类型编程语言,创建时不声明变量类型。如下所示

js 复制代码
var a=5;          //在赋值前不知道变量类型 
var b='hello'     

强类型编程语言如Java、.net、Python、C++、C,创建时需要声明变量类型

c 复制代码
int i=1;          //在赋值前通过'int'知道声明的变量是整型
char c='hello'

那Js在函数调用时怎么知道变量是什么类型呢,函数在执行后,Js会编译其作用域中的变量,并获取其数据类型。

在编译时,会通过找到某个域当中的有效标识符来获取数据类型。但是函数在没有调用的时候是不执行的,也就是不会进行编译。所以函数作用域并不是和全局作用域一起编译,而是先不编译,等要执行的时候再编译,编译永远只发生在执行的前一步.

而函数执行时,变量的查找会是从内到外的查找,即先查找执行函数的作用域,再去外层查找。

js 复制代码
var a=1
function foo(){
    var a =2
        console.log(a);
}
foo()
// 打印 2
js 复制代码
var a=1
function foo(){
    var a =2      
} 
console.log(a);
foo()
// 打印 1

不能从外到内的查找,即不能查找执行函数作用域内部的作用域变量。

js 复制代码
var a = 1
 function foo(){
     var a = 2
     function bar(){
         console.log(a);
     }
 }
 foo()
 bar()
// undefined

上述代码报错原因是因为函数bar()未被定义,函数bar()是在函数foo() 的函数作用域内的,而bar()是在全局作用域中被调用执行的,又因为函数执行不能从外到内,所以上述代码执行不了

什么是作用域

作用域是可访问的变量的集合。它决定了代码区块中这些变量和其他资源的可见性(可访问性)。

作用域的类型

全局作用域、函数作用域、块级作用域、欺骗词法作用域

1.全局作用域

任何不在函数中或是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问

js 复制代码
// 全局变量
var a = 1 
function foo(){
      console.log(a); 
} 
foo();
// 打印 1
foo();//调用函数执行编译

2.函数作用域

函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在此函数作用域内部。这些变量只能在函数内部访问,不能在函数以外去访问。

js 复制代码
  var a = 1          // 全局编译
  function foo()     // 全局编译
  {
      console.log(a);// 函数编译   未调用时不编译
  }
  foo()              // 调用函数执行,编译器暂停,进行函数编译,函数编译完成后再重新执行全局编译
// 打印 1

声明提升

var 声明的变量存在声明的提升,提升到当前作用域的顶峰

js 复制代码
// 表面上的代码
 console.log(a);
 var a = 1
// 实际上的代码 
 var a 
 console.log(a);
 a = 1
 
 // undefined

函数声明会整体提升

js 复制代码
 //表面上的代码
 function foo(){
     console.log('123')
 }
 foo()
 
 //实际上的代码
 foo()
 function foo(){
     console.log('123')
 }

怎么避免声明提升呢

用let代替var定义可以避免变量提升

let虽然可以避免变量提升

js 复制代码
 console.log(a)
 let a = 1
 // 报错

但是不能重复声明同一变量

js 复制代码
 let a = 1
 let a = 2
 console.log(a)
 // 报错

var重复声明同一变量会覆盖

js 复制代码
 var a = 1
 var a = 2
 console.log(a)
 // 打印 2

const 定义也不会发生声明提升 const虽然可以避免变量提升

js 复制代码
 console.log(a)
 const a = 1
 // 报错

但是不能重复声明同一变量

js 复制代码
 const a = 1
 const a = 2
 console.log(a)
 // 报错

而且const声明的变量值不能被修改

js 复制代码
 const a = 1
 a = 'hello'
 // 报错

3.块级作用域

let + {} 会形成块级作用域

const + {} 也会形成块级作用域

js 复制代码
 if(1){
     var a = 1
 }
 console.log(a);
 // 打印1

 if(1){
     let a = 1  // or const a = 1
 }
 console.log(a);
 //报错

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

4.欺骗词法作用域

eval( ) 让原本不属于这里的代码变成就是写在这里的

js 复制代码
function foo(str){
    eval(str) //var b = 2
    var a= 1
    console.log(a,b);
}
foo('var b = 2');
// 打印 1,2

with 可以批量化修改对象的属性

js 复制代码
 var obj = {
     a: 1,
     b: 2,
     c: 3,
 }
 // 使obj里面所有元素都加1
 // 直接定义
 obj.a = 2
 obj.b = 3
 obj.c = 4
 
 with(obj) {  // 批量修改obj内的值
     a = 2,
     b = 3,
     c = 4
 }

但当修改对象中不存在的属性时,该属性会泄漏到全局成为全局变量

js 复制代码
function foo(obj){
    with(obj){
        a=2
    }
}
var o1={
    a:3
};
var o2={
    b:3
};
foo(o2);
console.log(a);// 此时a被泄漏到全局作用域上了
// 打印 2
相关推荐
前端之虎陈随易2 小时前
2年没用Nodejs了,Bun很香
linux·前端·javascript·vue.js·typescript
好运的阿财3 小时前
OpenClaw工具拆解之host_workspace_write+host_workspace_edit
前端·javascript·人工智能·机器学习·ai编程·openclaw·openclaw工具
XiYang-DING3 小时前
JavaScript
开发语言·javascript·ecmascript
空中海4 小时前
02 React Native状态、导航、数据流与设备能力
javascript·react native·react.js
空中海5 小时前
02 状态、Hooks、副作用与数据流
开发语言·javascript·ecmascript
空中海5 小时前
04 React Native工程化、质量、发布与生态选型
javascript·react native·react.js
杨超凡6 小时前
豆包收费了?我特么自己用“意念”搓了一个!
javascript
threelab6 小时前
Three.js 咖啡杯烟雾效果 | 三维可视化 / AI 提示词
开发语言·javascript·人工智能
Heo7 小时前
14_React 中的更新队列 updateQueue
前端·javascript·面试