简单理解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
相关推荐
前端拾光者38 分钟前
利用D3.js实现数据可视化的简单示例
开发语言·javascript·信息可视化
木子02041 小时前
前端VUE项目启动方式
前端·javascript·vue.js
endingCode2 小时前
45.坑王驾到第九期:Mac安装typescript后tsc命令无效的问题
javascript·macos·typescript
Myli_ing3 小时前
HTML的自动定义倒计时,这个配色存一下
前端·javascript·html
I_Am_Me_3 小时前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
℘团子এ3 小时前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z3 小时前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
前端百草阁4 小时前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜4 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish4 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue