1. 首页
  2. IT资讯

「ES6基础」let和作用域

“u003Cdivu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F8b98e8f49c8b4e218b1aed0fe7d69446″ img_width=”900″ img_height=”383″ alt=”「ES6基础」let和作用域” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E本篇文章小编将带着大家一起学习如何使用新的语法声明变量——let。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E你将会学到以下内容:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Culu003Eu003Cliu003Elet基本介绍u003Cu002Fliu003Eu003Cliu003E作用域介绍u003Cu002Fliu003Eu003Cli class=”ql-indent-1″u003E作用域u003Cu002Fliu003Eu003Cli class=”ql-indent-1″u003E全局作用域和函数作用域u003Cu002Fliu003Eu003Cli class=”ql-indent-1″u003E块级作用域u003Cu002Fliu003Eu003Cliu003Evar和let的区别u003Cu002Fliu003Eu003Cliu003E重复定义变量的问题u003Cu002Fliu003Eu003Cliu003E提升概念的问题u003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E本篇文章阅读时间预计u003Cstrongu003E10分钟u003Cu002Fstrongu003E。u003Cu002Fpu003Eu003Ch1u003Eu003Cstrongu003Elet介绍u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fdfic-imagehandleru002Fbbfb04d9-8f1f-4c74-8925-317edbfbe0f3″ img_width=”1200″ img_height=”855″ alt=”「ES6基础」let和作用域” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003EES6引入了let,用let声明变量,解决了JavaScript没有块级作用域的问题(注:ES3的catch分句会产生块作用域)。u003Cu002Fpu003Eu003Cpu003E有其它语言背景的比如JAVA,C#开发者来说,这个概念并不难以理解,反而ES6之前,JavaScript没有块级作用域,对于新手而言,使用var声明变量,会让JavaScript不易懂和难以调试,用不好,甚至会有内存泄露的可能性。为什么会这样,主要是没有清楚作用域的概念,接下来我们首先了解下什么是作用域。u003Cu002Fpu003Eu003Ch1u003Eu003Cstrongu003E作用域u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003E作用域简单的来说,就是一套寻找变量的规则,用于确定在何处以及如何查找变量。说直白点:这些变量在哪里?它们存储在哪里?编译器如何找到它们?ES6代码之前,只有全局作用域或函数作用域。u003Cu002Fpu003Eu003Cpu003E当一个块或函数嵌套在另一个函数时,就发生了作用域嵌套。如图所示,就有三个嵌套作用域:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002Ff25e1d2529bd49beb2cc1facd689fb9d” img_width=”700″ img_height=”435″ alt=”「ES6基础」let和作用域” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Colu003Eu003Cliu003E全局作用域,其中有一个标识符:foo(整个绿色区域)u003Cu002Fliu003Eu003Cliu003Efoo创建的函数作用域,其中有三个标识符:a,bar和b(整个黄色区域)u003Cu002Fliu003Eu003Cliu003Ebar创建的函数作用域,其中有一个标识符:c(蓝色区域)u003Cu002Fliu003Eu003Cu002Folu003Eu003Cpu003E如何在嵌套作用域中寻找变量呢:引擎从当前作用域开始查找变量,如果找不到,就会向上一级继续查找。当抵达最外层全局作用域时,无论找到还是没有找到,查找过程中都会停止。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E全局作用域和函数作用域u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E如何理解全局作用域和函数作用域呢,使用var声明变量时,如果在函数外声明,就是全局变量,任何函数都可以进行使用,这就是全局作用域查找。如果在函数内使用var声明变量,就是函数作用域查找,只能在函数内部进行访问,外部不能进行访问,如下段代码所示:u003Cu002Fpu003Eu003Cpreu003Evar a = 12; u002Fu002F 全局作用域都能访问u003Cbru003E function myFunction() {u003Cbru003E console.log(a); u002Fu002F alerts 12u003Cbru003E var b = 13;u003Cbru003E if(true) {u003Cbru003E var c = 14; u002Fu002F 函数内部可以访问u003Cbru003E alert(b); u002Fu002F alerts 13u003Cbru003E }u003Cbru003E alert(c); u002Fu002F alerts 14u003Cbru003E }u003Cbru003E myFunction();u003Cbru003E alert(b); u002Fu002F alerts undefinedu003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E为什么b变量访问不到?因为变量b是在函数内进行声明的,因此函数执行完后,由于垃圾数据回收机制的存在,引擎认为函数执行完了,变量应该进行销毁。u003Cu002Fpu003Eu003Cpu003E如果你在函数内忘记写了b标识前忘记写了var,引擎就会自作聪明,在函数外全局作用域为你自动声明变量b,这样在函数外就能访问b变量了(全局作用域)。u003Cu002Fpu003Eu003Cpu003E因此使用var进行声明时,如果一不小心,你就会声明一个全局作用域的变量,更糟糕的情况还有可能污染一个同名的变量,因此产生的BUG就很难查找。u003Cu002Fpu003Eu003Cpu003E以下这个例子会更加明显,也是开发者经常会出现的问题,i变量会绑定到外部作用域(函数或全局作用域),污染整个外部作用域:u003Cu002Fpu003Eu003Cpreu003Efor(var i=0;i<10;i++){u003Cbru003Etconsole.log(i); u002Fu002F依次输出1到9u003Cbru003E}u003Cbru003Econsole.log(i);u002Fu002F10u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E块级作用域u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E幸好es6引入了let,避免了有var声明变量的一些问题,让变量和函数不仅可以属于所处的作用域,也可以属于某个代码块(通常是{…}内部),有一点需要强调,在块级作用域定义的变量,块级作用域外是无法访问的,如下段代码所示:u003Cu002Fpu003Eu003Cpreu003Elet a = 12; u002Fu002F 全局作用域,可以访问u003Cbru003Efunction myFunction() {u003Cbru003E console.log(a); u002Fu002F alerts 12u003Cbru003E let b = 13;u003Cbru003E if(true) {u003Cbru003E let c = 14; u002Fu002F this is NOT accessible throughout the function!u003Cbru003E alert(b); u002Fu002F alerts 13u003Cbru003E }u003Cbru003E alert(c); u002Fu002F alerts undefined {}外,因此无法访问u003Cbru003E }u003Cbru003E myFunction();u003Cbru003E alert(b); u002Fu002F alerts undefined {}外,因此无法访问u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E在for循环体,使用var和let的区别更加明显,一个是在全局作用域进行查找变量,一个是在块级作用域查找变量,块级作用域每一次执行都会产生一个作用域。首先在for循环里,使用var声明变量,如下段代码所示:u003Cu002Fpu003Eu003Cpreu003Efor(var i=0;i<5;i++){u003Cbru003EtsetTimeout(function() {u003Cbru003Ettconsole.log(i); u003Cbru003Et}, 1000);u003Cbru003E}u003Cbru003Eu002Fu002F 输出 5 5 5 5 5u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E由于JavaScript是单线程,事件循环机制的存在(不太了解事件循环机制的,大家可以查看《JavaScript基础——你真的清楚JavaScript是什么吗?》),主线程执行for循环后,才会执行SetTimeOut里的函数,由于使用var声明的变量,作用域会绑定for循环的上一层作用域,由于for循环执行完后,i的变量自然就等于5,因此setTimeOut在执行内部函数时,查找i变量的值,才会输出5。如图所示变量寻找路径:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002Feb67d9114d7d4b959f51c6add2ed318b” img_width=”788″ img_height=”400″ alt=”「ES6基础」let和作用域” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cpu003E将var替换let,将会输出什么结果,如下段代码所示:u003Cu002Fpu003Eu003Cpreu003Efor(let i=0;i<5;i++){u003Cbru003EtsetTimeout(function() {u003Cbru003Ettconsole.log(i);u003Cbru003Et}, 1000);u003Cbru003E}u003Cbru003Eu002Fu002F 输出 0,1,2,3,4u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E由于块级作用域的存在,每次循环,就会产生一个循环体块级作用域,因此才会达到预期的输出。如图所示变量寻找路径:u003Cu002Fpu003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F4d206eb7d05e456ab04714345515b387″ img_width=”764″ img_height=”512″ alt=”「ES6基础」let和作用域” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Ch1u003Eu003Cstrongu003Evar和let的比较u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cdiv class=”pgc-img”u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002F0bc8654663e04f7da6493b2e604d3e66″ img_width=”1478″ img_height=”528″ alt=”「ES6基础」let和作用域” inline=”0″u003Eu003Cp class=”pgc-img-caption”u003Eu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Ch1u003Eu003Cstrongu003E重复定义变量问题u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003E用var在同一个作用域重复定义变量,后者将会覆盖前者声明的变量的值,如下段代码所示:u003Cu002Fpu003Eu003Cpreu003Evar a = 0;u003Cbru003Evar a = 1;u003Cbru003Ealert(a); u002Fu002F alerts 1u003Cbru003Efunction myFunction() {u003Cbru003E var b = 2;u003Cbru003E var b = 3;u003Cbru003E alert(b); u002Fu002F alerts 3u003Cbru003E}u003Cbru003EmyFunction();u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E使用let在同一作用域下重复定义变量,将会产生SyntaxError的错误,如下段代码所示:u003Cu002Fpu003Eu003Cpreu003Elet a = 0;u003Cbru003Elet a = 1; u002Fu002F SyntaxErroru003Cbru003Efunction myFunction() {u003Cbru003E let b = 2;u003Cbru003E let b = 3; u002Fu002F SyntaxErroru003Cbru003E if(true) {u003Cbru003Etlet c = 4;u003Cbru003Etlet c = 5; u002Fu002F SyntaxErroru003Cbru003E }u003Cbru003E}u003Cbru003EmyFunction();u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E如果你在嵌套作用域里进行重新定义变量,虽然变量名相同,但是不是同一变量,如下段代码所示:u003Cu002Fpu003Eu003Cpreu003Evar a = 1;u003Cbru003Elet b = 2;u003Cbru003Efunction myFunction() {u003Cbru003Etvar a = 3; u002Fu002F different variableu003Cbru003Etlet b = 4; u002Fu002F different variableu003Cbru003Etif(true) {u003Cbru003Et var a = 5; u002Fu002F overwrittenu003Cbru003Et let b = 6; u002Fu002F different variableu003Cbru003Et console.log(a); u002Fu002F 5u003Cbru003Et console.log(b); u002Fu002F 6u003Cbru003E}u003Cbru003Et console.log(a); u002Fu002F 5u003Cbru003Et console.log(b); u002Fu002F 4u003Cbru003E}u003Cbru003EmyFunction();u003Cbru003Econsole.log(a);u003Cbru003Econsole.log(b);u003Cbru003Eu003Cu002Fpreu003Eu003Ch1u003Eu003Cstrongu003E提升概念的问题u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003E初学JavaScript的同学,直觉上会认为编译器会由上到下一行行的执行,其实并不正确,函数声明和变量声明都会被提升(使用var声明变量,let声明变量将不会被提升)。函数首先会被提升,然后才是变量提升。u003Cu002Fpu003Eu003Cpu003E首先我们看下段函数提升的代码:u003Cu002Fpu003Eu003Cpreu003EbookName(“ES8 Concepts”);u003Cbru003Efunction bookName(name) {u003Cbru003Etconsole.log(“I’m reading ” + name);u002Fu002FI’m reading ES8 Conceptsu003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E正常输出,由于函数会提升至执行语句前。在来看以下代码,使用变量的方式声明函数:u003Cu002Fpu003Eu003Cpreu003EbookName(“ES8 Concepts”); u002Fu002FTypeError: bookName is not a functionu003Cbru003Evar bookName = function(name) {u003Cbru003Etconsole.log(“I’m reading ” + name);u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E为什么会这样呢,JavaScript引擎只会先提升函数,在提升变量声明,引擎将会对上述代码这样调整,代码如下:u003Cu002Fpu003Eu003Cpreu003Evar bookName; u002Fu002F 变量声明提升至最上面u003Cbru003EbookName(“ES8 Concepts”); u002Fu002F bookName is not function u003Cbru003Etttttttt u002Fu002F because bookName is undefinedu003Cbru003EbookName = function(name) { u002Fu002F 变量赋值不会被提升u003Cbru003Etconsole.log(“I’m reading ” + name);u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E如果使用let替换var声明函数呢?将会有什么提示输出呢?如下段代码所示:u003Cu002Fpu003Eu003Cpreu003EbookName(“ES8 Concepts”); u002Fu002F ReferenceError: bookName is not definedu003Cbru003Elet bookName = function(name) {u003Cbru003Etconsole.log(“I’m reading ” + name);u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E从中可以看出,使用let声明的变量将不会产生变量声明提升。这样的好处就是,让我们更好的按照由上到下的常规方式书写代码,尽量避免提升问题产生的难以查找的问题。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E小节u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E今天的文章就到这里,从中我们可以看出let可以说是var的进化版,为了避免产生奇奇怪怪的问题,让我们能按照大多数高级语言书写代码的思维方式,在绝大部分情况下,我们应该使用let声明变量,让我们的代码更加易于维护和使用。u003Cu002Fpu003Eu003Cpu003E本文部分内容参:《你不知道的JavaScript》u003Cu002Fpu003Eu003Cpu003E多精彩内容,请微信关注u003Cstrongu003E“前端达人”公众号u003Cu002Fstrongu003E!u003Cu002Fpu003Eu003Cu002Fdivu003E”

原文始发于:「ES6基础」let和作用域

主题测试文章,只做测试使用。发布者:杀手梦三刀,转转请注明出处:http://www.cxybcw.com/10762.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code