552 let、const、var及其区别,变量提升,前端代码中的上下文(作用域),循环中的 IIFE、块级作用域,循环绑定事件的优化

let、const、var及其区别,变量提升

代码获取到后:

词法解析(AST):把代码拆成对应的字符,并且识别成浏览器可以解析的对象。

上下文 --> 【初始化】作用域链、【初始化】this、形参赋值......【最后】变量提升 --> 代码执行

1594108282520

/*

  • JS中声明变量或者函数的方式
  • 【传统】
  • var n = 10;
  • function func(){} -> var func=function(){};
  • 【ES6】
  • let n = 10;
  • const m = 20;
  • let func = () => {};
  • import xxx from 'xxx';
    */

/*
const设置的是常量,存储的值不能被改变 【不对】
const创建的变量,它的指针指向一旦确定,不能再被修改【正确】
let设置的是变量,存储的值可以改变 【正确】
*/

const n = 10;
n = 20; // => Uncaught TypeError: Assignment to constant variable.
console.log(n);



const obj = {
  name: 'xxx'
};
obj.name = "哈哈嘿嘿";
console.log(obj);  // =>  {name:"哈哈嘿嘿"}



// ------------------------------



/*
 * let 和 var 的区别?
 *    =>  let不存在变量提升
 *    =>  let不允许重复声明
 *    =>  let会产生块级作用域
 *    =>  暂时性死区的问题
 */

// 变量提升:在当前上下文代码自上而下执行之前,会把所有带var/function关键字的进行提前的声明或者定义(带var是只声明,带function是声明+定义(赋值)都完成了)
/*
 * EC(G)
 *   变量提升: var a;  func=AAAFFF000;
 *   代码执行:
 */
console.log(a); // => undefined
func(); // => "OK"
var a = 12;
function func() {
  console.log('OK');
}


// ------------------------


/*
 * EC(G)
 *    变量提升:--
 *    代码执行
 */

console.log('STRAT');
console.log(a); // => Uncaught ReferenceError: Cannot access 'a' before initialization 代码执行中遇到输出a,检测到下面有基于let声明的操作,则提示不允许在声明之前使用这个变量
func();
let a = 12;
let func = () => {
  console.log('OK');
};



// ------------------------


// Uncaught SyntaxError: Identifier 'a' has already been declared 重复声明的检测和报错,不是发生在代码执行阶段,发生在词法解析阶段(不论基于什么声明的变量,只要上下中有这个变量,都不能再基于let重复声明了)
console.log('START');
var a = 12;
let a = 13;
console.log(a);


// ------------------------



/*
 * 暂时性死区(浏览器的BUG)
 * 《深入理解ES6》:使用 let 或 const 声明的变量,在达到声明处之前都是无法访问的,试图访问会导致一个引用错误,即使在通常是安全的操作时(例如使用 typeof 运算符),也是如此。
 */
console.log(a); //Uncaught ReferenceError: a is not defined
console.log(typeof a); // => 检测一个未被声明的变量,不会报错,结果是"undefined"
typeof window !== "undefined"  // => 说明当前环境下存在window(浏览器环境) JQ源码中也是基于这样的方式处理的

console.log(typeof a); // => Uncaught ReferenceError: Cannot access 'a' before initialization
let a;

前端代码中的上下文(作用域)

/*
 * 前端代码中的上下文(作用域)
 *    1. 全局上下文
 *    2. 函数执行形成的私有上下文
 */

var a = 12;
if (1 == 1) {
  console.log(a); // 12
  var a = 13;
  console.log(a); // 13
}
console.log(a); // 13



// ------------------------


// 如果代码块中出现了 let、const、function,则当前代码块会产生一个 块级上下文(词法、块级作用域)  =>  私有的上下文
let a = 12;
if (1 == 1) {
  // console.log(a); // => Uncaught ReferenceError: Cannot access 'a' before initialization
  let a = 13;
  console.log(a); // 13
}
console.log(a); // 12


// ------------------------


// 即使混在一起,跨级作用域只对let、const、function生效,对var不生效
var n = 12;
let m = 13;
if (1 == 1) {
  var n = 120;
  let m = 130;
  console.log(n, m); // 120 130
}
console.log(n, m); // 120 13



// ------------------------



// 循环
for (var i = 0; i < 5; i++) {
  // i 都是全局的
}
console.log(i); // => 5

for (let i = 0; i < 5; i++) {
  // 私有的块级上下文
  // 循环几次会产生几个块级上下文 【上下文是平级的,不嵌套。】
}
console.log(i); // => Uncaught ReferenceError: i is not defined

循环中的 IIFE、块级作用域

// 设置5个1s后执行的定时器
for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}
// i是全局变量
// 第一轮循环  i=0  设置定时器 1000MS => {} 【任务队列】  i++
// 第二轮循环  i=1  ...
// 循环结束 i=5
// -----
// 达到时间后,依次把任务队列中的五个定时器到时候后要做的事情去执行
// ()  =>  { console.log(i); }  i不是自己私有上下文中的变量,则找其上级上下文(全局),但是此时全局的 i = 5


// ------------------------


// 设置5个1s后执行的定时器
for (var i = 0; i < 5; i++) {
  // 每一轮循环:把自执行函数执行
  // EC(AN1) [不会释放]
  // 作用域链:<EC(AN1),EC(G)>
  // 形参赋值:i=0
  (function (i) {
    // 设置一个定时器(异步任务:任务队列)
    setTimeout(() => {
      console.log(i);
    }, 1000);
  })(i);
}


// ------------------------


function func(i) {
  return function anonymous() {
    console.log(i);
  }
}

for (var i = 0; i < 5; i++) {
  // 第一轮循环:i=0  设置定时器的时候,把func函数执行,传递0进去,把返回的匿名函数anonymous设置给定时器  这样func执行形成的上下文是不被释放的(形参i=0也是不释放的)
  setTimeout(func(i), 1000); // 执行func(i)返回函数anonymous
}
// 1000MS后执行的是绑定的anonymous 


// ------------------------


for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

循环绑定事件的优化

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
  <title>哈哈哈</title>
  <!-- IMPORT CSS -->
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    html,
    body {
      height: 100%;
      overflow: hidden;
    }

    button {
      padding: 5px 10px;
      cursor: pointer;
    }
  </style>
</head>

<body>
  <button value="pink">红</button>
  <button value="yellowgreen">绿</button>
  <button value="skyblue">蓝</button>
</body>

</html>
<script>
  // 补充:var 和 function 的提升顺序
  // 结论:先var a = undefined;然后 a = 函数a的地址 0x111
  console.log(a) // 函数a
  var a = 11
  function a() {
    console.log(22)
  }

  console.log(b) // 函数b
  function b() {
    console.log(66)
  }
  var b = 55
</script>

<script>
  // 基于事件委托实现多元素的事件绑定,要比传统循环一个个的给元素进行事件绑定,性能提高40%~60%
  document.body.onclick = function (ev) {
    var target = ev.target;
    if (target.tagName === "BUTTON") {
      // this -> body
      this.style.background = target.value;
    }
  };


  // ------------------------------


  var body = document.querySelector('body')
  var buttons = document.querySelectorAll('button')
  var arr = ['pink', 'yellowgreen', 'skyblue']

  for (var i = 0; i < buttons.length; i++) {
    var item = buttons[i];
    item.myIndex = i; // => 在循环的时候,把每一个按钮的索引赋值给当前按钮(元素对象)的myIndex自定义属性
    item.onclick = function () {
      // this  =>  当前点击的这个按钮
      body.style.background = arr[this.myIndex];
    };
  }



  // ------------------------------


  // 都是利用闭包的机制去解决的
  for (var i = 0; i < buttons.length; i++) {
    buttons[i].onclick = (function (i) {
      // IIFE中必须 return一个函数,作为IIFE的返回值
      return function anonymous() {
        body.style.background = arr[i];
      };
    })(i);
  }


  // ------------------------------


  for (let i = 0; i < buttons.length; i++) {
    buttons[i].onclick = function () {
      body.style.background = arr[i];
    };
  }
</script>
原文地址:https://www.cnblogs.com/jianjie/p/13853211.html