(Day 22) ES6 的 let 、const

前言

在 ES6 新增两种变数方法 letconst ,不过我们比较常把 const 叫做常数,主要是因为 var 的特性 容易触发 Bug , 这边与 var 的差异主要有:

  • 作用域范围不同
  • 提升特性不同
  • 全域不挂在 window 下

在提及这些不同特性之前,先大致介绍一下这两个用法的差别。

let 、 const 基本介绍

let 能够重新赋值,但不能重复宣告

let String1 = 'test1'
String1 =  'test2'
let String1 = 'test3' // 重复宣告错误, Uncaught SyntaxError: Identifier 'String1' has already been declared

const 不能重新赋值,也不能重新宣告。

const String1 = 'test1'
const String1 = 'test2' //  重复宣告错误, Uncaught SyntaxError: Identifier 'String1' has already been declared
String1 = 'test3' // 赋值错误, Uncaught TypeError: invalid assignment to const 'String1'

要补充一点如果 const 的值是物件,对物件底下的属性赋值, const 则能接受这种操作继续使用。

const obj = {}
obj.name = 'Ryder'
console.log(obj) // { name: 'Ryder' }

const array = []
array.push(1)
console.log(array) // [1]

作用域不同

在过去 var 作用域是根据函式作用域,而 letconst 则是以 { ... } Block 区块做为作用域,来看看以下范例:

var name1 = 'Ryder'
let name2 = 'Ryder'

function test(){
	var name1 = 'Jack'
	let name2 = 'Jack'
}
test()
console.log(name1, name2)  // ?

结果是 Ryder, Ryder ,这范例很好理解,不论是 var 还是 letname = 'Jack' 的作用范围都只在 test() 这个函式中,console 的位置则是在全域,自然都会是 Ryder ,那麽再来看看这个范例:

var name1 = 'Ryder'
let name2 = 'Ryder'

{
 var name1 = 'Jack'
}

{
 let name2 = 'Jack'
}
console.log(name1 , name2)  // ?

结果会是 Jack, Ryder ,这是因为上面提到的, let 是以 { ... } 区块做为作用域,因此 let name1 = 'Jack' 这个语法的有效范围只会存在於 { ... } 之中,而 var 则会被 { ... } 中的 var name1 = 'Jack' ,直接做替换,因此 name1 会是 'Jack'

这边要补充一下, 这边提到的 { ... } 并不是物件,而是一个作用域范围,主要是为了搭配 letconst 特性 ES6 才引入的,不过实做中通常不会刻意使用 { ... } 去区分作用域。

提升特性不同

在提升章节我们有提到,JavaScript 在编译程序码时,会分为两个阶段:
1.创造阶段
2.执行阶段
var 变数会先在 创造阶段 被建立,进入执行阶段才会实际赋值,而在创造阶段中的 var 变数,他的值会是 undefined ,如这个范例:

console.log(name1)  // undefined
var name1 = 'Ryder'

let 虽然也有提升概念,也同样分成:
1.创造阶段
2.执行阶段
但在创造阶段和 var 不同,let 在创造阶段不是直接显示 undefined ,他是进入一个 暂时性死区 (TDZ) 的状况,MDN 文件是这麽描述的:

The variable is in a "temporal dead zone" from the start of the block until the initialization is processed

如果我们在 暂时性死区 的状态去取得 let 变数的值,浏览器会跳错,要注意的是,不同浏览器跳出的错误讯息会不同,如下范例:

FireFox 版本

console.log(name2)  // Uncaught ReferenceError: can't access lexical declaration 'name1' before initialization
let name2 = 'Ryder'

Chrome 版本

console.log(name2)  //Uncaught ReferenceError: name1 is not defined
let name2 = 'Ryder'

全域建立变数

上面有提到到 letconst 是根据 { ... } 来区分作用域的,这边要提一点的是 var 全域建立时,会是挂在 window 下。

而使用 letconst 在全域建立变数时,他并不会挂在 window 下,但我们若直接呼叫变数,他也会正确显示,如范例

var name1= 'Ryder'
let name2 = 'Ryder'

window.name1 // 'Ryder'
window.name2 // undefined

name1 // 'Ryder'
name2 // 'Ryder'

这个原因是出在全域执行环境(Global space) 上面,首先这个全域执行环境其实是由两个环境所组成的

  • 全域物件 - Object Env
  • 宣告环境 - Declare Env

因此全域执行环境(Global space) 其实是一个由双环境组成的东西,一般来说我们是看不到 Declare Env 的。

所以 var 其实是基於 ObjectEnv 宣告并加入到 Declare Env,而 letconst 则是只会宣告在 Declare Env 中,这也就是为什麽我们无法在 Window 上面看到由 letconst 宣告的变数但却又可以正常取得到值的原因。

参考文献


<<:  模型架构--1

>>:  Day 7 - 浅谈Laravel资料库关联的运用

DAY 28『 使用相机拍照 』 ImagePicker - Part2

继昨天的实作练习,有相机,会执行以下的程序码: 令 vc 为 UIImagePickerContro...

冒险村24 - Design Pattern(4) - Calculator

24 - Design Pattern(4) - Calculator Calculator 的 d...

[Day26] swift & kotlin 游戏篇!(8) 小鸡BB-游戏制作-历史纪录

Swift 游戏示意 Swift 游戏纪录 最後一个功能是游戏纪录 修改一下Player.swift...

27. Tech leader的重要战略

前言 这篇的讲者很nice,直接讲了这篇演讲很适合给这几种人看 刚成为TL 还不是TL但你觉得你会...

Day 28:PVE VM 内 CPU、GPU 效能评测

前言 在安装过後,今天我们来测试 VM 的相关效能。 CPU 效能测试 我们采用一套有天梯的效能测试...