javascript var、let、const 有什么区别?

前言:随着2015年6月ECMAScript6.0(简称ES6)发布,有一些新的命令进入前端开发领域,即let、
const、import和class命令。其中的let和const是用于声明变量的,这两个命令跟ES5的var有许多不同,并且
let和const也有一些细微的差别。在阅读了阮一峰老师的文档后,
整理了这篇文章,分享给大家。

简单介绍:

let的用法类似于var,但是let只在所在的代码块内有效,所以一般用let替代var。而const通常用于声明常量。

首先看下面这张表:

声明方式变量提升暂时性死区重复声明初始值
var允许不存在允许不需要
let不允许存在不允许不需要
const不允许存在不允许需要

一、var方式定义变量有什么bug

1. js没有块级作用域

在js函数中的var声明,其作用域是函数体的全部。

for(var i=0; i<10; i++){
    var aa = 'a';
}
console.log(aa) // a
console.log(i) //10

已经跳出for循环了,但是还可以访问到循环内定义的变量a,甚至连变量i都可以访问到

2. 循环内变量过度共享

for(var i=0; i<3; i++){
    setTimeout(function(){
        console.log(i) 
    },1000);
}

控制台输出了3个3,而不是预想的 0、1、2。
事实是,循环本身及三次timeout回调均共享唯一变量i。当循环结束执行时,i的值为3,所以当第一个
timeout执行时,调用的i也就是3了。

二、区别

var和let/const的区别

1.块级作用域
2.不存在变量提升
3.暂时性死区
4.不可重复声明
5.let、const声明的全局变量不会挂在顶层对象下面

const命令的两个注意点:

1.const声明之后必须马上赋值,否则会报错
2.const简单类型一旦声明就不能更改,复杂类型(数组、对象)指针指向的地址不能更改,内部数据
可以更改。

为什么需要块级作用域

ES5只有全局作用域和函数作用域,没有块级作用域
这会导致许多不合理的场景:
1.内层变量可能覆盖外层变量
2.用来计数的循环变量泄露为全局变量

var tmp = [1,2,3];
function f(){
    console.log(tmp);
    if(false){
        var tmp = 'this is a string';
    }
}
f(); // undefined

var str = 'string';
for (var i=0;i<str.length;i++){
    console.log(str[i]);
}
console.log(i);
// s
// t
// r
// i
// n
// g
// 6

块级作用域

  1. 作用域
function foo(){
    let n = 7;
    if(true){
        let n = 9;
        console.log(n); //9
    }
    console.log(n);  //7
}
foo();
  1. 块级作用域可以任意嵌套
{{
    {let say = 'hello world';}
    console.log(say); // 报错Uncaught ReferenceError: say is not defined
}}

块级作用域声明函数:

在块级作用域声明函数,因为浏览器要兼容老代码,会产生一些问题

在块级作用域声明函数,最好使用匿名函数的形式

if(true){
    let a = function(){};
}

不存在变量提升

变量提升:在同一作用域下,变量可以在声明之前使用,值为undefined
ES5使用var声明变量时,经常会出现变量提升的现象

// var 情况
console.log(aaa); // 输出 undefined
var aaa = '1111';

// let 情况
console.log(aaa);   //Uncaught ReferenceError: aaa is not defined
let aaa = '111';

暂时性死区

只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明
之前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。

if(true){
    // TDZ开始
    aaa = 'abc';     //Uncaught ReferenceError
    console.log(aaa); //Uncaught ReferenceError

    let aaa; // TDZ结束
    console.log(aaa); //undefined

    aaa = '111';     
    console.log(aaa); //111
}

暂时性死区和不能变量提升的意义在于:
为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。

上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。

“暂时性死区”也意味着typeof不再是一个百分之百安全的操作。

typeof x; // ReferenceError
let x;

但是,如果一个变量根本没有被声明,使用typeof反而不会报错。

typeof undeclared_variable // undefined

总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

不允许重复声明

在测试的时候可能会出现这样的情况:var aa=‘声明一个变量’;const aa=‘重复声明一个变量不报错’;
出现这种情况是因为babel在转化的时候做了一些处理,在浏览器控制台中测试,就会报错。

let、const不允许在相同作用域内,重复声明同一个变量

function foo(){
    let aaa = 10;
    var aaa = '111111111';  //Uncaught SyntaxError: Identifier 'aaa' has already been declared
}
function foo(){
    var aaa = '111111111'; 
    let aaa = 10; 
}

function foo(arg){
    let arg = 111;  //Uncaught SyntaxError: Identifier 'arg' has already been declared
}

let、const声明的全局变量不会挂在顶层对象下面

  1. 浏览器环境的顶层对象是:window
  2. node环境的顶层对象是:global
  3. var声明的全局变量会挂在顶层对象下面,而let、const不会挂在顶层对象下面。
var aa = 111;
// 如果在node环境,可以写成global.aa
// 或者采用通用方法,写成this.aa
console.log(window.aa)

let bb = 222;
console.log(window.bb); // undefined

三、const 命令

  1. 声明之后,必须马上赋值
let aa; var aa; // 不报错
const aa2 = '赋值';
const aa2; // Uncaught SyntaxError: Missing initializer in const declaration

2.const 声明值之后就不能改变

简单类型:不能改动

const aa = '不能改变';
aa = '报错';  // Uncaught TypeError: Assignment to constant variable.

复杂类型:变量指针不能变

const aa = ['do not update'];
aa[0] = 'update array'; // 不报错
aa = ['重新给数组赋值'];  // Uncaught TypeError: Assignment to constant variable.

const bb = {
    a1:'复杂类型'
};
bb.a1 = 'object';  // 不报错
bb = {
    a1:'new 复杂类型' 
};   // Uncaught TypeError: Assignment to constant variable.

如果真想讲对象冻结,应该使用Object.freeze方法

const foo = Object.freeze({});

// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
foo.prop = 123;

上面代码中,常量foo指向一个冻结的对象,所以添加新属性不起作用,严格模式时还会报错。

const所说的一旦声明值就不能改变,实际上指的是:变量指向的那个内存地址所保存的数据不得改动

。简单类型(number、string、boolean):内存地址就是值,即常量(修改就报错)
。复杂类型(对象、数组等):内存地址保存的是一个指针,const只能保证指针是固定的(总是指向同一个
地址),它内部的值是可以改变的。

所以,只要不重新赋值整个数组/对象,const定义的变量时可以修改的,因为保存的是一个指针,所以对数组
用push、shift、splice等方法也是允许的。

复杂类型还有函数等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值