一、var、let、const的区别
1. 作用域不同
- var声明变量存在于函数级作用域内(该域内存在变量提升) ,意味着该变量将在函数退出时被销毁。
- let/const ,可以使用块级作用域。块级作用域解决了ES5中的两个问题:
①内层变量可能覆盖外层变量
②用来计数的循环变量泄露为全局变量 - let/const有一个暂时性死区的概念,即进入作用域创建变量到变量可以开始访问的一段时间,也就是 在使用let、const命令声明变量之前,该变量都是不可用的。
console.log(a)
var a
//var存在变量提升相当于
var a
console.log(a); //undefined
//针对let存在暂时性死区
console.log(a) // ReferenceError: a is not defined
let a
当程序的控制流程在新的作用域(module function 或 block 作用域)进行实例化时,在此作用域中用let/const声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,所以是不能被访问的,如果访问就会抛出错误。因此,在这运行流程进入作用域创建变量,到变量可以被访问之间的这一段时间,就称之为暂时死区。
通俗一点来讲就是:
S6规定,let/const 命令会使区块形成封闭的作用域。若在声明之前使用变量,就会报错。
总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。
这在语法上,称为 “暂时性死区”( temporal dead zone,简称 TDZ)。
TDZ 也意味着 typeof 不再是一个百分之百安全的操作。
看下面这段代码:
typeof a // ReferenceError: a is not defined
let a
2. 重复声明
- var重复声明不会报错,全局声明的变量成为window对象的属性。
- let不允许同一个块级作用域中出现冗余声明。
- 混用var、let重复声明一个变量也会报错。
3. 变量赋值
- let,const主要的区别在于:const一旦被赋值就不再"改变(这并不意味着声明的变量本身不可变,只是说它不可再次被赋值了。const定义引用类型时,只要它的引用地址不发生改变,仍然可以改变它的属性)
const person = { name : 'Jack'}
person.name = 'Tom'
console.log(person); //{name: 'Tom'}
4. 指针指向
- let和const都是ES6新增的用于创建变量的语法。
let创建的变量是可以更改指针指向(可以重新赋值)。但const声明的变量是不允许改变指针的指向。
5. 初始值设置
- 在变量声明时,var 和 let 可以不用设置初始值。而const声明变量必须设置初始值。
6. 给全局添加属性
- 浏览器的全局对象是window,Node的全局对象是global。var声明的变量为全局变量,并且会将该变量添加为全局对象的属性,但是let和const不会。
二、变量提升
JavaScript引擎会先进行预解析,获取所有变量的声明复制undefined,然后逐行执行,这导致所有被声明的变量,都会被提升到代码的头部(函数的top的地方,且被提升的只有变量,值不会被提升),也就是变量提升(hoisting)
我们先来看一个例子
var a = 2;
function A() {
console.log(a);
}
A();
不出意外打印出的结果是2
var a = 2;
function A() {
console.log(a);
var a = 1;
}
A();
这里的打印结果与我们预想的可能有出入,执行结果是undefined,这里面隐藏了一个陷阱-----JavaScript中的变量提升(Hoisting)。
我们定义三个变量
(function(){
var a = 'A';
var b = 'B';
var c = 'C';
})()
实际上它是这样的
(function(){
var a,b,c;
a = 'A';
b = 'B';
c = 'C';
})()
再回过头来看上面的代码相当于
var a = 2;
function A() {
var a;
console.log(a); //undefined
a = 1;
}
A();
因在之前打印之前只是声明了变量并未赋值,所以是undefined。
三、函数提升
函数提升是把整个函数都提到前面去。
在我们写js代码的时候,我们有2种写法,一种是函数表达式,另外一种是函数声明方式,只有函数声明形式才能被提升。
function test(){
promote();
function promote(){
console.log("函数声明提升成功");
}
}
test();
能够打印出“函数提升成功”
function test(){
promote();
var promote = function promote(){
console.log("函数表达式不能被提升");
}
}
test();
执行报错:“promote is not a function”
再看一段代码的输出
var a = 2;
function A() {
console.log(a);
var a = 1;
function a() {
}
}
A();
以上代码类似于:
var a;
function A() {
var a;
function a() {
}
console.log(a);
a = 1;
}
a = 2;
A();
运行结果打印出了方法a
这是因为先把变量提升了,接着函数也提升了,变量名又和函数名同名,最终函数声明的a覆盖了变量声明的 a导致最终a为声明的函数。
var fn = 721;
function fn() {
console.log('函数声明能被提升');
}
console.log(fn);
fn();
以上代码类似于下面代码:
var fn;
function fn() {
console.log('函数声明能被提升');
}
fn = 721;
console.log(fn);
fn();
结果为:721
结果为:fn2 is not a function
相当于函数声明被提升,变量fn被声明且赋值为721,且由于同名又覆盖了函数声明。
var fn = 721;
var fn = function() {
console.log('函数表达式声明不能提升');
}
console.log(fn);
fn();
以上代码类似于下面代码:
var fn;
fn = 721;
var fn;
fn = function() {
console.log('函数表达式声明不能提升');
}
console.log(fn);
fn();
结果为:function fn()
结果为:函数表达式声明不能提升
相当于声明了两个同名的变量并发生了覆盖