zoukankan      html  css  js  c++  java
  • 546 变量提升

    **栈内存、作用域、执行上下文【执行环境】**
    

    当浏览器开辟出供代码执行的栈内存后,代码并没有自上而下立即执行,而是继续做了一些事情:把当前作用域中所有带var、function关键字的进行提前的声明和定义 =>变量提升机制 【预解析】

    • 带var的只是提前声明(declare): “var a;” ,如果只声明,没有赋值,默认值是undefined
    • 带function的不仅声明,而且还定义了(defined): “a=13”定义其实就是赋值,准确来说就是让变量和某个值进行关联

    带var和不带var的区别

    //  => 在全局作用域下的区别
    /*
     * 不带var的:相当于给全局对象window设置了一个属性a 【而不是变量。】
     *    window.a = 13;
     */
    a = 13;
    console.log(a); //  => window.a
    
    /*
     * 栈内存变量存储空间
     *    b
     * 带var的:是在全局作用域下声明了一个变量b(全局变量),但是在全局下声明的变量也同样相当于给window增加了一个对应的属性(只有全局作用域具备这个特点) 【即是变量,又是window的属性。】
     */
    var b = 14; //  => 创建变量b & 给window设置了属性b
    console.log(b); //  => 14
    console.log(window.b); //  => 14
    

    console.log(a); // undefined
    var a = 12;
    var b = a;
    b = 13;
    console.log(a); // => 12
    
    
    // ---------------------
    
    
    console.log(sum(10, 20));  // 30
    function sum(n, m) {
      return n + m;
    }
    
    
    // ---------------------
    
    
    //  函数表达式方式,由于使用VAR来创建SUM,变量提升阶段只会声明变量,不会赋值,所以此时函数在前面执行,函数是没有值的,不能执行(真实项目中这种方式最常用,因为它操作严谨)
    console.log(sum); // =>undefined
    // sum(10, 20); // =>Uncaught TypeError: sum is not a function
    var sum = function (n, m) {
      return n + m;
    };
    // let sum = (n, m) => n + m;
    console.log(sum(10, 20));
    

    /*
     * 变量提升:在当前上下文中(全局/私有/块级),JS代码自上而下执行之前,浏览器会提前处理一些事情(可以理解为词法解析的一个环节,词法解析一定发生在代码执行之前)
     *    会把当前上下文中所有带VAR、FUNCTION关键字的进行提前的声明或者定义
     *       var a=10;
     *       声明declare:var a;
     *       定义defined:a=10;
     *    带VAR的只会提前的声明
     *    带FUNCTION会提前的声明 + 定义
     */
    
    
    /*
     * 代码执行之前: 全局上下文中的变量提升
     *    var a;   默认值是undefined
     */
    console.log(a); // => undefined
    var a = 12; // => 创建值12  不需要在声明a了(变量提升阶段完成了,完成的事情不会重新处理) a=12赋值
    a = 13; //全局变量a=13
    console.log(a); // => 13
    
    
    // ------------------------------------------
    
    
    /* 
     * 全局上下文中的变量提升
     *   func=函数   函数在这个阶段赋值都做了
     */
    func();
    function func() {
      var a = 12;
      console.log('OK');
    }
    
    
    // ------------------------------------------
    
    
    func(); // => Uncaught TypeError: func is not a function
    var func = function () {
      // 真实项目中建议用函数表达式创建函数,因为这样在变量提升阶段只会声明FUNC,不会赋值
      console.log('OK');
    };
    func();
    
    
    // ------------------------------------------
    
    
    var func = function AAA() {
      // 把原本作为值的函数表达式 匿名函数“具名化”(虽说是起了名字,但是这个名字不能在外面访问  => 也就是不会在当前当下文中创建这个名字)
      // 当函数执行,在形成的私有上下文中,会把这个具名化的名字做为私有上下文中的变量(值就是这个函数)来进行处理
      console.log('OK');
      console.log(AAA); // => 当前函数
      AAA(); // 递归调用, 而不用严格模式下都不支持的 arguments.callee 了
    };
    // AAA();  // => Uncaught ReferenceError: AAA is not defined
    func();
    
    
    // ------------------------------------------
    
    
    /*
     * EC(G)变量提升 
     */
    console.log(a); // => Uncaught ReferenceError: a is not defined
    a = 13;
    console.log(a);
    
    
    // ------------------------------------------
    
    
    /*
     * EC(G)变量提升:只有VAR/FUNCTION会变量提升(ES6中的LET和CONST不会) 
     */
    // 这里不是词法解析错误
    console.log('OK'); // => 'OK'
    console.log(a); // => Uncaught ReferenceError: Cannot access 'a' before initialization  不能在LET声明之前使用变量
    let a = 12;
    a = 13;
    console.log(a);
    
    
    // ------------------------------------------
    
    
    /*
     * 基于“VAR或者FUNCTION”在“全局上下文”中声明的变量(全局变量)会“映射”到GO(全局对象window)上一份,作为他的属性;而且接下来是一个修改,另外一个也会跟着修改;
     */
    var a = 12;
    console.log(a); // => 12  全局变量
    console.log(window.a); // => 12 映射到GO上的属性a
    
    window.a = 13;
    console.log(a); // => 13 映射机制是一个修改另外一个也会修改
    
    
    // ------------------------------------------
    
    
    /*
     * EC(G): 全局上下文中的变量提升
     *    不论条件是否成立,都要进行变量提升
     *    细节点:条件中带function的,在新版本浏览器中只会提前声明,不会再提前的赋值了
     *   [老版本]
     *      var a;
     *      func = 函数; 【老版本的函数,声明、赋值都提前。】
     *   [新版本]
     *      var a;   全局上下文中声明一个a也相当于 window.a
     *      func;
     */
    
    console.log(a, func); // => undefined undefined
    if (!("a" in window)) {
      // => "a" in window:检测a是否为window的一个属性   !TRUE  =>  FALSE
      var a = 1;
    
      function func() { }
    }
    console.log(a); // => undefined
    
    
    // ------------------------------------------
    
    
    /*
     * EC(G)变量提升
     *    fn => 1
     *       => 2
     *    var fn;  已经声明过了,不再重复声明,只需重新赋值
     *       => 4
     *       => 5
     * 全局上下文中有一个全局变量fn, 值是输出5的函数(此时window.fn => 5)
     */
    fn(); // => 5
    function fn() { console.log(1); }  // => 不再处理,变量提升阶段搞过了
    fn(); // => 5
    function fn() { console.log(2); }
    fn(); // => 5
    // => var fn不用再处理了,但是赋值在变量提升阶段没处理过,此处需要处理  fn = window.fn => 3
    var fn = function () { console.log(3); }
    fn(); // => 3
    function fn() { console.log(4); }
    fn(); // => 3
    function fn() { console.log(5); }
    fn(); // => 3
    
    
    // ------------------------------------------
    
    
    var foo = 1;
    function bar() {
      if (!foo) {
        var foo = 10;
      }
      console.log(foo); // 10
    }
    bar();
    
    
    // ------------------------------------------
    
    
    var a = 0;
    if (true) {
      a = 1;
      function a() { };
      a = 21;
      console.log(a); // 21
    }
    console.log(a); // 1
    
    
    // ------------------------------------------
    
    
    var a = 0;
    if (true) {
      a = 1; // => Uncaught ReferenceError: Cannot access 'a' before initialization
      let a = 10;
      a = 21;
      console.log(a)
    }
    console.log(a);
    
    
    // ------------------------------------------
    
    
    // Uncaught SyntaxError: Identifier 'a' has already been declared
    console.log(a);
    {
      // 【块级作用域,function提升,a被声明过。】
      console.log(a);
      var a = 10;
      function a() { }
      console.log(a);
    }
    console.log(a);
    

    变量提升练习题1

    * 在变量提升阶段,遇到大括号、判断体等,不论条件是否成立,都要进行变量提升

    * 起作用的:var、function

    *

    * IE低版本浏览器(<= IE10):

    * 都会进行变量提升,而且函数依然是 声明 + 定义 都完成了

    * 高版本浏览器:

    * 虽然也会进行变量提升,但是对于函数只声明,不会再定义了

    *

    * ES6新语法规范中,存在块级上下文(除对象的大括号中,出现let、const、function,都会把当前大括号形成一个私有的跨级上下文) => 兼容高版本浏览器

    *

    * 现在的浏览器很悲催,因为既要兼容低版本语法规范,还要适应高版本语法规范,所以对于有冲突的规范,会采用一些取中间的方式....

    console.log(foo) // undefined
    {
      function foo() { } // 【是这句代码之前的所有操作都提升,这句代码不提升,不包括这句代码】
      foo = 1;
      console.log(foo); // => 1
    }
    console.log(foo); // => 函数foo 
    
    
    // -------------
    
    
    // 全局变量提升: function foo;
    console.log(foo) // undefined
    {
      /*
       * 私有的块级上下文的变量提升:
       *    function foo() {alert('1')}
       *    function foo() {alert('2')} 【保留的】
       */
      console.log(foo); // => alert('2')的 函数foo 
      function foo() { alert('1') }
      foo = 1;
      // 会把当前代码之前对于foo的操作映射到全局一份 【是这句代码之前的所有操作都提升,这句代码不提升,不包括这句代码】
      function foo() { alert('2') }
    }
    console.log(foo); // => 1 
    
    
    // -------------
    
    
    // 全局  function foo;
    {
      // 私有 
      // function foo() {alert('1')}  
      // function foo() {alert('2')} 【保留】
      function foo() { alert('1') }
      foo = 1;
      function foo() { alert('2') } //把之前的操作映射给全局一份
      foo = 2;
      console.log(foo); // => 2
    }
    console.log(foo); // => 1 
    

    变量提升练习题2

    * 如果同时符合了这两个条件:

    * 1. 形参有赋值默认值

    * 2. 函数体中有声明过变量 var、function...

    * 此时的函数执行会形成两个上下文

    * 1. 私有的上下文

    * 2. 函数体所在大括号中的块级上下文 【在私有上下文中,用var、let、const等声明的变量属于块级上下文。】

    * => 函数体中遇到一个变量,我们首先看是否为块上下文中的变量,如果是,接下来都操作块上下文中的变量,和私有没关系;如果不是,操作的是私有的或者全局的...

    /*
     * 全局上下文
     *   var x; 
     *   function func(x,y...){...};
     */
    var x = 1;
    function func(x, y = function anonymous1() { x = 2 }) {
      /*
       * 私有上下文EC(1) 
       *   形参赋值:x = 5,  y = anonymous1
       *   变量提升:--
       */
      x = 3;  // 私有x = 3
      y();
      /* 
       * anonymous1()  私有向下文EC(2)  
       *    形参赋值:--
       *    变量提升:--
       * x = 2  让EC(1)上级上下文中的x=2
       */
      console.log(x); // => 2
    }
    func(5);
    console.log(x); // => 1 
    
    
    // ------------------------------------
    
    
    /*
     * 如果同时符合了这两个条件:
     *   1. 形参有赋值默认值
     *   2. 函数体中有声明过变量 var、function...
     * 此时的函数执行会形成两个上下文
     *   1. 私有的上下文
     *   2. 函数体所在大括号中的块级上下文 【在私有上下文中,用var、let、const等声明的变量属于块级上下文。】
     *    => 函数体中遇到一个变量,我们首先看是否为块上下文中的变量,如果是,接下来都操作块上下文中的变量,和私有没关系;如果不是,操作的是私有的或者全局的...
     */
    var x = 1;
    function func(x, y = function anonymous1() { x = 2 }) {
      // 【形参赋值】私有上下文 x = 5,y = anonymous1 【这里可以把私有上下文当做块级上下文的上级上下文】
      // 块级上下文 x = 5 【x的初始值是5】
      console.log(x); // => 5 【块级】
      var x = 3; // 块级中的 x = 3 
      y(); // x = 2 私有中的 x = 2 【因为没有用var、let等重新声明y,所以y是上级的私有上下文的y。】
      console.log(x); // => 3
    }
    func(5);
    console.log(x); // => 1 
    
    
    // ------------------------------------
    
    
    var x = 1;
    function func(x, y = function anonymous1() { x = 2 }) {
      // 私有上下文 x = 5,y = anonymous1 
      // 块级上下文 x = 5,y = anonymous1,在还没有执行到指定代码之前,存储的值和私有上下文中的值是一样的
      var x = 3;  // 块级上下文x=3
      var y = function anonymous2() { x = 4 };  // 块级上下文y = anonymous2
      y(); // anonymous2()  x = 4  块级上下文中的x = 4
      console.log(x); // => 4
    }
    func(5);
    console.log(x); // => 1 
    

  • 相关阅读:
    ALV实时刷新功能的实现
    sap中批量导入Excel表格中的数据
    从se11新建的表维护中,给维护的数据做限制处理,例如,只允许输入vp开头的数据
    abap之ranges使用
    Abap中LOOP循环时使用AT FIRST. 传数过程中出现一串 ******** ------解决办法
    Django项目-创建第一个页面
    Python3创建django项目
    Python3安装Pyyaml
    Appium命令行环境搭建及参数使用
    c# ABP 中开启新的事务
  • 原文地址:https://www.cnblogs.com/jianjie/p/13831452.html
Copyright © 2011-2022 走看看