在ES6中多了两个变量定义的操做符——let和const,在如今项目中,ES6已是不可获缺,我打算在掘金上整理一套ES6的系列,会收集经常使用的知识点,喜欢的能够点个喜欢,关注,或者能够去github点个starhtml
你们都知道js是没有块级做用域的,咱们先了解一下块级做用域。git
任何一对花括号中的语句集都属于一个块,在这之中定义的全部变量在代码块外都是不可见的es6
了解定义以后,咱们👀一个用烂了的例子:github
for(var i = 0; i < 10; i++) {
console.log(1);
}
console.log(i);复制代码
上面这个例子,最外面会输出10。显而易见,没有块级做用域。面试
关于这一点咱们能够看道面试题就能明白。segmentfault
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...安全
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复制代码
对当即执行函数有兴趣的好能够看看这么几篇博文,我在这里就不用大篇幅赘述,咱们简单过一下下面几种方法,而后去将咱们今天的主角们。
推荐博文
推荐博文函数
try-catch这个建立块级做用域在红皮书中有提到过,我没用过,以为这个知识点了解就能够,也不经常使用。ui
try {
throw 'myException';
}
catch (e) {
console.log(e);
}
console.log(e);
//第一个输出myException,第二个输出e is not defined复制代码
在ES6中提出了let和const,咱们能够看一下下面这几个例子,在每次循环中,let会建立一个词法做用域,并与以前迭代中同名变量的值将其初始化。es5
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定义的常量时不能修改绑定的,因此会报错。
对于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,谢谢支持