谈谈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的 ...
随机推荐
- 160419、CSS3实现32种基本图形
CSS3可以实现很多漂亮的图形,我收集了32种图形,在下面列出.直接用CSS3画出这些图形,要比贴图性能更好,体验更加,是一种非常好的网页美观方式. 这32种图形分别为圆形,椭圆形,三角形,倒三角形, ...
- myeclipse 代码提示(alt+/)
windows -->preference-->general-->keys找到 alt+/ 解除绑定 windows -->preference-->general-- ...
- Spoken English Practice(And I can't walk. Should i just stay home and pout about it?)
绿色:连读: 红色:略读: 蓝色:浊化: 橙色:弱读 下划线_为浊化 口语蜕变(2017/7/12) ...
- datasnap rest Windows客户端编写
首先吐槽一下XE关于datasnap的资料真的是太少了... 服务端用DSHTTPService1控件可以实现http接口方式的调用,返回的都是json格式数据,适用于跨平台解析. 这里着重强调一下d ...
- Javascript闭包学习(Closure)
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 下面就是我的学习笔记,对于Javascript初学者应该是很有用的. 一.变量的作用域 要理解 ...
- Django源码安装方法及创建启动项目
一.源码安装方法 下载源码包:https://www.djangoproject.com/download/ 输入以下命令并安装: tar xzvf Django-X.Y.tar.gz # 解压下载包 ...
- 一条SQL引起的雪崩
1.问题描述 MySQL服务器卡死,CPU飚到300%多,命令执行缓慢. 2.问题定位 踩了狗屎运,直接找到了问题缘由 发现了一条SQL写的模糊匹配,将%写在了关键字的前面,这样会造成查询不使用索引, ...
- Django自定义模板函数
Django自定义模板函数 https://www.cnblogs.com/SunsetSunrise/p/7680491.html 在django中新建一个应用:listpage在listpage中 ...
- SSO之CAS基础及应用视频教程(1)
CAS介绍 CAS = Central Authentication Service,中央认证服务.CAS 是 Yale 大学发起的一个开源项目,能够为 Web 应用系统或者非Web应用系统提 ...
- idea配置mave
历史信息为同意管理,本博客是从小号迁移而来,‘会飞的鱼la’已经注销 准备工作1.下载idea并激活http://idea.iteblog.com/key.php 2.下载mavenhttp://mi ...