let,var,const 相关的小问题

276 阅读4分钟

let,var,const相关的小知识点,便于之后复习。

主要包括:var 和let 的变量提升、在全局变量中的影响、let和const声明和赋值、for循环、let块级作用域的理解。

  1. var的变量提升
        console.log(a); // => undefined
		fn(); // => ok
		var a = 12;
		function fn() {
			console.log('ok');
			}

变量提升解析

  ECStack
	EC(G)
	  VO(G)
	  a = 12
	 fn = AAAFFF111  fn[[scope]]=VO(G)
	//  在当前执行上下文代码执行之前,首先会把所有带var或者function关键字的声明或者定义(带var的只是提前声明,带function会提前的声明+定义)
	  console.log(a); //=>undefined
	  fn(); //已经存在了,可以执行 =>ok
	  a = 12;
	  console.log(a); //=>12  
  1. let的变量提升
  • let不存在变量提升。
 ECStack
	 EC(G)
	 VO(G)
	代码执行
	 let a = 12; // 代码执行到a =12时才会声明定义
	 --------------------------------
	 console.log(a); //=>Uncaught ReferenceError: Cannot access 'a' before initialization
  	 let a = 12; 
  • let function 现在项目中创建函数,一般都是基于函数表达式来实现,这样防止其提前变量提升
        let fn = function () {

		};
		fn(); 
  1. 练习题
        fn(); // 1 
		function fn(){ console.log(1); } 
		fn(); //2 
		function fn(){ console.log(2); } 
		fn(); //3 
		var fn = function(){ console.log(3); } //=> fn = AAAFFF333 // fn提前声明,但不赋值, 运行到这行的时候,赋值AAAFF333
		fn(); //4  
		function fn(){ console.log(4); }
		fn(); //5  
		function fn(){ console.log(5); } 
		fn(); //6

答案:5 5 5 3 3 3

/*
		 * EC(G)
		 *   VO(G)
		 *     fn = AAAFFF111
		 *        = AAAFFF222
		 *        = AAAFFF444
		 *        = AAAFFF555 (变量提升阶段完成,fn=AAAFFF555)
		 * 代码执行
		 */
  1. let和var对全局变量
  • 全局变量对象VO(G)中声明的变量(用var声明的),也会给全局对象GO中增加一个对应的属性;但是用let声明的变量不存在这个特点
         var x = 12;
		 console.log(window.x); //=>12

		 let x = 12;
		 console.log(window.x); //=>undefined
  • 私有上下文
 function fn() {
	  /*
	 	 * EC(FN)
		 *   AO(FN) 
		 *     x = 100
	 */
	 	var x = 100;
		console.log(fn.x); //=>undefined  仅限于全局有创建全局变量, 也相当于给全局对象设置属性有这个特点,私有的执行上下文中就是私有变量
		 }
	/ fn();
  • function 中不带var
 function fn() {
			x = 100;
		// 此时的x不是AO(FN)中的私有变量,则向全局找,此处相当于给全局VO(G)变量对象中设置了一个x的全局变量,也相当于给全局对象GO设置了一个x的属性
		}
		fn();
		console.log(window.x);  // => 100
		console.log(x);  // => 100
  1. let重复声明,执行不下去
  • 编译阶段(编译器)
    • 词法解析 =>AST抽象语法树(给浏览器引擎去运行的)
  • 引擎执行阶段
    • ECStack => EC(G) => VO(G) ...
    • 带var的是可以重复声明的(词法解析可以审核过),执行阶段遇到已经声明过,不会在重新声明;
    • 但是let是不可以,词法解析阶段都过不去,也就不存在引擎去执行代码的阶段了;
	   console.log('OK'); // 不执行
		let x = 12;
		console.log(x);// 报错
		let x = 13;
		console.log(x); // 不执行
  • 未声明,提前用let的变量,会进去执行阶段
console.log(a) //  Uncaught ReferenceError: a is not defined  
let a=12
  1. for循环的问题
  • 定时器异步任务
for (var i = 0; i < 5; i++) {
			//定时器是异步操作:不用等定时器到时间,继续下一轮循环
			setTimeout(_ => {
				console.log(i);
			}, 10);
		} 
// =>10MS后连续输出五个5
  • 自执行函数
 for (var i = 0; i < 5; i++) {
	 	(function (i) {
/*
		 * EC(自执行)
		 *   AO(自执行) 
		 *    i = 0~4
		 *   创建一个匿名函数_=>{...} BBBFFF000
		 *   BBBFFF000[[scope]]:AO(自执行)
		 * 
		   window.setTimeout(BBBFFF000,10);
	 		 */
		 		setTimeout(_ => {
	 /*
			 * EC(BBBFFF000) 
			 *   AO(BF0)  <AO(BF0),AO(自执行)>
			 */
		 			console.log(i);
		 		}, 10);
			})(i);
		 }


// =>10MS后连续输出0~4
* =>每一轮循环都执行自执行函数,形成全新的执行上下文EC并且把每一轮循环的全局i的值,当做实参赋值给私有下文中的私有变量i(形参变量)10MS定时器触发执行,用到的i都是私有EC中的保留下来的i
*  =>充分利用闭包的机制(保存/保护)来完成的(这样处理不太好,循环多少次,就形成了多少个不销毁的EC)
  • 用let 解决
    • 因为let存在块级作用域,而var没有
for (let i = 0; i < 5; i++) {
			setTimeout(_ => {
				console.log(i);
			}, 10);
	  }
	console.log(i);//=>Uncaught ReferenceError: i is not defined 用来累计的i也是父块作用域中的,也不是全局的,全局还是不能用 */

过程解析

{
			//=>父块作用域
			let i = 0;

			// 第一轮循环
			{
				//=>子块作用域
				let i = 0;
				setTimeout(_ => {console.log(i);}, 10);
			}
			父块作用域i++; //=>i=1
			// 第二轮循环
			{
				//=>子块作用域
				let i = 1;
				setTimeout(_ => {console.log(i);}, 10);
			}
			// ....
		}
  1. let块的范围的理解
if (1 === 1) {
			let x = 100;
			console.log(x);
		}
		console.log(x); //=>Uncaught ReferenceError: x is not defined 
let a = 100;
		switch (a) {
			case 100:
				let x = 200;
			case 200:
				// let x = 300; //=>Uncaught SyntaxError: Identifier 'x' has already been declared
				break;
		}
		console.log(x); //=>Uncaught ReferenceError: x is not defined
 try {
			let x = 100;
			console.log(x); //=>100
			console.log(a);
		} catch (e) {
			let y = 200;
			console.log(y); //=>200
		}
		// console.log(x);//=>Uncaught ReferenceError: x is not defined
		// console.log(y); //=>Uncaught ReferenceError: y is not defined
  • 暂时性死区
console.log(typeof a); //=>undefined  JS的暂时性死区(暂时没解决的BUG)
-----------------------------------------
console.log(typeof a); //=>Uncaught ReferenceError: Cannot access 'a' before initialization
let a;
  1. let和const
  • let创建的变量是可以更改指针指向的(也就是可以重新赋值的),但是const声明的变量是不允许改变指针指向的。
        let x = 100;
		x = 200;
		console.log(x); //=>200

		const y = 100;
		y = 200; //=>Uncaught TypeError: Assignment to constant variable.
		console.log(y);