了解javascript ES6中的let和const
在过去的两年中,ES6 已被我们广泛地用于程序开发中,在本教程中,我将通过重述我们大多数人在 JavaScript 中学到的第一件事——定义变量和占位符,将您带入 JavaScript ES6 的美妙世界。
定义变量的两种新方法—let 和 const
关键字一直是我们在 JavaScript 中定义变量的方式。然而,在 JavaScript ES6 中,用两个新的关键字来更好地表示存储的数据类型,从而使它们更容易调试,更不容易出错。它们是:
- let
- const
首先要理解的是,let 和 const 是块作用域,而 var 则是函数作用域。这意味着它们是在最接近的块(花括号)中定义的,在这里,当 var 在整个函数中是本地的,或者在定义外部函数时是全局的。
let 和 const 之间的区别在于,前者应该用于保存可能发生变化的变量,而 const,顾名思义,则是保存我们所知道的不变的数据。实际上,试图在设置 const 值之后重置它将导致错误。
关键字 let
当所持有的数据可能发生变化时,使用 let 去声明一个变量,类似 var:
let myage = 39 if (new Date().getFullYear == 2018){ myage = 40 }
如上所见,一旦我使用 let 定义了一个变量,我就可以通过使用新值再次引用它来更新它的值,而不需要在它前面使用 let 关键字。与 var 不同的是,您不能在块内多次定义同一个 let 变量:
let myage = 39 let myname = 'George' let myage = 40 //SyntaxError:标识符 myage 已经声明
这有助于防止我们在声明变量之后意外重写变量,在使用 var 时这种情况太频繁了。之前我们已经讨论过了,让它块作用域,这意味着它可以在块(包括内部块)中定义,但不在它外面:
if (true){ // 定义新的块 let myname = 'George' } console.log(myname) //语法错误,myname 未定义
相比之下,如果我们使用 var 关键字:
if (true){ // 新建块 var myname = 'George' } console.log(myname) // 输出 'George'
在 var 中,变量的作用域限定在函数的作用域上,或者在函数外部(如上面的示例),限定在窗口对象本身。
定义具有相同名称的多个 let 变量
虽然我们不能在一个块中多次定义同一个 let 变量,但是没有什么可以阻止我们在另一个块中这样做。需要记住的是,ES6 脚本将它们视为独立的变量:
let mybrother = 'Paul' if (true){ // 新建块 let mybrother = 'Jason' console.log(mybrother) // Jason } console.log(mybrother) // Paul
上面的例子中我们使用 let 声明 mybrother 两次,每个块中一次,这是有效的。每一个 mybrother 变量都是不同于另一个的,这表明,let 声明的变量只在它所在的代码块有效。如果我们替换成 var,console.log()会在两个实例中返回“Jason”,因为第二个 var 声明会在遇到“jamie”时重写第一个值。
用 let 作为 for 循坏
在 JavaScript 中用(var;;)循环,使用 let 代替 var 迭代跟踪提供了一个独特的优势——在每次循坏中我们不再需要使用立即调用函数表达式内循环正确捕获迭代变量的值,我们应该需要这些值。
考虑传统的 for 循环,该循环试图在循环运行时间之后调用迭代变量的值,例如使用 setTimeout:
for (var i = 0; i < 5; i++){ setTimeout(function(){ console.log(i) }, i * 100)} //输出 '5, 5, 5, 5, 5'
这个失败了,你得到的就是我在最后一次迭代的值,不是 0 1 2 3 4。问题是,var 变量的作用域不是它所在的块,因此可以在任何地方访问,无论是在它所定义的函数内部,还是在函数外部。这意味着在 for 循环中重复重写变量 i。当 setTimeout 试图调用 i 时,它得到的是我设置的最后一个值。
在过去,克服这个麻烦问题的一种常见方法是在 for 循环中加入一个立即调用函数表达式,创建一个闭包,在每次迭代中捕获 i 的值:
for (var i = 0; i < 5; i++){ (function(x){ setTimeout(function(){ console.log(x) }, i * 100) })(i)} //logs '0, 1, 2, 3, 4'
使用 let 替换 var 可以自动解决这个常见问题,因为迭代变量使用 let 将 i 的每个实例都限定到它所在的块中,创建与在 for 循环中包装立即调用函数表达式相同的结果:
for (let i = 0; i < 5; i++){ setTimeout(function(){ console.log(i) }, i * 100) } //'0, 1, 2, 3, 4'
无论延迟是什么,i 的正确值都会被返回。这在许多场景中都很有用,比如在循环中依赖于 i 的值来生成不同请求的 Ajax 请求,或者利用 i 的值执行针对其绑定到的元素的操作的事件处理程序。下面是后面的一个例子:
let links = document.getElementsByTagName('a'); for (let i=0; i暂时性死区
在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。在该区域中,尝试引用让变量返回一个 ReferenceError:
function test(){ console.log(dog) // 返回 ReferenceError let dog = 'spotty' }这似乎是显而易见的,但是传统上,由于变量提升的概念,var 变量的行为非常不同。简单地说,使用 var 定义的变量会自动“挂起”到执行上下文的最顶端(例如:如果是全局变量,则在函数的顶部或脚本的顶部),即使它是在块的更远处初始化的。这意味着你可以在变量实际声明之前引用它,而不会得到一个 ReferenceError:
function test(){ console.log(dog) //返回未定义 var dog = 'spotty' console.log(dog) // ‘spotty’ }将返回未定义的值,因为提升只将变量本身移动到顶部,而不是将其设置为的值。由于变量提升,上述情况相当于:
function test(){ var dog console.log(dog) //返回未定义 var dog = 'spotty' console.log(dog) // ‘spotty’}对于 let 变量,不涉及变量提升,在块中物理声明一个 let 变量之前尝试引用它将返回一个 ReferenceError。
ES6 规定暂时性死区和 let、const 语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。
总之,暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
const 关键字
我们花了很多时间讨论 let 这个关键字,但是我们不要忘记它可靠的一面,const。
使用 const 来声明不会更改的变量,例如 PI 的值或兄弟的名称。当开发团队中的另一个成员在您的代码中看到 const 关键字时,他/她知道这是一个更少的变量,他必须跟踪更改。
const 在块作用域类似于 let,使它只能在它所定义的块(花括号)中访问。此外,它还受暂时性死区规则的影响。
与 let 不同的是,在定义时必须使用值进行初始化。此外,它不能被重新分配到另一个值之后:
const mydog = 'spotty' // 正确 mydog = 'fluffy' // error:赋值为常数 const mybrother // error: const 中缺少初始化器虽然 const 变量不能完全重新分配到一个不同的值,如果 const 的值是一个对象或数组,对象的属性本身仍然是可变的,可以修改:
const myobject = {name:'George', age:39} //myobject = {name: 'Ken', age:39} //error myobject.age = 40 // OK const myarray = [] myarray[0] = 'Football' // OKlet 和 const 与 var
既然大家已经掌握了如何使用 let 和 const,那么问题就变成了,是否应该开始完全在代码中使用 let 和 const 替换 var ?关于这一点,有许多流派的观点,尽管我同意这样的观点:一般来说,var 现在应该被视为最弱的,在使用 let 和 const 的情况结束后使用。你怎么看?
码云笔记 » 了解javascript ES6中的let和const