zoukankan      html  css  js  c++  java
  • 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>
    
  • 相关阅读:
    Windows平台下Glade+GTK开发环境的搭建
    uCOSII移植STM32F10x_Keil
    C语言中的内存管理与双向链表
    Windows平台下Glade+GTK实现多线程界面的探讨
    C语言中可变形参个数的函数实现
    从STM32的位带操作重谈嵌入式中寻址与对齐的理解
    uCOSII的中断ARM7实现中断嵌套的方法探究
    uCOSII中的内存管理C语言构建完整的微型动态内存管理机制
    uCOSII中的任务切换图解多种任务调度时机与问题
    uCOSII中的任务切换机制
  • 原文地址:https://www.cnblogs.com/jianjie/p/13853211.html
Copyright © 2011-2022 走看看