看过的一些文章,还有一些其它的区别,但是我认为最常见的区别就是上述五个!
let n = 10;
n = 11;
console.log(n);//=>11
const m = 10;
m = 11;//index.js:6 Uncaught TypeError: Assignment to constant variable.
console.log(m);
const obj = { name: 'cat' };
obj.age = 1;
console.log(obj);//{name: "cat", age: 1}
一般前两组代码常被一些文章拿来说明const是定义常量的说明,这样的说法是不严谨的。
对比分析上面的三组代码:等号赋值其实就是指针指向的过程:
在代码执行过程中即执行上下文中,变量赋值的操作基本都是先创建值存储在栈或者堆里,再创建变量存储在AO或VO,最后让变量和指针关联。
console.log(n);//undefined
var n = 10;
console.log(m);//index.js:4 Uncaught ReferenceError: Cannot access 'm' before initialization
let m = 10;
//======================
fn1();
function fn1(){}
fn();//Uncaught ReferenceError: Cannot access 'fn' before initialization
//函数表达式,函数只能在其声明之后使用
let fn = function () { };
var m = 1;
var m = 1;//不报错
let m = 1;
let m = 1;//Uncaught SyntaxError: Identifier 'm' has already been declared
let m = 1;
var m = 1;//Uncaught SyntaxError: Identifier 'm' has already been declared
var m = 1;
let m = 2;//Uncaught SyntaxError: Identifier 'm' has already been declared
真实项目中,代码量较多,除了声明前置,可以避免重复声明报错的问题,要尽可能把业务拆分,每一个小模块单独在一个闭包中,降低相同上下文中变量声明的个数,这也是防止重复声明的办法。
3.在全局上下文中,基于LET声明的全局变量和全局对象GO(window)没有任何的关系,VAR声明的变量会和GO有映射机制
let bb = 10;
console.log(window.bb);//undefined
//window没有当前的属性bb,对象的成员访问,没有此属性时是undefined
var aa = 11;
console.log(window.aa);//11
console.log(typeof n); //=>"undefined" 检测一个没有被声明过的变量,不会报错,结果是UNDEFINED而已 “暂时性死区”:浏览器BUG
使用let声明会报错,有效解除暂时性死区
console.log(typeof n); //Uncaught ReferenceError: Cannot access 'n' before initialization
let n;
for (var i = 0; i < 5; i++) {
setTimeout(_ => {
console.log(i);
}, 1000);
}
//1s后输出5个都是5的值
//解决方法一:闭包,每一次的循环都形成一个不被销毁的闭包,这样会产生很多闭包
for (var i = 0; i < 5; i++) {
(i => {
setTimeout(_ => {
console.log(i);
}, 1000);
})(i)
}
//1s后输出 0、1、2、3、4
//解决方法二:使用let产生块级上下文
for (let i = 0; i < 5; i++) {
setTimeout(_ => {
console.log(i);
}, 1000);
}
//1s后输出 0、1、2、3、4
在理解上述代码前先来看下块级作用域,除函数外的块级上下文,在大括号中用let会形成一个私有的块级上下文,在此之外的上下文不能访问块级上下文中的变量。
let n = 10;
{
let n = 11;
console.log(n);//11
}
console.log(n);//10
//==================
{
let n = 11;
console.log(n);//11
}
console.log(n);//n is not defined
在循环中使用let的时候,是如何处理块级上下文的.
for (let i = 0; i < 5; i++) {
setTimeout(_ => {
console.log(i);
}, 1000);
}
用图片来展示循环的上下文是如何创建的:
现在有一个循环五次的循环,创建了六个块级上下文,不是五个。
首先有一个大的块级上下文,这个上下文是控制i的值累加和验证循环条件=>控制循环的走向,就是小括号中的部分。
第一轮循环:i=0–>条件成立,每一次条件成立都要执行循环体中的内容,循环体执行,形成小的执行上下文i=0–>设置定时器
【大的执行上下文中执行i++,大的块级上下文中i=1】
第二轮循环:i=1–>条件成立–>设置定时器
…循环五次,接下来的操作都是类似的
五次结束之后一共形成六个执行上下文,第一个大的执行上下文是控制循环走向的,每一次只要条件成立执行循环体就形成一个独立的小的执行上下文。在小的上下文中要给全局下window添加定时器,就是window.setTimeout设置方法,我们形成的这些小的上下文是不销毁的,整个循环结束后大的执行上下文是会释放(销毁)的。每一个小的执行上下文 中的定时器都赋值一个堆地址,机制跟闭包一样,所以小的执行上下文不会被销毁。定时器到达时间后要输出i,i不是定时器的这个小函数私有的,顺着原型链找到上级上下文即这个函数创建的地方,找到设置定时器的循环体中这个小的执行上下文,用的i就是此处的i。