在ES5中,变量声明只有var和function以及隐式声明三种,在ES6中则增长了let、const、import和class四种。node
1. letjquery
1.1 块级做用域面试
let声明的变量的做用域是块级做用域(这个特性有点相似于后台语言),ES5 并无块级做用域,只有函数做用域和全局做用域。数组
{ let a = 'ES6'; var b = 'ES5'; } console.log(b) // ES5 console.log(a) // ReferenceError: a is not defined.
那么let的块级做用域有什么好处呢?数据结构
let很是适合用于 for循环内部的块级做用域。JS中的for循环体比较特殊,每次执行都是一个全新的独立的块做用域,用let声明的变量传入到 for循环体的做用域后,不会发生改变,不受外界的影响。看一个常见的面试题目:异步
for (var i = 0; i <10; i++) { setTimeout(function() { // 同步注册回调函数到异步的宏任务队列。 console.log(i); // 执行此代码时,同步代码for循环已经执行完成 }, 0); } // 输出结果 10 (共10个) // 这里变量为i的for循环中,i是一个全局变量,在全局范围内都有效,因此每一次循环,新的i值都会覆盖旧值,致使最后输出的是最后一轮i的值,即i的最终结果为10,实际上都是console.log(10)。涉及的知识点:JS的事件循环机制,setTimeout的机制等
把 var改为 let声明:函数
for (let i = 0; i < 10; i++) { setTimeout(function() { console.log(i); //当前的i仅在本轮的循环中有效,就是说每一次循环,i其实都是一个新产生的变量。 //用 let 声明的变量 i 只做用于循环体内部,不受外界干扰。 }, 0); } // 输出结果: 0 1 2 3 4 5 6 7 8 9
1.2 暂时性死区(Temporal Dead Zone)ui
在一个块级做用域中,变量惟一存在,一旦在块级做用域中用let声明了一个变量,那么这个变量就惟一属于这个块级做用域,不受外部变量的影响,以下面所示。this
var tmp = 'bread and dream'; if(true){ tmp = 'dream or bread'; //ReferenceError let tmp; }
这个例子中tmp = 'dream or bread'的赋值会报错,由于在if块中的let对tmp变量进行了声明,致使该tmp绑定了这个做用域,而let临时死区致使了并不能在声明前使用,因此在声明前对变量赋值会报错。prototype
暂时性死区的本质就是,只要一进入当前做用域,所要使用的变量就已经存在了,可是不可获取,只有等到声明变量的那一行代码出现,才能够获取和使用该变量。
暂时性死区的意义也是让咱们标准化代码,将全部变量的声明放在做用域的最开始。
1.3 不容许重复声明
(1) 在相同的做用域内,用let声明变量时,只容许声明一遍,但 var是能够屡次声明的。你们都知道ES5 屡次声明会形成变量覆盖并且不会报错,这就给调试增长了难度,而let可以直接扼杀这个问题在摇篮之中,由于会直接报错。
// 不报错 function demo() { var a = 'bread and dream'; var a = 'dream or bread'; } // 报错,Duplicate declaration "a" function demo() { let a = 'bread and dream'; var a = 'dream or bread'; } // 报错,Duplicate declaration "a" function demo() { let a = 'bread and dream'; let a = 'dream or bread'; }
(2) 不能在函数内部从新声明参数:
function demo1(arg) { let arg; // 报错 } demo1() function demo2(arg) { { let arg; // 不报错 } } demo2()
2. const
2.1 用于声明常量
const声明的常量是不容许改变的,只读属性,这意味常量声明时必须同时赋值, 只声明不赋值,就会报错,一般常量以大写字母命名。
const Person; // 错误,必须初始化 const Person = 'bread and dream';// 正确 const Person2 = 'no'; Person2 = 'dream or bread'; //报错,不能从新赋值
这样作的两个好处:一是阅读代码的人马上会意识到不该该修改这个值,二是防止了无心间修改变量值所致使的错误。好比咱们使用nodejs的一些模块的时候,咱们只是使用对应的模块(如http模块),可是并不须要修改nodejs的模块,这个时候就能够声明成const,增长了代码的可读性和避免错误。
2.2 支持块级做用域
const和let相似,也是支持块级做用域.
if (true) { const MIN = 5; } MIN // Uncaught ReferenceError: MIN is not defined
2.3 不支持变量提高,有暂时性死区
const声明的常量也是不提高,一样存在暂时性死区,只能在声明的位置后面使用。
if (true) { console.log(MIN); // ReferenceError const MIN = 5; }
2.4 特殊状况
若是声明的常量是一个对象,那么对于对象自己是不容许从新赋值的,可是对于对象的属性是能够赋值的。
const obj = {}; obj.a = 'xiao hua'; console.log(obj.a); //'xiao hua'
实际上const能保证的,并非变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,所以等同于常量。
但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针。
至于它指向的数据结构是否是可变的,就彻底不能控制了。所以,将一个对象声明为常量必须很是当心。
若是要完全将对象冻结(不可修改其属性),应该使用Object.freeze(obj)方法。同理,数组也是同样的。
3. import
ES6采用import来代替node等的require来导入模块。
import {$} from './jquery.js'
$对象就是jquery中export暴露的对象。
若是想为输入的变量从新取一个名字,import命令要使用as关键字,将输入的变量重命名。
import { JQ as $ } from './jquery.js';
注意,import命令具备提高效果,会提高到整个模块的头部,首先执行。
4. class
ES6引入了类的概念,有了class这个关键字。类的实质仍是函数对象。
先定义一个类:
//定义类 class Animal { constructor(name, age) { this.name = name; this.age = age; } setSex(_sex) { this.sex=_sex; } }
constructor方法,就是构造方法,也就是ES5时代函数对象的主体,而this关键字则表明实例对象。
上面的类也能够改为ES5的写法:
function Animal(name, age){ this.name = name; this.age = age; } Animal.prototype.setSex = function (_sex) { this.sex=_sex; }
其实,大多数类的特性均可以经过以前的函数对象与原型来推导。
生成类的实例对象的写法,与ES5经过构造函数生成对象彻底同样,也是使用new命令。
class Animal {} let dog = new Animal();
在类的实例上面调用方法,其实就是调用原型上的方法,由于类上的方法其实都是添加在原型上。
Class其实就是一个function,可是有一点不一样,Class不存在变量提高,也就是说Class声明定义必须在使用以前。
5.总结
在ES6以前,JavaScript是没有块级做用域的,若是在块内使用var声明一个变量,它在代码块外面仍旧是可见的。ES6规范给开发者带来了块级做用域,let和const都添加了块级做用域,使得JS更严谨和规范。
let 与 const 相同点:
块级做用域 有暂时性死区 约束了变量提高 禁止重复声明变量
let 与 const不一样点:
const声明的变量不能从新赋值,也是因为这个规则,const变量声明时必须初始化,不能留到之后赋值。
合理的使用ES6新的声明方式,不论是面试仍是工做中都有实际的应用,尤为是工做中,你们必定要尽可能的多使用新的声明方式,不但可让代码更规范,更能够避免没必要要的bug,浪费调试时间,进而提升工做效率。