[JS]04.var和let的区别

193 阅读4分钟

1. js中声明变量的方式

  • 传统方式: var、function
  • ES6:let、const、import(模块导入)

2. let和const

  • let用来声明一个变量,变量存储的值可以修改
let n = 12;
n = 13;
console.log(n); // 13
  • const声明的变量,一旦赋值,则不能和其它值关联(不允许指针重新指向)
const m = 12;
m = 13; // Uncaught TypeError: Assignment to constant variable.
console.log(m);

const obj = {name: 'xxx'};
obj.name = 'yyy';
console.log(obj);  // {name: 'yyy'}
obj = {sex: 'male'}; // Uncaught TypeError: Assignment to constant variable.

3. let和var

3.1 变量提升

  • 在当前上下文,代码执行之前,会吧所有带var / function关键字的进行提前声明或定义
    • 带var的只是提前声明
    • 带function的是声明+定义
// EC(G) 全局上下文中,变量提升 var n
console.log(n) // undefined
console.log(m) // Uncaught ReferenceError: Cannot access 'm' before initialization
var n = 12;
let m = 13;

3.2 var有变量提升,let没有

3.3 var的映射机制

  • 全局上下文中,基于var声明的变量,也相当于给GO(G),声明一个变量,同时给全局对象window,新增一个属性,并且任何一个发生值得改变,另外一个也跟着变化
  • 基于let声明的变量,就是全局变量,和windows对象,没有任何关系
let n = 12;
console.log(n) // 12
// 对象的成员访问中,不存在这个成员,不报错,会提示undefined
console.log(window.n) // undefined

var n = 12;
console.log(n) // 12
console.log(window.n) // 12

n = 12;
console.log(n) // 12
console.log(window.n) // 12

var m = 500; // VO(G) 中n=500 GO(widnow中)n=500 两个部分都有
console.log(m)   // 500
console.log(window.m) // 500
window.m = 250
console.log(m)  // 250 映射

// 未被声明的变量处理,项目中尽量不使用不经声明的变量 for循环中
n=13; // window.n = 13, 没有基于任何关键字声明的,不是全局变量
console.log(n)// 13,看是否为变量,是全局按照全局,如果不是,就去window的属性里找
console.log(m) // Uncaught ReferenceError: m is not defined


// fn()执行形成私有上下文,m既不是形参,也不是私有上下文声明的,m就不是私有变量,往上级上下文寻找,全局也没定义,则window.m = 13
// 设置,相当于给window设置了属性,获取直接报错
function fn() {
  m = 13 
  console.log(m) // 13
  console.log(n) // Uncaught ReferenceError: n is not defined
}
fn() 
console.log(m) //12

3.4 在相同的上下文中,let不允许重复声明

  • 无论之前基于何种方式声明,只要声明过,就不能基于let声明了。
  • var可以重复声明,浏览器按照声明一次处理。
/*
var 语法不会报错,第二次不会识别成声明,识别成赋值,浏览器不处理
let会报错,代码运行之前就报错,不执行前面的console
代码执行之前,浏览器会做很多事情,词法分析,变量提升
词法分析阶段,发现有基于let/const并且重复声明,则直接报语法错误,整个代码不会执行
定义报错都在词法分析阶段就会报错,语法错误
*/
console.log('ok')
var n = 12;
var n = 13;
let n = 14; 

3.5 暂时性死区

  • 浏览器暂存的BUG
// n is not defined 输出一个未被定义变量
console.log(n); 

// 基于typeof检测一个未被声明的变量
// 不报错,结果是undefined
console.log(typeof n)

// 后定义n,会报错,不允许在变量声明前使用
// 代码运行阶段错误,不是词法分析阶段错误
console.log(typeof n)
let n = 12

3.6 let / const / function 会在{}中产生块级私有上下文

  • 上下文 & 作用域:
    • 全局上下文
    • 函数执行形成的私有上下文
    • 块级作用域(块级私有上下文)
      • 除了对象和函数(大括号是栈) let obj = {} / function fn() {} 的大括号之外,例如 判断体if(){},循环体for(){},代码块{},都可能产生块级上下文
// var声明,不产生块级作用域,n都是全局的,12
{
  var n = 12;
  console.log(n) // 12
}
consoel.log(n) // 12


// let 声明,大括号会形成块级上下文
// m 是代码块中私有的
{
  var n = 12;
  console.log(n) // 12
  
  let m = 13
  console.log(m) // 13
}
consoel.log(m) // m is not defined


// for循环中let产生块级上下文
for (var i = 0; i < 5; i++) {
    console.log(i) // 0~4
}
console.log(i) // 5

// let 定义的 i 不是全局的,是块级上下文中私有的
for (let i = 0; i < 5; i++) {
    console.log(i) // 0~4
}
console.log(i) // i is not defined


// 此种写法 i 是全局下声明的全局变量
// 不会产生若干个私有块级上下文,or 若干个闭包
let i = 0;
for (; i < 5; i++) {
    console.log(i) // 0~4
}
console.log(i) // 5
  • 执行流程 image.png

  • 循环绑定let执行流程

    • 浏览器在每轮循环中,形成的闭包
    • 被占用,闭包,不会释放,性能差 image.png