ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会)以JavaScript为基础制定的一种脚本语言标准。目前,该标准基本上每一年发布一次新的ES规范标准,目前最新的标准是ECMAScript 2018(ES9),因为前端开发的应用场景日益复杂,自从费时六年之久ES6(ECMAScript 2015)的出现,增长了不少新的特性,让JavaScript语言更加标准化和工程化。所以咱们有必要从新学习JavaScript,这样才能适应前端突飞猛进的发展。javascript
从今天开始,小编将会介绍ES6及之后的相关内容,为了便于理解和学习,每篇文章尽可能简短。本篇文章小编将带着你们一块儿学习如何使用新的语法let声明变量。前端
本篇文章阅读时间预计10分钟。java
ES6引入了let,用let声明变量,解决了JavaScript没有块级做用域的问题(注:ES3的catch分句会产生块做用域)。es6
有其它语言背景的好比JAVA,C#开发者来讲,这个概念并不难以理解,反而ES6以前,JavaScript没有块级做用域,对于新手而言,使用var声明变量,会让JavaScript不易懂和难以调试,用很差,甚至会有内存泄露的可能性。为何会这样,主要是没有清楚做用域的概念,接下来咱们首先了解下什么是做用域。bash
做用域简单的来讲,就是一套寻找变量的规则,用于肯定在何处以及如何查找变量。说直白点:这些变量在哪里?它们存储在哪里?编译器如何找到它们?ES6代码以前,只有全局做用域或函数做用域。微信
当一个块或函数嵌套在另外一个函数时,就发生了做用域嵌套。如图所示,就有三个嵌套做用域:函数
如何在嵌套做用域中寻找变量呢:引擎从当前做用域开始查找变量,若是找不到,就会向上一级继续查找。当抵达最外层全局做用域时,不管找到仍是没有找到,查找过程当中都会中止。学习
如何理解全局做用域和函数做用域呢,使用var声明变量时,若是在函数外声明,就是全局变量,任何函数均可以进行使用,这就是全局做用域查找。若是在函数内使用var声明变量,就是函数做用域查找,只能在函数内部进行访问,外部不能进行访问,以下段代码所示:ui
var a = 12; // 全局做用域都能访问
function myFunction() {
console.log(a); // alerts 12
var b = 13;
if(true) {
var c = 14; // 函数内部能够访问
alert(b); // alerts 13
}
alert(c); // alerts 14
}
myFunction();
alert(b); // alerts undefined复制代码
为何b变量访问不到?由于变量b是在函数内进行声明的,所以函数执行完后,因为垃圾数据回收机制的存在,引擎认为函数执行完了,变量应该进行销毁。this
若是你在函数内忘记写了b标识前忘记写了var,引擎就会自做聪明,在函数外全局做用域为你自动声明变量b,这样在函数外就能访问b变量了(全局做用域)。
所以使用var进行声明时,若是一不当心,你就会声明一个全局做用域的变量,更糟糕的状况还有可能污染一个同名的变量,所以产生的BUG就很难查找。
如下这个例子会更加明显,也是开发者常常会出现的问题,i变量会绑定到外部做用域(函数或全局做用域),污染整个外部做用域:
for(var i=0;i<10;i++){
console.log(i); //依次输出1到9
}
console.log(i);//10复制代码
幸亏es6引入了let,避免了有var声明变量的一些问题,让变量和函数不只能够属于所处的做用域,也能够属于某个代码块(一般是{...}内部),有一点须要强调,在块级做用域定义的变量,块级做用域外是没法访问的,以下段代码所示:
let a = 12; // 全局做用域,能够访问
function myFunction() {
console.log(a); // alerts 12
let b = 13;
if(true) {
let c = 14; // this is NOT accessible throughout the function!
alert(b); // alerts 13
}
alert(c); // alerts undefined {}外,所以没法访问
}
myFunction();
alert(b); // alerts undefined {}外,所以没法访问复制代码
在for循环体,使用var和let的区别更加明显,一个是在全局做用域进行查找变量,一个是在块级做用域查找变量,块级做用域每一次执行都会产生一个做用域。首先在for循环里,使用var声明变量,以下段代码所示:
for(var i=0;i<5;i++){
setTimeout(function() {
console.log(i);
}, 1000);
}
// 输出 5 5 5 5 5复制代码
因为JavaScript是单线程,事件循环机制的存在(不太了解事件循环机制的,你们能够查看《JavaScript基础——你真的清楚JavaScript是什么吗?》),主线程执行for循环后,才会执行SetTimeOut里的函数,因为使用var声明的变量,做用域会绑定for循环的上一层做用域,因为for循环执行完后,i的变量天然就等于5,所以setTimeOut在执行内部函数时,查找i变量的值,才会输出5。如图所示变量寻找路径:
将var替换let,将会输出什么结果,以下段代码所示:
for(let i=0;i<5;i++){
setTimeout(function() {
console.log(i);
}, 1000);
}
// 输出 0,1,2,3,4复制代码
因为块级做用域的存在,每次循环,就会产生一个循环体块级做用域,所以才会达到预期的输出。如图所示变量寻找路径:
对比项 |
let |
var |
---|---|---|
声明变量 |
√ |
√ |
能够被释放 |
√ |
√ |
能够被提高 |
|
√ |
重复定义检查 |
√ |
|
可被用于块状做用域 |
√ |
|
用var在同一个做用域重复定义变量,后者将会覆盖前者声明的变量的值,以下段代码所示:
var a = 0;
var a = 1;
alert(a); // alerts 1
function myFunction() {
var b = 2;
var b = 3;
alert(b); // alerts 3
}
myFunction();复制代码
使用let在同一做用域下重复定义变量,将会产生SyntaxError的错误,以下段代码所示:
let a = 0;
let a = 1; // SyntaxError
function myFunction() {
let b = 2;
let b = 3; // SyntaxError
if(true) {
let c = 4;
let c = 5; // SyntaxError
}
}
myFunction();复制代码
若是你在嵌套做用域里进行从新定义变量,虽然变量名相同,可是不是同一变量,以下段代码所示:
var a = 1;
let b = 2;
function myFunction() {
var a = 3; // different variable
let b = 4; // different variable
if(true) {
var a = 5; // overwritten
let b = 6; // different variable
console.log(a); // 5
console.log(b); // 6
}
console.log(a); // 5
console.log(b); // 4
}
myFunction();
console.log(a);
console.log(b);复制代码
初学JavaScript的同窗,直觉上会认为编译器会由上到下一行行的执行,其实并不正确,函数声明和变量声明都会被提高(使用var声明变量,let声明变量将不会被提高)。函数首先会被提高,而后才是变量提高。
首先咱们看下段函数提高的代码:
bookName("ES8 Concepts");
function bookName(name) {
console.log("I'm reading " + name);//I'm reading ES8 Concepts
}复制代码
正常输出,因为函数会提高至执行语句前。在来看如下代码,使用变量的方式声明函数:
bookName("ES8 Concepts"); //TypeError: bookName is not a function
var bookName = function(name) {
console.log("I'm reading " + name);
}复制代码
为何会这样呢,JavaScript引擎只会先提高函数,在提高变量声明,引擎将会对上述代码这样调整,代码以下:
var bookName; // 变量声明提高至最上面
bookName("ES8 Concepts"); // bookName is not function
// because bookName is undefined
bookName = function(name) { // 变量赋值不会被提高
console.log("I'm reading " + name);
}复制代码
若是使用let替换var声明函数呢?将会有什么提示输出呢?以下段代码所示:
bookName("ES8 Concepts"); // ReferenceError: bookName is not defined
let bookName = function(name) {
console.log("I'm reading " + name);
}复制代码
从中能够看出,使用let声明的变量将不会产生变量声明提高。这样的好处就是,让咱们更好的按照由上到下的常规方式书写代码,尽可能避免提高问题产生的难以查找的问题。
今天的文章就到这里,从中咱们能够看出let能够说是var的进化版,为了不产生奇奇怪怪的问题,让咱们能按照大多数高级语言书写代码的思惟方式,在绝大部分状况下,咱们应该使用let声明变量,让咱们的代码更加易于维护和使用。
本文部份内容参:《你不知道的JavaScript》
更多精彩内容,请微信关注”前端达人”公众号!