let、const和var的区别

var

  1. 存在变量提升 即变量可以在声明之前调用,值为undefined
  2. 可以重复声明
  3. 函数中使用var声明变量的时候,该变量是局部的;对于声明在任何函数外的变量来说是全局的。

let

  1. 不存在变量提升,let声明变量前,该变量不能使用(暂时性死区)。
  2. let命令所在的代码块内有效,在块级作用域内有效。
  3. let不允许在相同作用域中重复声明,注意是相同作用域,不同作用域有重复声明不会报错。

const

  1. 不存在变量提升
  2. 存在块级作用域
  3. 同一作用域不允许重复声明变量
  4. const声明一个只读的常量。一旦声明,常量的值就不能改变。const并不是变量的值不能改动,而是变量指向的内存地址所保存的数据不得改动。例如,在引用内容是对象的情况下,这意味着可以改变对象的内容。

变量提升和函数提升

在ES6之前,js是没有块级作用域,只有两种作用域

  • 全局作用域
  • 函数作用域

什么是没有块级作用域?

1
2
3
4
5
var i=1;
if(true){
var a = '123';
}
console.log(a);// 123

变量a是声明在if的{}里,但在js里面,因为没有块级作用域,所以此时的变量a的作用域是全局作用域。

什么是变量提升?

在我们的js中,代码的执行时分两步走的,1、解析 2、一步一步执行

那么变量提升就是变量声明会被提升到作用域的最顶上去,也就是该变量不管是在作用域的哪个地方声明的,都会提升到作用域的最顶上去。

1
2
3
4
5
6
7
8
console.log(a);// undefined
var a='hello';
console.log(a);// hello
// 等价于
var a;
console.log(a);// undefined
a='hello';
console.log(a);// hello

看几个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var a=10;// 全局变量:任何一个地方都可以访问,包括函数内部
function fn(){
console.log(a);// undefined
var a=20;// 局部变量,而且会变量提升,就是把声明提升到作用域的最顶上去
console.log(a);// 20
}
fn();// 调用fn
console.log(a);// 10

// 稍作改动
var a=10;// 全局变量:任何一个地方都可以访问,包括函数内部
function fn(){
console.log(a);// 10
a=20;// 全局变量的重新赋值
console.log(a);// 20
}
fn();// 调用fn
console.log(a);// 20

再看一个例子:

1
2
3
4
5
6
7
var a=1;
if(true){
console.log(a);// 1
var a = 2;// 由于没有块级作用域,此处相当于在全局作用域重复声明了两次,第二次声明被忽略,仅用于赋值
console.log(a);// 2
}
console.log(a);// 2

什么是函数提升?

函数声明式,会将函数的声明和定义一起提升到作用域的最顶上去。

1
2
3
4
5
6
fn();// 可正常调用
console.log(fn);
function fn(){
console.log('123');
}
fn();// 可正常调用

如果是这种写法:函数表达式声明的函数,相当于变量提升,如果使用constlet,则无法提升。

1
2
3
4
5
6
fn();// 可正常调用
console.log(fn);// undefined
var fn = function(){
console.log('123');
}
fn();// 可正常调用

优先级

1
2
3
4
5
console.log(fn);// ƒ fn(){}

function fn(){};
var fn='hello';
console.log(fn);// hello

最后的总结:

1:所有的声明都会提升到作用域的最顶上去

2:同一个变量只会声明一次,其他的会被忽略掉。

3:函数声明的优先级高于变量声明的优先级,并且函数声明和函数定义的部分一起被提升。

函数传参的一道题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let a = {
name: 'Julia',
age: 20
}

function change(a) {
// 这里其实相当于 var a = a;
a.age = 24; // 此时局部变量a指向外部a的地址,所以可以修改
// 此时相当于给局部变量a一个新的对象引用,不再指向外部a地址,所以无法修改
a = {
name: 'Kath',
age: 30
}
return a;
}
change(a);
console.log(a.age); // 24

作用域链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// var a
let a = {
name: 'Julia',
age: 20
}

function change() {
a.age = 24;
a = {
name: 'Kath',
age: 30
}
return a;
}
change();
console.log(a.age); // 这里函数内部的a并不是隐式声明,而是根据作用域链找到了window或script上的a

隐式声明

1
2
3
4
5
6
7
8
9
10
function change() {
// a.age = 24;
a = {
name: 'Kath',
age: 30
}
return a;
}
change();
console.log(a.age); // 函数隐式声明了一个全局变量a,