1-1 变量的声明方式 var let const

176 阅读5分钟

大家好,我们学习的是ES6也就是 ES2015这样的一个版本,我们一起来学一下在ES6里面都有哪些新特性可以供我们去使用。

在ES5 和 ES5之前我们使用的声明方式叫 var,为什么在有var 的情况下又推出了let 跟const呢?三者有什么区别呢?下面我们来一起看一下吧。

1. 顶层对象window

    // 大家看以下代码都是正常输出,那写var跟不写var两者到底有什么区别呢?
    var  a = 1 
    console.log(a)  // 1
    b = 2
    console.log(b)  // 2
  • 当我们使用 var 关键字定义的一个变量的时候,相当于在当前的作用域内声明的一个变量
  • 不使用var的话相当于是一个属性进行赋值,在window这个全局对象上面定义了一个属性叫做b

下面我们使用 delete 删除一下对象的属性验证一下 (delete 只能删除对象的属性,并不能删除变量)

    var  a = 1 
    delete a
    console.log(a)  // 1
    b = 2
    delete b
    console.log(b)  // b is not defind
  • 以上我们看到使用 delete 之后 使用 var 声明的变量还存在, 而未使用 var 声明的变量提示 b is not defind,说明不使用var 直接声明的变量是一个全局对象上面的属性

下面我们再看一段代码

    var  a = 1 
    console.log(window.a)  // 1
    b = 2
    console.log(window.b)  // 2

为什么var 声明的变量也会出现在window 上面呢?

  • 这个是因为JS作者在设计初期的时候,把顶层对象的属性和全局变量进行了挂钩,这个被很多人认为这是JS语言最大的设计败笔之一。

下面我们使用let 来看一下这个问题

    let  a = 1 
    console.log(a)  // 1
    console.log(window.a)  // undefined
  • let 弥补了 var 对全局变量和顶层对象的属性进行挂钩的问题
  • 由此我们得出 let 定义的变量不属于顶层对象 window,也不会出现引起污染全局变量这样的问题

2. 对象的重复声明

var 是允许重复声明的,而且后定义的会把先定义的覆盖掉,下面我们来输出看一下

    var a = 11
    var a = 22
    console.log(a) // 22

下面我们换成 let 看一下

    let a = 11
    let a = 22
    console.log(a) // Identifier 'a' has already been declared

我们看到控制台报错了,提示 a 这样的修饰符已经被定义过。

  • let 不允许重复声明变量

3. 变量提升

var 定义的变量具有变量提升

    console.log(a)  // undefined
    var a = 11
    // -----相当于先定义了一个变量 a 并没有赋值,下面对 a 再进行一个赋值-----
    var a
    console.log(a)
    a = 11

下面我们换成 let 看一下

    console.log(a)  // Cannot access 'a' before initialization
    let a = 11

我们看到控制台报错了,提示初始化前不能访问 a

  • let 定义的变量不存在变量提升

4. 块级作用域

在 var 定义的变量并没有块级作用域,一个{} 就可以形成一个块级作用域,相当于我在这{}定义的变量,只能在这一块使用。

下面我们来用代码演示一下

for(var i = 0; i < 3; i++){
    console.log(i)  // 0, 1, 2
}
console.log(i)   // 3
  • 在ES 5 里面只有全局作用域和函数作用域没有块级作用域,所以说在循环外面可以取到 i,并且是循环结束之后的 i ,所以 i 的值为 3

下面我们换成 let 看一下

for(let i = 0; i < 3; i++){
    console.log(i)  // 0, 1, 2
}
console.log(i)   // i is not defined
  • let 具有块级作用域

let

  • 不属于顶层对象 window
  • 不允许重复声明
  • 不存在变量提升
  • 块级作用域
  • 暂时性死区(所有的变量必须要先定义再去使用)

const

使用 const 定义常量,用于声明常量,常量不允许被修改

  • 定义的变量不属于顶层对象 window
  • 不允许重复声明
  • 不存在变量提升
  • 同样也是具有块级作用域的
  • 常量声明后不可修改
  • 必须声明时候赋值
// ---------定义的变量不属于顶层对象 window---------
let a = 1
console.log(a) // 1
console.log(window\.a) // undefined

// ---------不允许重复声明---------
const a = 11
const a = 22
console.log(a) // Identifier 'a' has already been declared

// ---------不存在变量提升---------
console.log(a)  // Cannot access 'a' before initialization
const a = 11

// ---------具有块级作用域---------
{
    const a = 1
}
console.log(a) // a is not defined

// ---------常量声明后不可修改---------
const a = 1
a = 2  // Assignment to constant variable.

// ---------必须声明时候赋值---------
const a
a = 2   // Missing initializer in const declaration

如果我们使用const 定义引用类型会有什么效果呢?

const obj = {
    name: "小白",
    age: 4,
}
console.log(obj)  // {name: '小白', age: 4}
obj.color = '黑色'
console.log(obj) // {name: '小白', age: 4, color: '黑色'}

const 不是定义的常量吗?不是不能被改变吗?为什么还能添加 color 呢? 首先我们要先明白两个概念:

  • 基本数据类型是存储在栈内存的
  • 引用数据类型是存在堆内存里面的,在栈内存里面存的是一个引用的地址,指向堆内存

所以变量指向的栈内存的引用地址并没有改变,改变的只不过是堆内存里面的存的内容

什么时候使用let? 什么时候使用const 呢?

当你定义当前值的时候需要考虑一下,在后面的逻辑中是否会改变 如果当前这个值在后面要进行逻辑业务的运算,要给他赋一个新的值,就使用 let 如果这个值一直是初始值,不会被改变的情况下是就用const