一文弄懂js中的var,let,const

在es6中新增了 let 和 const 关键字用来声明变量。要了解let、const和 var 的区别,我们先来聊聊变量提升

什么是变量提升

在JavaScript中,变量提升(Hoisting)是一种特殊的现象,它指的是变量和函数声明在代码执行之前就被提升到当前作用域的顶部。这意味着无论声明在何处,都会被视为在当前作用域的开始处声明。这个概念对于理解JavaScript代码的执行顺序非常重要。

通俗来讲,就是会把变量函数的声明给定义到当前作用域的顶部,但是先不给他赋值。
让我们来举个例子:

a = 1;
var a;
console.log(a);
 function test(){
     a = 1;
     var a;
     console.log(a);
 }

在上面这两段代码,按照道理来讲,假如一行一行执行,第一行就会报错,因为a未定义,但是由于变量提升,上面这段代码可以输出a的值,但是输出是underfined

函数同样存在着变量提升
js中存在着两种声明函数的方式

//函数声明式:
function fun1 () {}
//变量形式声明: 
var fun2 = function () {}

当使用变量方式声明函数时,变量同样会提升,此时变量还未定义,因此会报一个变量不是函数错误

fn() 
var fun1 = function () { 
console.log(1) } // Uncaught TypeError: fn is not a function 
fun() 
function fnu2 () { 
console.log(1) } // 输出结果:1

为什么会存在变量提升?

在es6之前,js和其他大部分语言不同是作用域只分为函数作用域(函数内部算一个作用域)和全局作用域,而其他语言支持块作用域(一个{}内为一个作用域)。
es6不支持块级作用域,将作用域内部的变量统一提升就是最快速,简单的设计。
这样做虽然能够提升性能和增大容错,但同样出现着问题。
1.容易导致变量被覆盖

var myname = "邓邓"
function showName(){
  console.log(myname);
  if(0){
   var myname = "茹茹"
  }
}
showName()

猜猜上面这段代码输出的是邓邓还是茹茹?答案是underfined。因为函数内部的变量myName会提升,并且在内部定义的变量跟外部变量名相同时会覆盖。

2.变量未销毁

int i=10086;
function foo(){
  for (var i = 0; i < 5; i++) {
  }
  console.log(i); 
}
foo()

上面这段输出的不是10086,也不是0,而是5.也是因为for循环中定义的变量i被提升到了foo函数顶端。

let,const,var的的使用和区别

我们正式引入我们今天的主角es6出现的let,const,块级作用域。
块级作用域从大括号开始 -> 到大括号结束。

块级作用域有以下几个特点:

  • 声明变量不会提升到代码块顶部

let/const 声明并不会被提升到当前代码块的顶部,因此你需要手动将 let/const 声明放置到顶部,以便让变量在整个代码块内部可用。

function getValue(condition) {
if (condition) {
let value = "blue";
return value;
} else {
// value 在此处不可用
return null;
}
// value 在此处不可用
}
  • 禁止重复声明

如果一个标识符已经在当前作用域被定义,那么在此代码块内使用同一个标识符进行 let 声明就会导致抛出错误。例如:

var count = 30;
let count = 40; // Uncaught SyntaxError: Identifier 'count' has already been declared
  • 局部作用域中声明跟全局变量同名的变量时一个新变量
function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // 同样的变量!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

在上述的这段代码中,由于var并不支持块作用域,所以最终只生成了一个变量 num,函数体内所有对 num 的赋值操作都会直接改变变量环境中的 num 的值。所以上述代码最后输出的是 2,而对于相同逻辑的代码,其他语言最后一步输出的值应该是 1,因为在 if 里面的声明不应该影响到块外面的变量。
当我们把var换成let,输出结果就对了,因为let支持块作用域,当出作用域时变量销毁,代码块中的x和函数中的x不是同一个变量,他们的声明也不会受到影响。

let和const的区别

let是声明了一个变量,而const声明了一个常量。变量可以随便改变他的值,而常量不能改变他的值。
但是常量存储对象数据类型时候,存储的是对象的地址,也就是他指向的地址不能变,但是指向对象的属性可以发生变化。

暂时性死区

其实上文中说的let不会将变量提升这句话并不完全正确,我们经过调试可以发现其实在作用域开头也初始化了变量,只是系统不允许访问,访问会报错。
ES6 规定:如果区块中存在 let 和 const,这个区块对这两个关键字声明的变量,从一开始就形成了封闭作用域。假如尝试在声明前去使用这类变量,就会报错。这一段会报错的区域就是暂时性死区。上面代码的第4行上方的区域就是暂时性死区。

### JavaScript 中 `var`、`let` 和 `const` 的区别 #### 1. **作用域** - 使用 `var` 声明的变量具有函数作用域或全局作用域。如果在函数外部声明,则该变量属于全局作用域;如果在函数内部声明,则它仅限于当前函数的作用域[^1]。 - 使用 `let` 或 `const` 声明的变量则具有块级作用域(block scope)。这意味着这些变量仅在 `{}` 所定义的范围内有效,无论是循环体还是条件语句内。 #### 2. **重复声明** - 如果尝试在同一作用域下多次使用 `var` 声明同一个变量名,JavaScript 不会报错,而是覆盖之前的声明[^3]。 - 对于 `let` 而言,在同一作用域中重新声明相同的变量会导致语法错误。 - 同样地,对于 `const` 来说,一旦声明了一个常量,就不能再对该名称进行重新赋值或者再次声明。 #### 3. **初始化需求** - 变量通过 `var` 关键字可以先声明后赋值,甚至不赋初始值也无妨。 - 利用 `let` 定义变量同样允许延迟初始化。 - 然而,采用 `const` 创建的是不可变绑定,因此必须立即赋予固定值,并且后续不能再改变这个值。 #### 4. **变量提升 (Hoisting)** - 当程序运行时,所有由 `var` 声明的变量都会被提前处理并设置为 `undefined` ,这就是所谓的 “变量提升”现象[^2]。 - 相反,那些经由 `let` 或者 `const` 引入的新实体不会经历这样的过程——直到执行流真正到达它们各自的声明处之前都是暂时性的死区(TDZ),在此期间试图访问未初始化的数据将会抛出 ReferenceError 错误。 ```javascript console.log(xVar); // 输出 'undefined' console.log(xLet); // 抛出 ReferenceError: Cannot access 'xLet' before initialization console.log(xConst); // 抛出 ReferenceError: Cannot access 'xConst' before initialization var xVar = 5; let xLet = 6; const xConst = 7; ``` #### 推荐使用的顺序 基于上述特性差异以及现代编码实践考虑,推荐按照如下优先级选用合适的关键词来完成相应的编程任务: 优先选择 `const` > `let` > `var` 。这是因为尽可能减少不必要的可变状态有助于构建更健壮的应用程序逻辑结构。 --- ### 示例代码展示不同之处 下面是一些简单的例子用来说明这三种方式之间存在的主要差别: ```javascript // var example with function and block scoping issues. function checkScope() { var color = "blue"; if (true){ var color = "green"; console.log('Inside Block:',color); } console.log('Outside Block but Inside Function:',color); } checkScope(); // Both logs will show green because of hoisting within the same functional scope. // let respects block level scopes properly unlike var does here above. for(let i=0;i<3;i++) {setTimeout(() => console.log(i),1);} // This prints numbers from 0 to 2 correctly due to individual copies per iteration created via block scoped variable i declared using let keyword inside loop construct body itself rather than shared single instance across all iterations as would happen had we used var instead there too! try{ const piValue=Math.PI; }catch(err){console.error(`Caught Exception:${err.message}.`)} finally{piValue=3;} // Error thrown since reassignment isn't allowed after first assignment when defining constants through const statement form. ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值