简单理解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
相关推荐
qiyi.sky8 分钟前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~11 分钟前
分析JS Crash(进程崩溃)
java·前端·javascript
哪 吒13 分钟前
华为OD机试 - 几何平均值最大子数(Python/JS/C/C++ 2024 E卷 200分)
javascript·python·华为od
安冬的码畜日常20 分钟前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
Q_w77421 小时前
一个真实可用的登录界面!
javascript·mysql·php·html5·网站登录
昨天;明天。今天。1 小时前
案例-任务清单
前端·javascript·css
一丝晨光2 小时前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby
Front思2 小时前
vue使用高德地图
javascript·vue.js·ecmascript
惜.己3 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
长天一色3 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript