13.变量提升练习题

419 阅读8分钟

1 .

  console.log(a, b, c)
  var a = 12,
      b = 13,
      c = 14;
  function fn(a) {
    console.log(a, b, c) // 101314
    a = 100 // 私有变量a= 100
    c = 200 // 全局c = 200
    console.log(a, b, c) // 10013200
  }
  b = fn(10) // 函数执行没有返回结果(EWTURN),默认为undefined
  console.log(a, b, c) // 12 undefined 200

解析:

/*
  * EC(G)
  *     1.变量提升:
        VO(G)
          a:12
          b:13 undefined
          c:14 / 200
          fn=>堆内存0x000000([[scope]]:EC(G))
        2.代码执行:
          (1):console.log(a,b,c)=> 只声明,没定义,所以输出undefined * 3
          (2):a = 12,b=13,c=14
          (3)b = fn(10)=>0x000000(10)
  */
  console.log(a, b, c)
  var a = 12,
      b = 13,
      c = 14;
  function fn(a) {
    /**
    * EC(FN)
    *       AO(FN):
                  a = 10 / 100
            作用域链:<EC(FN),EC(G)>
            形参赋值:a = 10
            变量提升:--
    */
    // a是自己私有的,值是10;b和c不是私有的,沿着作用域链向上级找,知道到全局存在,值范围内比为13,14
    console.log(a, b, c) // 10,13,14
    a = 100 // 私有变量a= 100
    c = 200 // 全局c = 200
    console.log(a, b, c) // 100, 13,200
  }
  b = fn(10) // 函数执行没有返回结果(EWTURN),默认为undefined
  console.log(a, b, c) // 12 undefined 200
var i = 10
  function A() {
    var i = 10
    function x() {
      console.log(i)
    }
    return x
  }
  var y = A()
  y()
  function B() {
    var i = 20
    y()
  }
  B()

var a = 1
  var obj = {
    name: 'tom'
  }
  function fn() {
    var a2 = a
    obj2 = obj
    a2 = a
    obj2.name = 'jack'
  }
  fn()
  console.log(a)
  console.log(obj)
/**
   * EC(G)
   *    VO(G)
   *          a
   *          obj
   *          fn  => 0x000000
   *          代码执行过程中,变量赋值:
   *           a = 1
   *           obj = 0x000001
   * 
   *    变量提升:var a;var obj; function fn(){...}
   *    代码执行:
   *            var = 1
   *            var obj = {name: "tom"}
   *            fn()
  */
  var a = 1
  var obj = {
    name: 'tom'
  }
  function fn() {
    /**
     * EC(FN)
     *    AO(FN)
     *          a2 = 全局a的值:1
     *    代码执行
     *           var a2 = a
     *            obj2 = obj // obj2既不是私有的,也不是全局的,此处相当于window.obj2 = 0x000001(全局的obj)
     *            a2 = a // window.a2 = a = 1
     *            obj2.name = "jack" // 把window.obj2中的name修改(obj2和obj指向的同一个堆内存0x000001)
     * 
    */
    var a2 = a
    obj2 = obj
    a2 = a
    obj2.name = 'jack'
  }
  fn()
  console.log(a) // 全局a = 1
  console.log(obj) // {name: 'jack'}
var a = 1
  function fn(a) {
    console.log(a)
    var a = 2
    function a() {}
  }
  fn(a)
/**
 * EC(G)
 *    VO(G)
 *      a = 1
 *      fn = 0x000000 [[scope]]:EC(G)
 * 
 *     变量提升:var a; function fn(){...}
 *     代码执行:
 *         var a = 1
 *         fn(a)
*/
  var a = 1
  function fn(a) {
    /**
     * EC(FN)
     *    AO(FN)
     *        a = 1
     *            0x000001
     *            2
     *    作用域链:<EC(FN),EC(G)>
     *    形参赋值:a=1
     *    变量赋值:
     *          a = 1
     *          function a() {};不需要声明,但是需要重新赋值。 创建一个函数堆内存:0x000000. 将a指向改地址
     *    代码执行:
     *          console.log(a) // 函数0x000000 => function a() {}
     * 
    */
    console.log(a)
    var a = 2 // 私有变量a重新赋值为2
    function a() {} // 不需要处理了,变量提升阶段都处理过了
    console.log(a) // 2
  }
  fn(a)
  // console.log(a) // 全局的a = 1

(1)

console.log(a) // undefined
var a = 12
function fn() {
  console.log(a) // undefined
  var a = 13 // 私有变量a 赋值为13
}
fn()
console.log(a) //12
/**
 * EC(G)
 *     VO(G)
 *          a
 *            代码执行后,a = 12
 *          fn
 *      变量提升:
 *          var a; function fn(){...} => 0x000000
 *       代码执行:
 *          console.log(a) => undefined
 *          var a = 12
 *          function fn(){} // 变量提升阶段已经处理过,不在处理
 *          fn() // fn执行,会形成全新的上下文EC(FN)
*/
console.log(a) // undefined
var a = 12
function fn() {
  /**
   * EC(FN)
   *      AO(FN)
   *            a
   *              代码执行后:a = 13
   *      代码执行:
  */
  console.log(a) // 此时私有变量a = undefined
  var a = 13 // 私有变量a 赋值为13
}
fn()
console.log(a) // 全局变量a的值还是12

(2)

console.log(a)
var a = 12
function fn() {
  console.log(a)
  a = 13
}
fn()
console.log(a)
/**
 *  和(1)几乎一样,差别就在与fn执行的时候
*/
console.log(a)
var a = 12
function fn() {
  /**
   * fn执行,形成新的私有上下文,这里没有变量提升,没有声明变量
  */
  console.log(a) // 所以,输出的还是全局a的值,12
  a = 13 // 全局变量a的值改为13
}
fn()
console.log(a) // 输出全局变量a => 13

(3).

/**
 * EC(G)
 *  fn => 0x000000 [[scope]]:EC(G)
 *  变量提升:function fn() {...}
*/
console.log(a) // 首先看是否为全局变量,不是的话再看是否为window的一个属性,
//如果不是,则报错  mst练习.html:930 Uncaught ReferenceError: a is not defined(这行代码一旦报错,下面的代码都不处理了)
a = 12
function fn() {
  console.log(a)
  a = 13
}
fn()
console.log(a)
var foo = 'hello';
(function (foo) {
    console.log(foo); // hello
    var foo = foo || 'world';
    console.log(foo); // hello
})(foo);
console.log(foo); // hello
/**
 * EC(G)
 *      VO(G)
 *          foo
 *            foo = 'hello'
 *      变量提升:var foo
 *      代码执行:var foo = 'hello'
*/
var foo = 'hello';
(function (foo) {
    // 自执行函数,执行时形成全新的私有上下文
    /**
     * EC(ANY)
     *    AO(ANY)
     *    作用域链:<EC(ANY),EC(G)>
     *    形参赋值:foo = 'hello'
     *    变量提升:var foo(已经声明过foo,不再声明)
     *    代码执行:
    */
    console.log(foo); // 私有foo => 'hello'
    // A || B A的值是真,返回A的值,否则返回B的值
    // A && B A的值是真,返回B的值,否则返回A的值
    // 同时出现,&& 优先级高于 ||
    var foo = foo || 'world'; // foo = 'hello'
    console.log(foo); // 'hello'
})(foo);
console.log(foo); // 全局foo => 'hello'

知识点:
A || B A的值是真,返回A的值,否则返回B的值
A && B A的值是真,返回B的值,否则返回A的值
同时出现,&& 优先级高于 ||

(1)

{
  function foo() {}
  foo = 1
}
console.log(foo) // function foo() {}

(2).

{
  function foo() {}
  foo = 1
  function foo() {}
}
console.log(foo) // 1
/**
 * EC(G)
 *    VO(G)
 *      foo  undefined
 *           0x000001
*            1
 *      
 *    变量提升:function xxx() {} 不出现在{}中是声明+定义
 *        出现在{}中(对象/函数除外)只声明不定义
 *    代码执行:{}中出现let/const/function会形成私有块级上下文
*/
{
  /**
   * 私有块级上下文EC(BLCOK)
   * EC(BLOCK)
   *    AO(BLOCK)
   *        foo => 0x000000
   *               0x000001
   *               1
   * 
   *    作用域链:<EC(BLOCK),EC(G)>
  *      变量提升:function foo() {} (声明+定义)
  *               第二次:function foo() {}
  *               (foo已经声明过,所以这次只定义,foo的值以第二次定义的为准:0x000001)
  */
  function foo() {} // 因为全局声明过foo,块级私有上下文也声明过foo,所以会把这行代码之前对foo的操作映射给全局一份
  foo = 1 // 私有foo值变为1
  function foo() {} // 又遇到这行代码,还是会把这行代码之前的操作给全局一份,所以全局foo的值是1
}
console.log(foo) // 1

(3).

{
  function foo() {}
  foo = 1
  function foo() {}
  foo = 2 // 私有foo变为2
  console.log(foo) // 2
}
console.log(foo) // 全局foo变为1
  1. (1)
var x = 1
function func(x,y=function anonymous1(){x=2}) {
  x = 3
  y()
  console.log(x)
}
func(5)
console.log(x)

(2).

var x = 1
function func(x,y=function anonymous1(){x=2}) {
  var x = 3
  y()
  console.log(x)
}
func(5)
console.log(x)

总结:
函数执行如果满足以下两个条件

  1. 形参有赋值默认值(不管有没有传实参,不管默认值是什么类型)
  2. 函数中有变量声明(必须是基于let/const/var, 注意的是let/const声明的变量不能和形参的变量名一致)

满足以上两个条件会形成一个新的块级私有上下文EC(BLOCK)

  1. 私有快及上下文中要执行的代码就是 函数{}中的代码
  2. 函数{}中的代码如果有声明的变量(带var/let/const)才是块级私有上下文的私有变量,和函数私有上下文没有关系了
  3. EC(BLOCK)的上级上下文就是函数私有上下文EC(FUNC)
  4. 函数私有上下文EC(FUNC)形参赋值结束后,会把赋值结果映射给EC(BLOCK)中相同变名字的变量

注意:形成EC(BLCOK)后,要执行的是EC(BLOCK),私有函数上下文EC(FUNC)不再执行(拿此例来说,到变量提升就结束了)

(3).

var x = 1
function func(x,y=function anonymous1(){x=2}) {
  var x = 3
  var y = function anonymous2(){
    x=4
  }
  y()
  console.log(x)  // 4
}
func(5)
console.log(x) // 全局x:1
/**
 * EC(G)
 *     VO(G)
 *          x  undefined
 *             1
 *          func => 0x000000 [[scope]]:EC(G)
 *     变量提升:var x; function func(...){...}
 *     代码执行:var x = 1;function func(x,y...){...}(变量提升时声明+定义,此时不再处理);func(5)
*/
var x = 1
function func(x,y=function anonymous1(){x=2}) {
  /**
   * func(5)执行,形成函数私有上下文EC(FUNC)
   * 
   * EC(FUNC)
   *    AO(FUNC)
   *        x   5
   *        y   0x000001  anonymous1  [[scope]]:EC(FUNC)
   *    作用域链:<EC(FUNC),EC(G)>
   *    形参赋值:x = 5
   *             y = function anonymous1(){...}
   *    变量提升:var x;(func这个函数,形参有默认值,且函数里有var声明变量,所以会形成一个全新的块级私有上下文EC(BLOCK))
  */
 /**
  * EC(BLOCK)
  *     A0(BLOCK)
  *         x   5(初始时是5,EC(FUNC)中会把形参赋值结束后的值映射给EC(BLOCK)中相同的变量名)
  *             3
  *             4(EC(Y)执行时将值改为4)
  *         y   anonymous1(初始值是anonymous1,理由同上)
  *             anonymous2  0x0000001  [[scope]]:EC(FUNC)
  *     作用域链:<EC(BLOCK),EC(FUNC)>
  *     形参赋值:--
  *     变量提升:var x;var y = function anonymous2....
  *     代码执行:var x = 3
 */
  var x = 3 // 私有变量x改为3
  var y = function anonymous2(){
    /**
     * EC(Y)
     *    作用域链:<EC(Y), EC(BLOCK)>
     *    形参赋值:--
     *    变量提升:--
     *    代码执行:
    */
    x=4 // 不存在私有变量x,沿着作用域链想上找,找到上级上下文EC(BLOCK)
  }
  y() // y执行,形成全新的私有上下文。EC(Y)
  console.log(x)  // 4
}
func(5)
console.log(x) // 全局x:1