ES6(2015)新的声明方式 letconst

Posted 优小U

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6(2015)新的声明方式 letconst相关的知识,希望对你有一定的参考价值。

1. 作用域

常见的作用域主要分为几个类型:全局作用域、函数作用域、块状作用域、动态作用域。

对象类型
global/window全局作用域
function函数作用域(局部作用域)
{}块状作用域
this动态作用域

全局作用域
变量在函数或者代码块 {} 外定义,即为全局作用域。不过,在函数或者代码块{} 内未定义的变量也是拥有全局作用域的(不推荐)。

var course = "es"

// 此处可调用 course 变量
function myFunction() {
    // 函数内可调用 course 变量
}

如果变量在函数内没有声明(没有使用 var 关键字),该变量依然为全局变量。

// 此处可调用 course 变量

function myFunction() {
    course = "es"
    // 此处可调用 course 变量
}

以上实例中 course 在函数内,但是拥有全局作用域,它将作为 global 或者 window 的属性存在。

函数作用域
在函数内部定义的变量,就是局部作用域。函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域!

function bar() {
    var testValue = 'inner'
}

console.log(testValue) 
// 报错:ReferenceError: testValue is not defined

如果想读取函数内的变量,必须借助 return 或者闭包。

function bar(value) {
    var testValue = 'inner'

    return testValue + value
}

console.log(bar('fun')) // "innerfun"

闭包的方式:

function bar(value) {
    var testValue = 'inner'

    var rusult = testValue + value

    function innser() {
        return rusult
    }

    return innser()
}

console.log(bar('fun')) // "innerfun"

return 是函数对外交流的出口,而 return 可以返回的是函数,根据作用域的规则,函数内部的子函数是可以获取函数作用域内的变量的。

块状作用域
在ES6之前除了全局作用域就是函数作用域,一直没有自己的块状作用域。在 ES6 中已经改变了这个现象,块状作用域得到普及。关于什么是块,只要认识 {} 就可以了。

if (true) {
    let a = 1
    console.log(a)
}

在这个代码中, if{}就是“块”,这个里面的变量就是拥有这个块状作用域,按照规则, {}之外是无法访问这个变量的。

动态作用域
javascript 中很多同学对this的指向时而清楚时而模糊,其实结合作用域会对this 有一个清晰的理解。不妨先来看下这段代码:

window.a = 3

function test() {
    console.log(this.a)
}

test.bind({
    a: 2
})() // 2
test() // 3

在这里bind已经把作用域的范围进行了修改指向了 { a: 2 },而 this 指向的是当前作用域对象。

变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。 相反,只能在执行阶段才能决定变量的作用域,那就是动态作用域。

2. let

ES6 新增了let命令,用来声明变量。

1.let 声明的全局变量不是全局对象window的属性

var a = 5
console.log(window.a) // 5
let a = 5
console.log(window.a) // undefined

2.用let定义变量不允许重复声明

var a = 5
var a = 6

console.log(a) // 6
let a = 5
let a = 6
// VM131:1 Uncaught SyntaxError: Identifier 'a' has already been declared
//   at <anonymous>:1:1

3.let声明的变量不存在变量提升

function foo() {
    console.log(a)
    var a = 5
}

foo() //undefined

上述代码中,a 的调用在声明之前,所以它的值是 undefined,而不是 Uncaught ReferenceError。实际上因为 var 会导致变量提升。而对于 let 而言,变量的调用是不能先于声明的:

function foo() {
    console.log(a)
    let a = 5
}

foo()
// Uncaught ReferenceError: Cannot access 'a' before initialization

4.let声明的变量具有暂时性死区
只要块级作用域内存在let命令,它所声明的变量就绑定在了这个区域,不再受外部的影响。

var a = 5
if (true) {
    a = 6
    let a
}
// Uncaught ReferenceError: Cannot access 'a' before initialization

上面代码中,存在全局变量 a ,但是块级作用域内let又声明了一个局部变量a,导致后者绑定这个块级作用域,所以在let声明变量前,对a 赋值会报错。

ES6 明确规定,如果区块中存在 letconst 命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。

5.let 声明的变量拥有块级作用域

let实际上为 JavaScript 新增了块级作用域

{
    let a = 5
}
console.log(a) // undefined

a 变量是在代码块 {} 中使用let定义的,它的作用域是这个代码块内部,外部无法访问。

3. const

不能被改变的叫做常量,ES5中定义一个常量:

Object.defineProperty(window, 'PI', {
    value: 3.14,
    writable: false
})
console.log(PI)
PI = 5
console.log(PI)

const除了具有let的块级作用域和不会变量提升外,还有就是它定义的是常量,在用 const定义变量后,我们就不能修改它了,对变量的修改会抛出异常

const PI = 3.1415
console.log(PI)
PI = 5
console.log(PI)
// Uncaught TypeError: Assignment to constant variable.

这个代码块中因为对 PI 尝试修改,导致浏览器报错,这就说明 const 定义的变量是不能被修改的,它是只读的。

注意:const 声明的变量必须进行初始化,不然会抛出异常 Uncaught SyntaxError: Missing initializer in const declaration。

需要注意的是,const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

const obj = {
    name: 'xiaoming',
    age: 18
}
obj.school = 'qinghua'
console.log(obj)
// {name: "xiaoming", age: 18, school: "qinghua"}

虽然定义的对象是const,但是只是保证地址值不变,地址指向的内存空间的值是可以变的。

如何让对象或者数组这种引用数据类型也不被改变呢?

Object.freeze(obj)
// 只是浅层冻结,只会对最近一层的对象进行冻结,并不会对深层对象冻结。

以上是关于ES6(2015)新的声明方式 letconst的主要内容,如果未能解决你的问题,请参考以下文章

ES6基础之letconst

ES6中letconst区别保证看完不后悔,印象还深刻

ES6中letconst区别保证看完不后悔,印象还深刻

ES6 常用总结——第一章(简介letconst)

ES6学习笔记(letconst变量的解构赋值字符串扩展)

letconst和var的区别(涉及块级作用域)