原文:http://www.javashuo.com/article/p-bycakzht-ga.htmlhtml
http://www.javashuo.com/article/p-kdrbbfvg-gn.html-----let和const----你所不知道的JavaScript系列(2)git
在ES6中多了两个变量定义的操做符——let和const,在如今项目中,ES6已是不可获缺,我打算在掘金上整理一套ES6的系列,会收集经常使用的知识点,喜欢的能够点个喜欢,关注,或者能够去github点个stares6
你们都知道js是没有块级做用域的,咱们先了解一下块级做用域。github
任何一对花括号中的语句集都属于一个块,在这之中定义的全部变量在代码块外都是不可见的面试
了解定义以后,咱们👀一个用烂了的例子:segmentfault
for(var i = 0; i < 10; i++) { console.log(1); } console.log(i);复制代码
上面这个例子,最外面会输出10。显而易见,没有块级做用域。安全
关于这一点咱们能够看道面试题就能明白。函数
var func = []; for(var i = 0; i < 10; i++) { func.push(function(){ console.log(i) }); } func.forEach((func) => { func(); }) //10个10复制代码
为何会产生这样的事情呢?由于在循环内部这些i都是用同一个词法做用域的,换言之,这10个i用的都是最后的输出的i,最后的i也就等于10。
而当即执行函数就不同,他用函数做用域代替块级做用域,强制在循环内部建立副本,以便输出1,2,3,4...post
var func = []; for(var i = 0; i < 10; i++) { func.push((function(value){ return function() { console.log(value) } })(i)); } func.forEach((func) => { func(); }) //会输出1到9复制代码
对当即执行函数有兴趣的好能够看看这么几篇博文,我在这里就不用大篇幅赘述,咱们简单过一下下面几种方法,而后去将咱们今天的主角们。
推荐博文
推荐博文ui
try-catch这个建立块级做用域在红皮书中有提到过,我没用过,以为这个知识点了解就能够,也不经常使用。
try { throw 'myException'; } catch (e) { console.log(e); } console.log(e); //第一个输出myException,第二个输出e is not defined复制代码
在ES6中提出了let和const,咱们能够看一下下面这几个例子,在每次循环中,let会建立一个词法做用域,并与以前迭代中同名变量的值将其初始化。
for(let i = 0; i < 10; i++) { console.log(1); } console.log(i); //报错i is not defined复制代码
const func = []; for(let i = 0; i < 10; i++) { func.push(function(){ console.log(i) }); } func.forEach((func) => { func(); }) //会输出0到9复制代码
这个特性一样适用于for in
const funcs = [], obj = { a: 'lai', b: 'hua', c: 'min' }; for (let key in obj) { funcs.push(() => { console.log(key) }) } funcs.forEach((func) => { func() }); //输出的是a b c复制代码
在一个做用域中,已经用var、let、const声明过某标识符以后,不能在用let、const声明变量,否则会抛出错误
var a = 0; let a = 10; // 报错复制代码
可是在做用域中嵌套一个做用域就不会,看下面这个例子
var a = 0; if (true) { let a = 10; } // 不会报错复制代码
const效果也是一致的,不过const用于定义常量,const还有如下特性
当你用const声明变量,不初始化的话,就会发生报错
const a; // 报错复制代码
而const的本质是声明的,不容许修改绑定,可是容许修改值,因此大多数场景,咱们都用const来声明对象,那样对象的指针不会改变,相对来讲安全,看一下下面的例子
const person = { name = 'laihuamin' } person.name = 'lai'; //到这里不会发生报错,只会改变值 person = {}; //这里改变了对象的指针,因此会发生报错复制代码
而const不止能用于对象指针绑定,还能运用在for in的迭代中,由于每次迭代不会修改已有的绑定,而是会建立新的绑定。看下面的例子
const funcs = [], obj = { a: 'lai', b: 'hua', c: 'min' }; for (const key in obj) { funcs.push(() => { console.log(key) }) } funcs.forEach((func) => { func() }); //输出a b c复制代码
可是在循环中就不能用,循环会修改已有的绑定,而const定义的常量时不能修改绑定的,因此会报错。
注:复合类型const变量保存的是引用。由于复合类型的常量不指向数据,而是指向数据(heap)所在的地址(stack),因此经过 const 声明的复合类型只能保证其地址引用不变,但不能保证其数据不变。
对于ES5的变量提高有一个经典的考题。以下:
var a = 10; (function () { console.log(a); var a = 1; })(); // 这个会输出undefined复制代码
其实这个很好理解,js做用域连是从内向外寻找变量的,那么函数的做用域中有a这个变量,因为var会发生变量提高,就至关于下面这个过程
var a; console.log(a); a = 1;复制代码
因此,这个a变量就是undefined。而let和const就不同,把var换成let或者const都会报错。
咱们先来看例子,再来根据例子解析:
console.log(a); let a = 10; //Uncaught ReferenceError: a is not defined复制代码
let和const定义的变量是存在暂时性死区的,而var没有,咱们来了解一下两个操做符的工做原理:
对于var而言,当进入var变量的做用域时,会当即为他建立存储空间,并对它进行初始化,赋值为undefined,当函数加载到变量声明语句时,会根据语句对变量赋值。
而let和const却不同,当进入let变量的做用域时,会当即给他建立存储空间,可是不会对他进行初始化,因此会抛出如上错误。
而对于typeof操做符来讲,结果是一致的,同样会报错:
console.log(typeof a); let a = 10; //Uncaught SyntaxError: Identifier 'a' has already been declared复制代码
因此最佳实践是把声明的变量所有提到做用域的开头,这样既方便管理,又能避免没必要要的麻烦
var声明全局变量的时候,当使用关键词,那么就会覆盖掉window对象上本来拥有的属性,咱们看一下下面这个例子:
var RegExp = 'lai'; console.log(window.RegExp); var a = 'hua'; console.log(window.a); var Array = 'min'; console.log(window.Array); var b = new Array(); //lai //hua //min //Uncaught TypeError: Array is not a constructor复制代码
而换成let和const的时候就不会发生这样的事情,咱们用一样的例子来看一看:
let RegExp = 'lai'; console.log(window.RegExp); let a = 'hua'; console.log(window.a); let Array = 'min'; console.log(window.Array); let b = new window.Array(); console.log(b); //ƒ RegExp() { [native code] } //undefined //ƒ Array() { [native code] } //[]复制代码
结果和上面同样,咱们更能够进一步认证
let RegExp = 'lai'; console.log(RegExp === window.RegExp); var Array = 'hua'; console.log(Array === window.Array); //会输出 false 和 true复制代码
根据以上讲的,最佳实践应该是,能用const定义对象的,不要用let,能用let定义变量的,不要用var。至于他的不少特性,了解了能更好的帮助你运用。若是以为笔者写的能够的点一个喜欢,以后还会持续更新其余板块,但愿能给笔者的github点个star,谢谢支持