var,let和const深入解析(二)
你想在在变量声明之前就使用变量?以后再也别这样做了。
新的声明方式(let,const)较之之前的声明方式(var),还有一个区别,就是新的方式不允许在变量声明之前就使用该变量,但是var是可以得。请看下面的代码,下面这个代码是可以正常运行的:
function func() { console.log(localVariable); // undefined var localVariable = 5; console.log(localVariable); // 5 } func();
但是这种却不可以
function func() { console.log(localVariable); // ReferenceError: localVariable is not defined let localVariable = 10; console.log(localVariable); // 10 } func();
等下,我们上一章曾经介绍了一个叫“提升”的概念,它会吧所有的变量定义在作用域的最前面。这是否意味着如果我不在实际的定义之前使用变量,然后就不会有提升了呢?答案是否定的。提升依然会有,并且适用于所有类型的变量类型。但是const和let却不是这样的。
首先,我们看一下var关键字是怎么工作的。规范对其是这样进行的描述的。
var声明定义了在正在运行的执行上下文(running execution context)作用域内的变量环境(VariableEnvironment中)的变量。var变量在当包含的词法环境(Lexical Environment)初始化时被创建,在创建的时候被赋值为undefined。[...] 在执行VariableDeclaration时,由带有Initializer的VariableDeclaration定义的变量被赋其设定项的Initializer's AssignmentExpression的值。
规范中有许多的细节,让我们简单的来看一下:
当你进入到一个作用域中,在内部被定义的所有的变量都会被创建。
所有存在的变量,都可以被访问,并且会把undefined赋值给该变量。
当代码(执行时)到达初始化时,会被分配给一个实际的值。
我们来看一下规范中对let和const的表述:
let和const声明是定义在当前执行上下文作用域中的词法环境中的变量。当包含的词法环境被初始化的时候,变量被创建。但是在变量的词法绑定时被计算之前是不允许通过任何方式来访问的。当词法绑定计算时而不是在变量被创建的时候,由词法绑定定义的变量的初始值被被赋予赋值表达式的值(也就是“=”右边的表达式)。当词法绑定被计算的时候,如果let声明中没有初始化的值的时候(也就是“let a;”这样的形式),会被赋值为undefined。
简单来说:
如果你进入到了指定的作用域中,它里面定义的所有的变量都会被初始化,这一点和var很像。
这里有一个不同点:像var一样,所有的变量都会存在,但是他们目前还不能被访问(里面没有值,甚至是undefined)。
如果let变量在相同的地方被定义和初始化,他们会被赋予合适的值,反之,变量就是undefined。const变量必须在定义的时候初始化。
我们来看一些相关的例子。
临时死区
实际上,这种描述引出了我们的另一个定义。他很让人可怕,因为他叫:临时死区(TDZ)。这个属于明确了一个我们无法访问我们的变量的代码的区域。我们来看一下下面的代码和相关联的注释,来简单的解释一下TDZ是什么。
function func() { // Start of TDZ for deadVariable // we can still do something here, just our deadVariable is not available yet const exampleVariable = 5; console.log(exampleVariable); // 5 // End of TDZ for deadVariable let deadVariable = 10; console.log(deadVariable); // 10 } func();
有一件事情值得去提醒。就是对于名字的建议,这是一个临时死区,意思这个区域是由时间定义的,而不是位置。因此当运行代码的时候,你的声明在被JS解析器解析之前是不能被访问的。因此你把使用的变量的位置放在哪里并不重要,只要是在声明执行后访问该变量就可以。所以看下面的代码:
function func() { return deadOrAlive; } let deadOrAlive = 'alive!' console.log(func()); // alive!
这是运行代码的步骤:
函数被声明
变量deadOrAlive被声明,并且初始化了一个值“alive”
现在我们调用我们的函数。
由于变量deadOrAlive已经被声明,是可访问的,因此会打印出正确的结果 “alive”。
但是下面的例子却会报错,思考一下原因。
function func() { return deadOrAlive; } console.log(func()); // ReferenceError: deadOrAlive is not defined let deadOrAlive = 'dead!'
所以TDZ是一个避免因先使用后声明而导致的一些诡异的bug而出现的一个很好机制(具体看“提升”相关内容)。我们不需要去额外做什么事情,就是记住永远不要在变量声明之前使用这个变量。即使我们这样做了,我们也会得到一个很好的报错信息。只有一个条件-你必须使用let或者是const来替换掉var。
双定义
var和let,const的另一个区别是 - 后者仅仅可以被定义一次。而对于var的话,如果被同时定义多次,程序也依然会很好的运行。
var doubledVariable = 5; var doubledVariable = 6; console.log(doubledVariable); // 6
但是现在,当你用let和const来做同样的事情,就会得到一个语法错误:
let doubledVariable = 5; let doubledVariable = 6; // SyntaxError: Identifier 'doubledVariable' has already been declared
但是,在嵌套的块级作用域中,使用相同名字的变量依然会很好的工作的,这个我想大家已经清楚了,就不用过多解释了吧。
let doubledVariable = 5; if (true) { let doubledVariable = 6; console.log(doubledVariable); // 6 } console.log(doubledVariable); // 5
不能重复定义这个功能实际上是很有用的,可以组织很多bug的发生。比如说你曾经在一个函数内,在不同地方用var定义了多个相同名称的变量,此时之前定义的变量可能会被覆盖,这样对于代码来说无疑是一个隐患,也就是因为这样,这个特性实际上是一个简单的,开箱即用的解决方案。
总结
总结一下,在ES6中有两种新方法来声明变量:通过let和const关键字,除此之外,两者都是块级作用域,并且在声明之前不能访问该变量。与之前的var相比是一个主要的升级。并且会消除你很多的困扰。我提出了几个例子,可能会帮助你节省了不少调试的时间,但是还有更多。如果你感兴趣的话,可以在网上简单的搜索一下。很久之前,我个人曾建议停止使用var关键字,所以现在我的代码里充满了let和const。我建议你也是这样,在以后当你想改变变量的值,就使用let和const。不要再使用var了。
本文翻译自:
https://blog.pragmatists.com/let-your-javascript-variables-be-constant-1633e56a948d
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
python : 动态不刷屏幕输出 python/shell 实现
前言后台运行程序有一种需求,比如查看当前进度,想在终端看到某个值的变化情况: 先提供一种很土的办法,把进度落地文件为 例如 process,采用创建写的方式。然后可以使用watch -n 1 cat process来查看进度。 这里提供两种方式,python和shell shell版本,如下(附带一个进度条的例子) #! /bin/bash for ((i=0; $i<=100; i+=1)) do printf "progress: [%-100s] %d%%\r" "xxxxxxxxxx xxx xxx" $i sleep 1 done function sleepPrograss(){ [ $# -eq 0 ] && echo "sleepPrograss Usage: sleepPrograss 10 " [ $# -eq 0 ] && return 1 allTime=$1 strDone='' stepTime=$(echo "scale=1; $allTime/100" | bc) for ((i=0; $i<=100; i+=...
- 下一篇
基于django的视频点播网站开发-step8-后台登录功能
从本讲起,我们会介绍后台管理系统的开发,后台管理,主要是对数据库中的数据进行增、删、改、查的操作,满足网站管理员对网站的管理与维护的需求。 其实,django自带的也有一个后台管理系统(/admin),但是自带的后台非常简陋,无论是界面,还是功能上,都无法满足用户的需求,因此,我们自己开发了一套后台管理系统。 demo地址 测试账号: admin123 密码: admin123 后台管理属于一个单独的模块,我们创建一个新的应用,命名为myadmin python3 manage.py startapp myadmin 好,之后的功能都是基于myadmin来实现的。 因为前面我们已经创建了user模块,所以此处的登录功能是基于之前的user模块来实现的。 首先在myadmin/urls.py中添加登录和登出的路由 from django.urls import path from . import views app_name = 'myadmin' urlpatterns = [ path('login/', views.login, name='login'), path('logo...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- 设置Eclipse缩进为4个空格,增强代码规范
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7安装Docker,走上虚拟化容器引擎之路
- Mario游戏-低调大师作品
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2更换Tomcat为Jetty,小型站点的福音