var
和 let
的区别,实际上就是 ES3
定义变量 和 ES6
定义变量的区别
ES3
定义变量的方式:var
function
ES6
定义变量的方式:let
const
class
import
ES6
语法相对于ES3
来说,最大的特点就是让JS
变得更加严谨,告别JS
松散的特点
思维导图
一、LET
和 VAR
的区别
1、变量提升方面
let
不存在变量提升,所以变量只能在声明定义后使用var
存在变量提升
console.log(n); //=>undefined
var n = 10;
console.log(n); //=>10
console.log(n); //=>Uncaught ReferenceError: Cannot access 'n' before initialization LET不存在变量提升,所以变量只能在声明定义后使用
let n = 10;
console.log(n); //=>10
复制代码
2、重复声明方面
let
是不允许重复声明的- 在当前上下文中,不管用什么方式,只要声明了这个变量,都不能基于
let
重复声明了,会报错 - 是否重新声明,并不是在代码执行阶段检测的,而是在词法解析的阶段检测的
- 在当前上下文中,不管用什么方式,只要声明了这个变量,都不能基于
var
允许重复声明- 浏览器本身只识别一次,但不会报错
var n = 12;
var n = 13;
console.log(n); //=>13
let n = 12;
let n = 13;
console.log(n); //=>Uncaught SyntaxError: Identifier 'n' has already been declared
var n = 12;
let n = 13; //=>Uncaught SyntaxError: Identifier 'n' has already been declared
//=>是否重新声明,并不是在代码执行阶段检测的,而是在词法解析阶段检测的(词法解析阶段类似于变量提升,在代码还没有执行之前,就发生了,一旦发现有词法错误 SyntaxError ,当前代码都不会再执行了)
console.log('OK');
var n = 12;
let n = 13; //=>报错 但是OK都没有被执行
复制代码
词法解析、词法错误、语法错误
- 词法解析:词法解析阶段类似于变量提升,在代码还没有执行之前就发生了
- 词法错误
SyntaxError
:在词法解析阶段报错,代码还没有执行就报错,一旦发现有词法错误SyntaxError
,当前代码不会在执行了- 语法错误
ReferenceError
:在代码执行阶段报错,报错前的代码会正常执行
3、全局对象GO方面
在全局上下文中
- let 声明的变量仅仅是全局变量,和GO没什么关系
- var 声明的变量即是全局变量,也相当于给GO(window)设置了一个属性,而且两者建立映射机制
var n = 10;
console.log(window.n); //=>10
let n = 10;
console.log(window.n); //=>undefined
复制代码
4、暂时性死区问题
let
能解决基于typeof
检测一个没有被声明过的变量结果是"undefined"
的暂时性死区问题;var
不能
浏览器有一个
BUG
(暂时性死区):基于typeof
检测一个没有被声明过的变量,并不会报错,结果是"undefined"
;但是从正确角度来讲应该报错才是正常的!
console.log(typeof n); //=>"undefined"
//=> 如果这个变量在后面会用`LET`声明,则前面再基于`typeof`检测就会报错:不能在声明之前使用
console.log(typeof n); //=>Uncaught ReferenceError: Cannot access 'n' before initialization
let n = 10;
复制代码
5、块级作用域
let
在遇到除对象/函数等大括号以外的大括号(例如判断和循环)时,会形成新的块级作用域var
没有块级作用域
二、LET
的块级作用域
在整个JS
中,目前为止我们接触的上下文(作用域)只有两种
- 全局上下文
EC(G)
- 函数执行形成的私有上下文
EC(XX)
此时循环体或者判断体等,都不会单独形成私有的上下文,里面的变量都是所在上下文中的变量。
if (1 === 1) {
var n = 10;
console.log(n); //=>10
}
console.log(n); //=>10
for (var i = 0; i < 5; i++) {...}
console.log(i); //=>5
复制代码
但是在
ES6
中,提供了一个新的上下文(作用域)形式:块级作用域
- 除对象/函数等大括号以外,如果在其余的大括号中(例如:判断和循环)出现
LET/CONST
等,则会把当前大括号包起来的部分形成一个独立的私有上下文,基于LET/CONST
创建的变量是当前块级作用域域中的私有变量;
if (1 === 1) {
var n = 10; //=>n是全局变量
let m = 20; //=>m是当前大括号包起来的 私有的 块级作用域中的 私有变量
console.log(m); //=>20
}
console.log(n); //=>10
console.log(m); //=>Uncaught ReferenceError: m is not defined
// 类似于形成两个闭包(这里叫做私有的块级作用域)
{
let x = 10,
y = 20;
console.log(x, y);
}
{
let x = 100,
y = 200;
console.log(x, y);
}
for (let i = 0; i < 3; i++) {
console.log(i); //=>0 1 2
}
console.log(i); //=>Uncaught ReferenceError: i is not defined
复制代码
循环中遇到let/const
等
图解:
1、创建一个总的块级作用域(父作用域):用来控制循环一轮轮执行
2、每一轮循环都会形成一个新的私有上下文(子作用域):
- 用来实现循环中要处理的业务需求
- 会把每一轮循环
i
的值,作为子作用域中的私有变量
和我们基于 VAR
,每一轮循环都创建一个闭包类似
三、LET
和 CONST
的区别
CONST
声明的变量,不能重新指向新的值(不能修改指针的指向);
let n = 10;
n = 20;
console.log(n); //=>20
const n = 10;
n = 20; //=>Uncaught TypeError: Assignment to constant variable.
console.log(n);
复制代码
一道经典面试题
用
const
定义的变量值,永远不能被修改了?
- 答:不对
- 1、
const
不能更改指的是:这个变量不能在和其他值进行关联了,也就是不能修改const
声明变量的指向- 2、但是可以在不改变指向的情况下,修改堆内存中的信息(这样也是把值更改了)
- 3、所以:
const
声明的变量,不能修改他的指针指向,但是可以改变其储存值的
例如下面这种情况:
const obj = {
name: '金色小芝麻'
};
obj = [10, 20]; //=>Uncaught TypeError: Assignment to constant variable.
//=> 它不能更改指的是:obj这个变量不能在和其它值进行关联了,也就是不能修改const声明变量的指向
obj.name = '哈哈';
console.log(obj);//=>{name: '哈哈'};
//=>但是可以在不改变指向的情况下,修改堆内存中的信息(这样也是把值更改了),
//=> 所以记住:const声明的变量,不能修改它的指针指向,但是可以改变其存储值的
//=> 所以也就不能说const创建的是一个常量;