谈谈let与const
let 命令
let命令用于声明变量,但是与传统var命令的不同之处在于拥有以下特性:
- 使用let命令声明的变量只在let命令所在的代码块内有效(我将之称为变量绑定);
- 不存在变量提升;
- 存在暂时性死区;
- 不允许重复声明;
- 在全局声明,但不是全局对象的属性;
下面依次对这几个特性进行解释说明,并尝试探讨这几个特性的意义所在。
01 变量绑定
众所周知,在ES5规范中,JavaScript只有两种作用域:全局作用域与词法作用域(又称函数作用域或局部作用域)。也就是说,JavaScript中的变量要么被暴露在外,所有人都可以对其进行修改,要么就要被封装在函数体内,这样变量中的值就只有在函数内才可以获取或修正(除非使用闭包)。在漫长的开发实践中,开发者们逐渐发现基于以上两种作用域的变量绑定模式存在以下问题:
- function 关键字的过度使用(块级作用域);
有时候,我们的初心仅仅是想要安静的声明一些变量以供我们在某段代码中使用,并且不想冒着变量泄露全局的风险(这使我们的变量有可能覆盖到其他人的变量,或者被后来的其他人覆盖掉我们的变量,同时我们也不想维护有关变量名的长长的文档,把他们都变成一个个“关键字”)。在ES5规范中,我们的最佳实践是使用IIFE函数解决这一问题:
(function() {
var a = 1;
})();
console.log(1); // err
但是承认吧,这样的写法其实并不优雅不是吗,毕竟函数是“可执行的代码块”,而IIFE函数的唯一目的只是为了创建一个词法作用域环境。
于是,在ES6规范中,引入了块级作用域这个概念,写法只是简单的使用‘{}’表示,于是原先我们的IIFE函数就可以被替换为:
{
let a = 1;
};
console.log(a); // err
是不是优雅了很多?其实ES6规范包含了许多这样使我们的代码更加简洁,优雅的语法糖。有关块级作用域的其他知识,请见我的另一篇文章[谈谈块级作用域]。
而let关键字即是用来将变量”绑定“至所在的块级作用域,也就是说,在所在的块级作用域外,我们无法读取,修改使用let关键字声明的变量。
var关键字的声明的变量具有”变量提升“效果,这导致了一些问题:
var tmp = new Date();
function foo() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}
}foo(); // undefined
由于没有块级作用域,var关键字声明的tmp变量在代码初始化的过程中被提升至foo函数作用域顶端,覆盖了全局作用域的tmp变量,因此输出的tmp值为undefined,这显然不是我们想要的结果。而有了块级作用域后,tmp改用let关键字声明,该变量就会乖巧的待在自身的块级作用域内,console.log则可以正确的输出我们想要的结果。
用来计数的循环变量会泄露为全局变量
var s = 'hello';
for (var i = 0; i<s.length; i++ ) {
console.log(s[i]);
}console.log(i); // 5
在本段代码中,函数的实际解析过程是这样的:
var s = 'hello';
var i = 0;
while ( i<s.length) {
console.log(s[i]);
i++;
}
console.log(i); // 5
发现了吗,我们无意中创建了一个全局变量i,这样的结果可能是我们不想要的。
因此ES6使用了块级作用域与let关键字解决这个问题:
var s = 'hello';
for (let i = 0; i<s.length; i++) {
console.log(s[i]);
}
console.log(i); // error
看到了吗,原先的变量i不复存在,怎么做到的,我认为上面代码被解析为如下代码:
var s = 'hello';
{
let i = 0;
while ( i<s.length) {
console.log(s[i]);
i++;
}
}
console.log(i); // undefined
值得注意的是,let关键字声明的变量仅在每次循环中有效,即每循环一次,都会重新创建一个全新的块级作用域并且它享有自己的let变量,利用这个特点,我们可以实现以往通过闭包才能实现的效果:
var arr = [];
for (let i=0; i<10; i++) {
a[i] = function() {
console.log(i);
};
};
a[6](); // 6
02 不存在变量提升
let关键字声明的变量不存在var关键字声明的变量那样会出现”变量提升“现象。所有变量都需要先声明后使用,否则会报错(这也意味着使用typeof关键字不再安全)。
也许,变量提升使我们的代码能够更加灵活,但是这种灵活性却带来了很多潜在的问题,let关键字拒绝了这种灵活性,却使我们的代码更加健壮,规范。
先声明后使用啊,朋友,let如是说。
03 暂时性死区
我已经出生(存在)了,只是还没有长大(声明),等我长大(声明)之后,你才可以娶(获取)我。这就是暂时性锁区的含义,在块级作用域内,let关键字绑定的变量名在声明之前不可获取,赋值,修改。这看起来似乎更加合理,很奇怪ES5规范中为什么没有这样规定。
04 不允许重复声明
“嘿,你没必要给我取两次相同的名字!“如果你视图在一个块级作用域内多次使用let声明一个变量,你就能听到let关键字冲着屏幕外的你向你怒吼,是的,你真的没有必要那样做。醒醒吧。ES6规范增加这一条这是为了给你一个友善的忠告。
05 虽然在全局声明但不是全局对象的属性
全局对象是最顶层的对象,在浏览器环境值的是window对象,在Node.js中值的是global对象,在ES5中,全局对象的属性与全局变量是等价的。但在ES6中,使用let关键字, const命令,class命令声明的全局变量将不再属于全局对象的属性。
为什么这样做?道理很简单,为了保护变量不会被意外的修改,但随之而来的问题是,在全局声明的变量去了哪里?
let b = 1;
window.b; // undefined
const 命令
const命令用来声明常量,一旦声明,其值就不能改变。这也就意味着,const一旦声明常量,就必须立即初始化。
除此之外,const的作用域与let命令相同,也拥有let命令的五大特性(仅在所在代码块内有效,暂时性死区,不存在变量提升,不允许重复声明, 在全局内声明不是全局对象的属性)。
最后值得一提的是,就像变量存储引用类型值时,存储的实际上是一个指向内存地址的”指针“,const实际上也是如此,因此其”值不能改变“,也就意味着”指针“不能发生改变,也就是说,你仍然可以在const声明的对象上添加属性,因为并未改变其指针。
如果想要完全”冻结“一个引用类型值(不可以在对象上添加属性),你需要使用Object.freeze方法:
const obj = Object.freeze({});
obj.prop = 123; // doesn't work
除了冻结对象本身之外,要想实现”全面封冻“,你还需要连带对象的属性一并冻结:
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).foreach( (key, value) => {
if (typeof obj[key] === 'object') {
constantize(obj[key]);
}
});
};
谈谈let与const的更多相关文章
- JavaScript系列文章:谈谈let和const
JavaScript系列文章:谈谈let和const 最近接触到ES6的一些相关新特性,想借let和const两个命令谈谈JavaScript在变量方面的改进. 由于let和const有很多相似之 ...
- JavaScript:谈谈let和const
最近接触到ES6的一些相关新特性,想借let和const两个命令谈谈JavaScript在变量方面的改进. 由于let和const有很多相似之处,我们就先说一说let吧. 1. let添加了块级作用域 ...
- 浅析const、let与var
以前无论声明变量还是常量,总是使用var一勺端,知道接触了es6之后,发现原来变量.常量的声明其实是很讲究的. 这里简单来谈谈var.const与let. 1.var.var声明的变量没有块级作用域, ...
- dart之旅(一)
前言 最近在看 dart 了,本着 "纸上得来终觉浅,绝知此事 markdown" 的原则,准备边学边写,写一个系列,这是第一篇.学习过程中主要是参考 A Tour of the ...
- 【我的面试-01】Web前端开发实习岗-面试题总结
简单开头 首先技术面试官会根据简历里所写的项目和个人掌握技术栈提问(我不知道已经改过多少次简历了,因为前期投简历是真的是沉在茫茫大海,捞漂流瓶都捞不到的那种) 我的技术栈:(Vue还在苦苦的自学当中, ...
- openssl 1.1.1 reference
openssl 1.1.1 include/openssl aes.h: # define HEADER_AES_H aes.h: # define AES_ENCRYPT 1 aes.h: # de ...
- 谈谈以下关键字的作用auto static register const volatile extern
(1)auto 这个这个关键字用于声明变量的生存期为自动,即将不在任何类.结构.枚举.联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量.这个关键字不怎么多写,因为所有的变量默认就是 ...
- JavaScript系列文章:从let和const谈起
注册博客园账号也有好些年了,有事没事经常来逛逛,感觉博客园的同学们一直都很活跃,相比国内其他社区来讲,这里的技术氛围很浓,非常适合学习和交流,所以博主我也决定在这里驻扎了,在这里,博主希望能坚持写一些 ...
- 谈谈基于OAuth 2.0的第三方认证 [下篇]
从安全的角度来讲,<中篇>介绍的Implicit类型的Authorization Grant存在这样的两个问题:其一,授权服务器没有对客户端应用进行认证,因为获取Access Token的 ...
随机推荐
- Java自定义注解的定义与使用
Java注解 Annotation(注解)是JDK5.0及以后版本引入的.它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查.注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可 ...
- CentOS设置密码复杂度及过期时间等
我们在使用linux系统设置密码的时候,经常遇到这样的问题,系统提示:您的密码太简单,或者您的密码是字典的一部分.那么系统是如何实现对用户的密码的复杂度的检查的呢? 系统对密码的控制是有两部分(我知道 ...
- 20165330 2017-2018-2 《Java程序设计》第1周学习总结
教材学习内容总结 java的历史,地位,特点. java的平台介绍 java应用程序的开发及源文件的编写规则 java反编译特点 安装JDK Windows上 在安装JDK后设置系统环境变量,因为我的 ...
- sersync+rsync原理及部署
标签:sersync+rsync部署文档 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://liubao0312.blog.51ct ...
- 设计模式之Singleton模式
当程序运行时,有时会希望在程序中,只能存在一个实例,为了达到目的,所以设计了Singleton模式,即单例模式. 单例模式的特征: 想确保任何情况下只存在一个实例 想在程序上表现出只存在一个实例 示例 ...
- VMwareWorkstation与Device/CredentialGuard不兼容
win10的虚拟与VMware Workstation的虚拟有冲突,需要关闭win10自带的虚拟Hyper-V功能. 1.Windows键 --- 设置 --- 搜索 “控制面板” --- 程序 - ...
- style2paints、deepcolor、sketchkeras项目
数据集不够怎么办? 1 一些传统的边缘提取算法可以提取图像边缘. 2 这里我们有一个使用神经网络提取线稿图的项目——sketchkeras 源码:https://github.com/lllyasvi ...
- javaScript 调用构造函数 Array() 时没有使用参数, length总是0
如果调用构造函数 Array() 时没有使用参数,那么返回的数组为空,length 字段为 0. 当调用构造函数时只传递给它一个数字参数,该构造函数将返回具有指定个数.元素为 undefined 的数 ...
- Spring-BeanFactory容器
Spring的BeanFactory容器 这是Spring中最简单地容器,它主要的功能是为依赖注入(DI)提供支持.这个容器接口在org.springframework.beans.factory.B ...
- 0605-Zuul构建API Gateway-使用Sidecar支持异构平台的微服务
使用非jvm语言 参看地址:https://cloud.spring.io/spring-cloud-static/Edgware.SR3/single/spring-cloud.html#_poly ...