当前位置: 首页 > Web前端 > vue.js

es6let用法

时间:2023-03-31 14:54:20 vue.js

let命令基本用法ES6增加了一个let命令来声明变量。它的用法与var类似,但声明的变量只在let命令所在的代码块内有效。{让a=10;varb=1;}a//ReferenceError:a未定义。b//1上面代码在代码块中,分别用let和var声明了两个变量。然后在代码块外调用这两个变量。结果是let声明的变量报错,var声明的变量返回正确值。这说明let声明的变量只在它所在的代码块中有效。for循环的计数器非常适合使用let命令。for(leti=0;i<10;i++){//...}console.log(i);//ReferenceError:iisnotdefined在上面的代码中,计数器i只在函数体中有效for循环。体外参考会报错。如果下面的代码使用了var,那么最后的输出就是10。vara=[];对于(vari=0;i<10;i++){a[i]=function(){console.log(i);};}a[6]();//10上面的代码中,变量i是通过var命令声明的,全局有效,所以全局只有一个变量i。每次循环,变量i的值都会变化,循环中函数里面的console.log(i)赋值给数组a,里面的i指向全局的i。也就是说,数组a的所有成员中的i指向同一个i,导致运行时输出最后一轮i的值,即10。如果使用let,则声明的变量为只在块级作用域有效,最终输出为6vara=[];for(leti=0;i<10;i++){a[i]=function(){console.log(i);};}a[6]();//6上面的代码中,变量i是通过let声明的,而当前的i只在当前循环中有效,所以每次循环中的i其实都是一个新的变量,所以最终输出的是6。你可能会问,如果每次循环都重新声明变量i,它怎么知道上一次循环的值来计算当前循环的值呢?这是因为JavaScript引擎内部会记住上一个循环的值,在初始化当前循环的变量i时,是在上一个循环的基础上计算的。此外,for循环还有一个特殊之处,就是设置循环变量的部分是一个父作用域,而循环体内是一个单独的子作用域。for(leti=0;i<3;i++){让i='abc';console.log(i);}//abc//abc//abc上面代码运行正确,输出了3次abc。这说明函数内部的变量i和循环变量i不在同一个作用域内,有各自独立的作用域。没有变量提升var命令中出现了“变量提升”现象,即变量在声明之前就可以使用,而其值是undefined。这种现象或多或少有些奇怪。按照一般的逻辑,变量应该用在声明语句之后。为了纠正这种现象,let命令改变了语法行为,它声明的变量必须在声明之后使用,否则会报错。//varcaseconsole.log(foo);//输出undefinedvarfoo=2;//让案例console.log(bar);//错误ReferenceErrorletbar=2;上面代码中,变量foo是用var命令声明的,会发生变量提升,即脚本开始运行时,变量foo已经存在,但是没有值,所以会输出undefined。变量bar是用let命令声明的,不会发生变量提升。这意味着变量bar在声明之前是不存在的,如果使用它会抛出错误。暂时死区只要块级作用域内有let命令,它声明的变量就会“绑定”(binding)这个区域,不再受外界影响。vartmp=123;if(true){tmp='abc';//ReferenceErrorlettmp;}上面代码中有一个全局变量tmp,但是let在块级作用域内声明了一个局部变量tmp,导致后者绑定了这个块级作用域,所以在变量之前是let声明的,给tmp赋值的时候会报错。ES6明确规定,如果块中有let和const命令,则块为这些命令声明的变量从一开始就形成一个封闭的作用域。在声明之前使用这些变量将导致错误。简而言之,在代码块中,变量在使用let命令声明之前是不可用的。这在语法上称为“时间死区”(简称TDZ)。if(true){//TDZ开始tmp='abc';//ReferenceErrorconsole.log(tmp);//ReferenceErrorlettmp;//TDZ结束console.log(tmp);//未定义tmp=123;控制台日志(tmp);//123}上面代码中,变量tmp在被let命令声明之前,属于变量tmp的“死区”。“临时死区”也意味着typeof不再是100%安全的操作。x类型;//ReferenceErrorletx;上面代码中,变量x是使用let命令声明的,所以在声明之前,属于x的“死区”,只要使用该变量就会报错。因此,typeof在运行时会抛出ReferenceError。作为对比,如果一个变量根本没有声明,使用typeof是不会报错的。typeofundeclared_variable//"undefined"上面代码中undeclared_variable是一个不存在的变量名,结果返回"undefined"。所以在没有let之前,typeof操作符是100%安全的,永远不会报错。这不再是真的。这样的设计是为了让大家养成良好的编程习惯。变量必须在声明后使用,否则会报错。有些“死区”比较隐蔽,不易发现。函数bar(x=y,y=2){return[x,y];}bar();//报错上面代码中,之所以调用bar函数报错(有的实现可能不报错)是因为参数x的默认值等于另一个参数y,而y还没有声明此时,属于“死区”。如果y的默认值为x,则不会报错,因为此时x已经声明了。函数bar(x=2,y=x){return[x,y];}bar();//[2,2]另外,下面的代码也会报错,这与var的行为不同。//没有报错varx=x;//报错letx=x;//ReferenceError:xisnotdefined上面的代码报错是因为临时死区。在使用let声明变量时,只要在声明完成之前使用该变量,就会报错。上面一行就属于这种情况。在执行变量x的声明语句之前,取了x的值,导致报错“xisundefined”。ES6规定临时死区和let和const语句没有变量提升,主要是为了减少运行时错误,防止变量在声明之前就被使用,导致意外行为。像这样的错误在ES5中很常见,现在有了这个规定,避免它们很容易。简而言之,临时死区的本质就是一进入当前作用域,要使用的变量已经存在,但是获取不到。只有当声明变量的代码行出现时,才能获取并使用该变量。不允许重复声明。Let不允许在同一范围内重复声明同一变量。//错误函数func(){leta=10;vara=1;}//错误函数func(){leta=10;leta=1;}因此,参数不能在函数内部重新声明。functionfunc(arg){letarg;}func()//报错functionfunc(arg){{letarg;}}func()//不报错Block-levelscope为什么需要block-levelscope?ES5只有全局作用域和函数作用域,没有块级作用域,带来很多不合理的场景。在第一种情况下,内部变量可能会覆盖外部变量。vartmp=newDate();functionf(){console.log(tmp);如果(假){vartmp='你好世界';}}F();//undefined上面代码的原意是,ifcode块外使用外层tmp变量,内层使用内层tmp变量。但是函数f执行后,输出结果是undefined。原因是变量提升导致内层的tmp变量覆盖了外层的tmp变量。在第二种情况下,用于计数的循环变量作为全局变量泄漏。vars='hello';for(vari=0;i{Object.freeze(obj);Object.keys(obj).forEach((key,i)=>{if(typeofobj[key]==='object'){constantize(obj[key]);}});};ES6声明变量的六种方式ES5只有两种声明变量的方式:var命令和function命令。除了在ES6中加入let和const命令外,后面的章节还会提到另外两种声明变量的方法:import命令和class命令。所以,ES6一共有6种声明变量的方式。顶层对象的属性顶层对象是指浏览器环境中的window对象,Node中的全局对象。在ES5中,顶级对象的属性等同于全局变量。window.a=1;a//1a=2;window.a//2上面的代码中,顶级对象的属性赋值和全局变量的赋值是一回事。顶级对象的属性与全局变量挂钩,这被认为是JavaScript语言最大的设计失败之一。这种设计带来了几个大问题。首先,编译时无法报未声明变量的错误,只有运行时才能知道(因为全局变量可能是由顶级对象的属性创建的,而属性的创建是动态的);其次,程序员很容易无意识地创建全局变量(如错别字);最后,顶层对象的属性可以随处读写,这对模块化编程非常不利。另一方面,window对象有实体意义,指的是浏览器的window对象,顶层对象是有实体意义的对象,也是不合适的。为了改变这一点,ES6一方面规定,为了保持兼容性,var命令和function命令声明的全局变量仍然是顶级对象的属性;另一方面,规定了let命令、const命令、class命令声明的全局变量、Properties不属于顶级对象。也就是说,从ES6开始,全局变量会逐渐和顶层对象的属性解耦。vara=1;//如果你在Node的REPL环境下,可以写成global.a//或者用一般的方法,写成this.awindow.a//1letb=1;window.b//undefined上面代码中,全局变量a是通过var命令声明的,所以它是顶层对象的一个??属性;全局变量b是let命令声明的,所以它不是顶层对象的属性,返回undefined。globalThis对象JavaScript语言有一个顶级对象,它提供所有代码在其中运行的全局环境(即全局范围)。然而,顶级对象在不同的??实现中并不统一。在浏览器中,最顶层的对象是window,但是Node和WebWorker是没有windows的。在浏览器和WebWorkers中,self也指向顶级对象,但是Node没有self。在Node中,顶级对象是全局的,但在其他环境中是不支持的。为了在各种环境下获取顶级对象,同一段代码一般使用this变量,但也有局限性。在全局环境中,这将返回顶级对象。但是,在Node模块和ES6模块中,这将返回当前模块。函数中的this,如果函数不是作为对象方法运行,而只是作为函数运行,this会指向顶层对象。但是,在严格模式下,此时这将返回undefined。不管是严格模式还是普通模式,newFunction('returnthis')()都会返回全局对象。但是,如果浏览器使用了CSP(ContentSecurityPolicy,内容安全策略),那么eval、newFunction等方法可能无法使用。综上所述,很难找到一种在所有情况下都能获取到顶级对象的方法。这里有两种勉强可用的方法。//方法一(typeofwindow!=='undefined'?window:(typeofprocess==='object'&&typeofrequire==='function'&&typeofglobal==='object')?global:this);//方法2vargetGlobal=function(){if(typeofself!=='undefined'){returnself;}if(typeofwindow!=='undefined'){返回窗口;}if(typeofglobal!=='undefined'){returnglobal;}thrownewError('无法定位全局对象');};ES2020在语言标准层面,引入了globalThis作为顶级对象。也就是说,globalThis存在于任何环境中,你可以从中得到顶级对象,指向全局环境中的this。shim库global-this模拟了这个proposal,globalThis在所有环境下都可以获得。