❝做者:米卡面试
❞
let 和 const 是 ECMAScript6 新推出的特性,其中 let 是可以替代 var 的“标准”,因此咱们探讨 var、let 和 const 的区别,首先应该知道 var 到底有什么不规范的地方,或者是说有什么弊端。数组
var 是 Javascript 用来定义变量的一个关键字,这是一个简单的变量定义方式浏览器
var a = 0; 复制代码
可是若是咱们在定义这个变量以前,查询这个 a 的话,实际上是不会报错误的markdown
console.log(a); // undefined var a = 0; console.log(a); // 0 复制代码
虽然它输出了 undefined ,但这并非咱们想要的,咱们但愿在变量初始化前,是没法访问这个变量的,这虽然是一个约束,可是能让你的程序变得更可捉摸与维护。而 var 之因此能在var a = 0;
前被访问,是由于这一句话在编译的时候实际上是按如下的顺序进行的:闭包
var a; console.log(a); // undefined a = 0; console.log(a); // 0 复制代码
这就是为何第二行 a 会打印出 undefined 的缘由了,这个叫作变量提高,使用 var 初始化变量的时候,在该做用域的开始,会先定义这个变量,再在后面进行赋值(初始化)。因此甚至你能够这么玩儿:编辑器
console.log(a); // undefined a = 0; console.log(a); // 0 var a; 复制代码
这样也是能够成功打印的,因此你们看到这应该明白了 var 会对程序眼维护形成的困扰吧? a 明明一路看下来没有看到的,竟然依旧正常使用?!!这个时候还须要从该做用域查看到底有没有隐藏的var a;
若是没有的话那还须要去做用域外边寻找。函数
console.log(a); // undefined let a = 0; console.log(a); // 0 复制代码
咱们再来看一下如下代码oop
for(var i = 0; i < 5; i++){ var a = 0; } console.log(i); // 5 console.log(a); // 0 复制代码
咱们定义在 for 循环块中的变量 「i、a」 竟然在循环外都能被取到,这显然并不规范。spa
那么这里能够再给你们提一个面试常常也会提到的题目code
for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i); // 5 5 5 5 5 }, 300); } for (let j = 0; j < 5; j++) { setTimeout(function () { console.log(j); // 0 1 2 3 4 }, 300); } 复制代码
几乎如出一辙的代码,就由于var、let的做用域区别,获得的结果大不同。对于第一个for循环来讲,i在全局范围内都有效,在setTimeout函数中没有找到i的定义,因此在全局里才能找到变量i,在for循环执行完以后,event loop中的,setTimeout中的匿名函数开始执行,这个时候的全局i其实已是5了,因此所有打印出了5。 而对于第二个for循环来讲,let有块级做用域的概念,且值得注意的是for循环头部的let声明有一个特殊的行为,这个行为指出变量在循环过程当中不止被声明一次,每次迭代都会被声明。随后的每一个迭代都会使用上一个迭代结束时的值来初始化这个变量。这使得咱们的j,每次执行其实都是块级做用域中的不一样的一个变量,天然就不是最终的5了。 因此若是在不能使用E6的时候,可使用闭包来建立一个新的做用域。这个闭包能保留对它被声明的位置所处的做用域的引用,将i强行容留在自执行函数的做用域中使其不被回收。这样就能获取到每次的i值。
for (var i = 0; i < 5; i++) { (function (i) { setTimeout(function () { console.log(i) // 0 1 2 3 4 }, 300) })(i) } 复制代码
这个很好理解,var是容许定义两次的,不报错误。
var a = 0; console.log(a); // 0 var a = 1; console.log(a); // 1 复制代码
编辑器会在判断有已经声明的同名变量时,会忽略 var 关键字,而后直接赋值,因此若是重复使用的一个声明有一个初始值,那么它是一个赋值语句。若是重复使用的一个声明没有一个初始值,那么它不会对原来存在的变量有任何的影响。这对于 ES5 以前的 JS 是合法的,可是 ES6 后,认为这种重复定义的作法是不科学的, let 和 const 皆不容许做用域内重复一个定义同名变量。
首先,绝对是不容许在let定义变量前使用这个变量的
console.log(a); // ReferenceError: a is not defined let a = 0; console.log(a); // 0 复制代码
let声明不会被提高到当前执行上下文的顶部,从该块级做用域开始,到初始化位置,称做“暂存死区”,因此在对于a的暂存死区中使用a会报「Reference」错误。
let声明的变量拥有块级做用域,在块级做用域外是访问不到该变量的,而var不一样,在for循环中定义的变量能够在外部访问到,这会致使一些意想不到的bug。注意,大括号 {} 是块级做用域,不是说函数做用域。
let a = 0; console.log(a); // 0 let a = 1; console.log(a); // SyntaxError: Identifier 'a' has already been declared 复制代码
在 HTML 中, 全局做用域是针对 window 对象,var关键字定义的全局做用域变量属于 window 对象,而let定义的不属于。注意这是在浏览器环境下,若是是Node则无window对象。
var a = 0; console.log(window.a) // 0 let b = 1; console.log(window.b) // undefined 复制代码
其实const和let很是很是相似,let该有的特性,const都有,不一样就是,第一,const不容许“修改”变量,第二const必须初始化,而let不须要
const a = 0; a = 1; // TypeError: Assignment to constant variable 复制代码
const b; // SyntaxError: Missing initializer in const declaration 复制代码
不过const定义的不是真正意义上的常量,若是定义的是一个对象或者数组,实际上是能够变化的,只不过不能将不一样的数据类型分配给他,通常能够记:const初始化定义后,不可以使用 = 赋值。
const a = {} a.b = 1; console.log(a) // { b: 1 } a = 1 // TypeError: Assignment to constant variable 复制代码
本文使用 mdnice 排版