资讯专栏INFORMATION COLUMN

【ES6系列】变量与块级作用域

PascalXie / 2482人阅读

摘要:不允许在相同作用域内,重复声明同一个变量。如但是在中则不再必要了,我们可以通过块级作用域就能够实现本次主要针对中的变量和块级作用域进行了梳理学习,并且通过与的实现方式进行了对比,从而看出其变化以及快捷与便利。

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

ES6的出现已经很久了,由于之前的项目只是基于ES5以及之前的版本内容进行开发的,所以没有系统性的梳理学习ES6的相关知识。感觉自己已经被发展的车轮落下了好远好远(@﹏@)~,所以在接下来这段时间针对性的进行学习整理,希望能够让自己有所提高,也希望能帮助和我一样的人……

变量 常量 const命令 常量的定义

在之前的版本ES5甚至更早的版本(后面统称ES5)中,很少能够听到常量的概念,我们也知道,JS不同于其他的语言,如Java、C++等,有常量的概念。虽然通过其他的方法能够达到实现常量的方法,但是并不直接、方便。

如可通过下述方式进行常量的实现:

// 此处在window对象上面定义了一个圆周率变量PI,使之不能被修改,模拟常量
Object.defineProperty(window, "PI", {
    value: 3.1415926,
    writable: false
})

在ES6中,定义一个常量则非常简单,通过const进行声明即可。

const声明一个只读的常量。一旦声明,常量的值就不能改变。
const PI = 3.1415926;

const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const定义常量的本质

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。

如:

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
let命令

在ES6中新增了let命令,可以用来声明一个变量,作用类似于之前常用的var,但是和var声明的变量其有效的作用域不同,let声明的变量只在let命令所在的代码块内有效。

如:

{
    let a = 1;
    var b = 2;
}
console.log(a); // Uncaught ReferenceError: a is not defined
console.log(b); // 2

通过上面代码的执行结果来看能够看出,let声明的变量只在它所在的代码块中有效,即{}所在的代码块内。

那么let声明的变量在什么情况下会比较有用呢?先让我们来看下下面一段代码:

var arrays = [];
for (var i = 0; i <= 2; i++) {
    arrays[i] = function() {
        return i * 2;
    }
}

console.table([
    arrays[0](),
    arrays[1](),
    arrays[2]()
])

可能我们预期的结果是显示0,2,4,但是从执行代码的结果中能够看出,得到的却是6,6,6,这是为什么呢?
首先我们上面是使用的var声明的一个i变量,此处会有一个变量提升,相当于在for循环外面声明了var i = 0变量。当在for循环中时,arrays[i]中的i即为每次循环的i的值(0,1,2),但是在函数体中并没有将i变量取值为每次循环的值,它只是对变量的一个引用,并不是值的引用。当在执行循环中的函数体时,i变量已经变成了3,所以每次的结构输出都是6。这个时候,ES6中的let命令就能够满足我们的要求了。

var arrays = [];
for (let i = 0; i <= 2; i++) {
    arrays[i] = function() {
        return i * 2;
    }
}

console.table([
    arrays[0](),
    arrays[1](),
    arrays[2]()
])

上面代码中变量i只在for循环体内有效,只在其所在的块作用域内有效,每次for循环时会将i变量的值保存在其对应的块作用域中(for循环的{}),每一轮循环都是重新生成一个新的作用域,在执行for循环中声明的函数时,都会获取其对应作用域内的i的值。这部分涉及到的作用域相关的概念,我们后面会说到。

1、在前面提到过,var命令声明的变量会发生变量提升的现象,即变量可以在声明之前使用,值为undefined。按照一般的逻辑来说,变量应该在声明语句之后才可以使用。所以在ES6中为了纠正这个现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。

2、只要在块级作用域中存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

var tmp = 123;

if (true) {
  tmp = "abc"; // ReferenceError
  let tmp;
}

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

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

3、typeof不再是一个完全安全的操作。之前我们知道,针对一个没有生命的变量调用typeof方法时会返回undefined,但是在上面出现的“暂时性死区”中,typeof将会被报错。

使用let声明变量时,只要变量在还没有声明完成前使用,就会报错。

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

块级作用域 为什么需要块级作用域

1、内层变量可能会覆盖外层变量

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = "hello world";
  }
}

f(); // undefined

内层的tmp变量由于变量提升覆盖了外层的tmp变量,导致输出不符合预期

2、计数的循环变量泄露为全局变量

我们上面用到的for循环的例子就是这个现象的证明,var声明的变量i在循环结束后泄露成了全局变量。

ES6的块级作用域

在ES6中{}包裹的块即为一个块级作用域,并且块级作用域支持嵌套,如

{{{{{let insane = "Hello World"}}}}}; // 5层块级作用域

并且外层作用域不能读取内层作用域的变量;
内层作用域可以定义外层作用域的同名变量。

在之前的版本中,当我们想要实现一个变量只在一个代码块中运行,我们往往需要通过使用立即执行函数来实现。如

(function () {
  var tmp = ...;
  ...
}());

但是在ES6中则不再必要了,我们可以通过块级作用域就能够实现

{
  let tmp = ...;
  ...
}
本次主要针对ES6中的变量和块级作用域进行了梳理学习,并且通过与ES5的实现方式进行了对比,从而看出其变化以及快捷与便利。希望能够对您有所帮助,如有写的不对的地方望请指正,从而共同进步。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107672.html

相关文章

  • ES6 变量作用与提升:变量的生命周期详解

    摘要:不同的是函数体并不会再被提升至函数作用域头部,而仅会被提升到块级作用域头部避免全局变量在计算机编程中,全局变量指的是在所有作用域中都能访问的变量。 ES6 变量作用域与提升:变量的生命周期详解从属于笔者的现代 JavaScript 开发:语法基础与实践技巧系列文章。本文详细讨论了 JavaScript 中作用域、执行上下文、不同作用域下变量提升与函数提升的表现、顶层对象以及如何避免创建...

    lmxdawn 评论0 收藏0
  • ES6系列文章 块级作用

    摘要:声明之函数作用域和全局作用域。块级作用域不能重复声明临时性死区等特性用来解决变量存在的种种问题。块级作用域终于在外面访问不到了。一些常量声明使用声明的变量名全部大写。 ES5之前javascript语言只有函数作用域和全局作用域,使用var来声明变量,var声明的变量还存在变量提升使人困惑不已。我们先来复习一下ES5的var声明,再对比学习let和const 。 var var声明之函...

    赵连江 评论0 收藏0
  • ES6 系列之 let 和 const

    摘要:块级作用域存在于函数内部块中字符和之间的区域和块级声明用于声明在指定块的作用域之外无法访问的变量。和都是块级声明的一种。值得一提的是声明不允许修改绑定,但允许修改值。这意味着当用声明对象时没有问题报错临时死区临时死区,简写为。 块级作用域的出现 通过 var 声明的变量存在变量提升的特性: if (condition) { var value = 1; } console.lo...

    PascalXie 评论0 收藏0
  • ES6学习总结(1)

    摘要:返回一个对象,遍历对象自身和继承的所有可枚举属性不含,与相同和在红宝书中就已经提到过属性,表示的是引用类型实例的一个内部指针,指向该实例的构造函数的原型对象。 半个月前就决定要将ES6的学习总结一遍,结果拖延症一犯,半个月就过去了,现在补起来,惭愧惭愧。 阮一峰的《ES6标准入门》这本书有300页左右,除了几个新的API和js语法的扩展,真正有价值的内容并不多。所谓存在即合理,每部分的...

    happyfish 评论0 收藏0
  • JavaScript基础系列---变量及其值类型

    摘要:但对于引用类型的数据主要是对象和数组,变量指向的内存地址,保存的只是一个引用地址指针,只能保证这个引用地址指针是固定的,至于它指向的堆内存中的存储的值是不是可变的,就完全不能控制了。 基础概念 变量是存储信息的容器,这里需要区分一下:变量不是指存储的信息本身,而是指这个用于存储信息的容器,可以把变量想象成一个个用来装东西的纸箱子 变量需要声明,并且建议在声明的同时进行初始化,如下所...

    sugarmo 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<