ES6新增的两个关键字
在ES5中我们声明变量都是使用的var关键字,从ES6开始新增了两个关键字可以声明变量:let、const。
let关键字
从直观的角度来说,let和var是没有太大的区别的,都是用于声明一个变量。
const关键字
const关键字是constant的单词的缩写,表示常量、衡量的意思,它表示保存的数据一旦被赋值,就不能被修改, 它表示保存的数据一旦被赋值,就不能被修改。
- 注意:let、const不允许重复声明变量
案例
//从直观角度来看let/const与var都是声明变量没有太大区别。
var foo = "foo"
let bar = "bar"
const name = "abc" //constant(常量)
name = "zahngsan"
console.log(name); //wrong
//注意事项一:用const定义的变量不可修改
//但是传递的如果是一个引用类型(内存地址),可以通过引用(地址)找到对应的对象,进而修改对象的属性
const obj = { //这里obj就是一个引用了 obj保存的是类似0x100的地址
foo:"foo"
}
obj.foo = "aaa" //这个是可以的,通过地址拿到值
console.log(obj.foo); //aaa
//但是如果obj = {} //这个就错了,这是直接修改了值
//注意事项二:无论是let还是const定义的变量名都是不可以重复定义的
let foo = "foo"
let foo = "abc" //wrong
const way = 'that way'
const way = 'migos' //wrong
探讨作用域提升的问题
什么是作用域提升?
作用域提升:在声明变量的作用域中,如果这个变量可以在声明之前被访问,那么我们可以称之为作用域提升。
探讨let与const的作用域提升
我们都知道var定义的变量是有作用域提升的。如果您对这方面的知识不了解,可以看下我写的第一篇文章浏览器执行原理、V8引擎。 但是let 与const定义的变量是没有作用域提升的。这个在社区中争议很大。争议的点是,这些变量在被定义之前有没有创建。
官方(ECMA262)给出的解释是这些变量会被创建在包含他们的词法环境被实例化时,但是是不可以访问它们的,直到词法绑定被求值。
所以说,当词法环境创建的时候,变量就已经被创建了,但是在被赋值之前,你不能访问它而已。
// console.log(foo); //undefined
// var foo = "foo"
console.log(foo); //wrong let是没有作用域提升的
//let/const它们是没有作用域提升的
let foo = "foo"
var的变量在window对象中的保存
var定义的变量是会被保存在window对象(Go里)中的。这是很不好的现象,很容易造成bug。而const与let定义的变量就没有这个毛病。
var foo = "foo"
var message = "Hello World"
console.log(window.foo);
console.log(window.message); //GO对象
window.message = "哈哈哈"
console.log(message);
//VE是有这些变量的,这些变量被v8放在了variables对象里面,它的数据类型是VariableMap(它是一个hashmap)
// v8是不实现window的,它是包含v8的浏览器实现的
// window:{Date/Number,还要那些变量}这其实是bug的温床,不应该放到window里面。
块级作用域的理解
ES5或之前就没有块级作用域。什么是块级作用域呢。
//block(块)
// {
// //表达式
// var foo = "foo"
// }
// 在C语言内,你块里声明的变量,外面是访问不了的,因为它是由作用域的。
//但是ES5之前是没有块级作用域的,这玩意({})形同虚设,没有任何意义。
//在es5中只有两个东西会形成作用域
//1.全局作用域
//2.函数作用域(函数的代码块),函数会顺着作用域链往外找,但是到全局作用域就停止寻找。
ES6的块级作用域
ES6的块级作用域对let/const/function/class声明的类型有效。但函数要注意下,大部分浏览器为了兼容以前的代码,让function没有块级作用域了。
// ES6的块级作用域对let/const/function/class声明的类型有效
{
let foo = "why"
function demo(){}
class person{}
}
console.log(foo); //报错foo is not defined
demo() //函数能访问,这是因为不同的浏览器有不同的实现,大部分浏览器为了兼容以前的代码,让function没有块级作用域了
// 如果是只支持es6的浏览器,那必然报错。
var p = new Person() //Person is not defined
if,switch for循环的块级作用域
//if语句的代码块就是块级作用域
if(true){
var foo = "foo" //外面可以访问
let bar = "bar" //外面不能访问
}
//块级作用域
switch(color){
case"red":
let bar = "abar"
}
console.log(bar); //报错
for(var i = 0; i < 10; i++){
console.log("hello"+i);
}
console.log(i); //能访问的
for(let i = 0; i < 10; i++){
console.log("hello"+i);
}
console.log(i); //报错undefined
块级作用域的应用场景
相信很多朋友都做过类似轮播图的那种案例,或者多个按钮点击的案例,要么用闭包要么this解决问题,反正是一言难尽,看下下面的案例就懂了。
//html代码省略,定义了5个按钮
const btns = document.getElementsByTagName('button')
for(var i = 0; i < btns.length; i++){
btns[i].onclick = function(){
console.log("第" + i + "个按钮被点击"); //分析:要访问i要往上层作用域中找,找到最外面的全局作用域,这时候i已经变成4了
}
//解法1(立即执行函数)闭包 (函数会形成作用域)
// (function(n){
// btns[i].onclick = function(){
// console.log("第" + n + "个按钮被点击"); //分析:要访问i要往上层作用域中找,找到最外面的全局作用域,这时候i已经变成4了
// }
// })(i) //将i传入
}
//解法2 let
for(let i = 0; i < btns.length; i++){
btns[i].onclick = function(){
console.log("第" + i + "个按钮被点击");
}
}
分析let定义的i在for循环中的表现形式
const names = ['abc','cba','ccc']
for(let i = 0; i < names.length ; i++){
console.log(names[i]);
}
//用let的for循环会遍历3次,形成三次块级作用域
{
let i = 0
console.log(names[i]);
}
{
//let会再定义一次i
将上面的i++
let i = 上面的那个结果 //temp = i++ i = temp
}
//这里是不能用const的,因为要做++的操作,因为const是不允许你做修改的
//比如
// const num = 0
// num ++ //wrong
//for of ES6新增的遍历数组(对象)
for(let item of names){ //尽量不要用var,这里是可以用const的(它是直接赋值的)
console.log(item);
}
ES6新增的遍历数组(对象)方式 for of
const names = ['abc','cba','ccc']
//for of ES6新增的遍历数组(对象)
for(let item of names){ //尽量不要用var,这里是可以用const的(它是直接赋值的)
console.log(item);
}
let/const定义的变量存在暂时性死区
在一个代码中,在使用let,const声明的变量,在声明之前,变量都是不可以访问的,我们称之为暂时性死区(TDZ)。
var foo = "foo"
if(true){
console.log(foo);
let foo = "abc" //报错这就是暂时性死区
//只要是在内部用let或者const使用了这个变量,前面的你就是不能访问的。
}
function bar(){
console.log(foo);
let foo = "abc" //这样子也会报错的
}
bar()
var,let,const的选择
var表现出更多的特殊性:作用域提升,window全局对象添加属性,没有块级作用域,这都是历史遗留问题,这些特殊性都是js设计之初的语言缺陷。市面上还是有很多题目利用这些缺陷出一系列的面试题,用来考察大家对js语言本身以及底层的理解。但是实际工作中,我们最好用最新的规范来编写,不要再用var来定义变量了,兼容性问题大家也不用操心,babel可以帮我们处理。
let与const的选择
优先推荐使用const(不确定会不会改),如果确实有一天要改,再改它的类型。const可以改变数据的安全性。减少bug的产生。