4_js作用域、闭包、 闭包的作用、立即执行函数、闭包的防范、js运行三部曲、预编译前奏、预编译

作用域

[[scope]]:每个 javascript函数都是一个对象,对象中有些属性我们可以访问,但有些不可以,这些属性仅供javascript引擎存取,[[scope]]就是其中一个。[[scope]]指的就是我们所说的作用域其中存储了运行期上下文的集合。

作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。

运行期上下文:当函数执行时,会创建一个称为执行期上下文的内部对象。一个执行期上下文定义了一个函数执行时的环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,它所产生的执行上下文被销毁。

查找变量:从作用域链的顶端依次向下查找。

执行期上下文就是下面要讲的预编译中的AO、GO

    function a () {
      function b () {
        function c () {

        }
        c();
      }
      b();
    }
    a();
    // a defined a.[[scope]] -- > 0 : GO
    
    // a doing   a.[[scope]] -- > 0 : aAO
    //                            1 : GO
    
    // b defined b.[[scope]] -- > 0 : aAO
    //                            1 : GO
    
    // b doing   b.[[scope]] -- > 0 : bAO
    //                            1 : aAO
    //                            2 : GO
    
    // c defined c.[[scope]] -- > 0 : bAO
    //                            1 : aAO
    //                            2 : GO
    
    // c doing   c.[[scope]] -- > 0 : cAO
    //                            1 : bAO
    //                            2 : aAO
    //                            3 : GO
闭包

当内部函数被保存到外部时,将会生成闭包。闭包会导致原有作用域链不释放,造成内存泄露。

闭包的作用
  • 实现公有变量
    • eg:函数累加器
  • 可以做缓存(存储结构)
    • eg:eater
  • 可以实现封装,属性私有化。
    • eg:Person();
        function Person (name, say) {
        var says = "hehe";// 私有化的变量,也是闭包的作用
        this.name = name;
        this.say = say;
        this.changeSay = function () {// 私有变量赋值
          this.say = says;
        }
        this.changeSays = function (target) {// 修改私有变量
          says = target;
        }
        this.showSays = function () {// 显示私有变量
          console.log(says);
        }
      }
      var person = new Person('Yin', 'haha');
    

在这里插入图片描述

  • 模块化开发,防止污染全局变量
    var num = 100;
    function a () {
      var num = 100;
      function b () {
        num ++;
        console.log(num);
      }
      return b;
    }
    var demo = a();
    demo();// 101
    // 因为a()中return b;所以demo()的方法就是b(),bAO中有aAO和GO,所以b()中的num就是aAO的num
    // 所以第一次打印出来的就是101
    demo();// 102
    // 因为是function,不是原始值,而是引用值,所以第二次指向的num变量就是第一次指向的num变量
    // 所以第二次打印出来的就是102
    // 这个就是所谓的累加器
    function test() {
      var num = 100;
      function a () {
        num ++;
        console.log(num);
      }
      function b () {
        num --;
        console.log(num);
      }
      return [a,b];
    }
    var myArr = test();
    myArr[0]();// 101
    myArr[1]();// 100
立即执行函数

定义:此类函数没有声明,在一次执行过后即释放。适合做初始化工作。

    // 立即执行函数 (function () {}());W3C 建议第一种
    //              (function () {})();
    (function abc () {
      var a = 123;
      var b = 234;
      console.log(a + b);// 357
    }())
    // 带参、返回的立即执行函数
    var num = (function (a, b, c){
      console.log(a + b + c);// 6
      var d = a + b + c * 2 - 2;
      return d;
    }(1, 2, 3));
    console.log(num);// 7
    function test () {
      var arr = [];
      for(var i = 0; i < 10; i ++) {
        arr[i] = function () {// 存在数组里的是函数体,这边主要是存函数体,不是执行函数体
          document.write(i + "");
        }
      }
      return arr;
    }
    var myArr = test();// 这边执行的只是存储十次函数体
    for(var j = 0; j < 10;j++) {
      myArr[j]();// 只有在执行这个函数体的时候,才会用到i,这时候的i已经循环完成,i为10
    }
    // 最后打印出十个10
    function test () {
      var arr = [];
      for(var i = 0; i < 10; i ++) {
        (function (j) {
          arr[j] = function () {
            document.write(j + "");
          }
        }(i));
      }
      return arr;
    }
    var myArr = test();
    for(var j = 0; j < 10;j++) {
      myArr[j]();
    }
    // 最后打印0123456789
闭包的防范

闭包会导致多个执行函数共用一个公有变量,如果不是特殊需要,应尽量防止这种情况发生。

js运行三部曲
  • 语法分析
    先通篇扫描一遍有没有语法错误
  • 预编译
    函数声明整体提升
    变量 声明提升
    console.log(a);// a = undefined
    var a = 123;// 但是如果把var a = 123;这行去掉,console.log()就会报错--未找到a变量
    test();
    function  test () {
      console.log('a');
    }
    // 函数声明整体提升
    // 变量 声明提升
  • 解释执行
预编译前奏
  • 1.imply global暗示全局变量:即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有
    • eg: a = 123;
    • eg: var a = b = 123;
  • 2.一切声明的全局变量,全是 window的属性。
    • eg: var a = 123; ===> window.a = 123;

window 就是全局的域
就是说在全局的范围内,var a = 1 和 直接用 a = 1这里面的a都归为windows.a( console.log(a) == console.log(window.a) )
全局变量都是在window上

预编译
  • 四部曲:重点重点重点
  1. 创建AO对象 Activation Object (执行期上下文)
  2. 找形参和变量声明,将变量和形参名作为AO属性名,值为 undefined
  3. 将实参值和形参统一
  4. 在函数体里面找函数声明,值赋予函数体
    function fn (a) {
      console.log(a);
      var a = 123;
      console.log(a);
      function a () {}
      console.log(a);
      var b = function (n) {}
      console.log(b)
      function d () {}
    }
    fn(1);
    // 预编译发生在函数执行的前一刻

程序分析:(预编译)
第一步:创建AO,函数产生的存储空间库
AO{
a :
b :
}
第二步:把变量和形参名作为AO属性名,赋值undefined
AO{
a : undefined,
b : undefined
}
第三步:将实参值和形参统一
AO{
a : 1,
b : undefined
}
第四步:在函数体里面找函数声明,a里面的1就会被覆盖掉
AO{
a : function a () {},
b : undefined,
d : function d () {}
}

四个console.log()打印输出
第一个为 :
f a () {}
因为 a在预编译的过程中就声明了,但是 a = 123的赋值语句还没有执行,所以 AO中的a 又被赋值为123!所以第二个为:
123
因为a已经被覆盖成123,所以第三个为:
123
第四个为:
f d () {}
在这里插入图片描述
全局的预编译因为没有实参形参,所以少了第三步,其他一样
全局生成的对象不是叫AO对象了,叫GO对象 Global Object
GO {

}
GO === window

重点!!!!!!!!!!!!!!

    function test () {
      var a = b = 123;
      console.log(window.a);// a这个变量是var a,所以它只是个局部变量,打印出来是undefined
      console.log(window.b);// 而b这个变量,从右往左时,他不是var出来的,直接是 b = 123; 所以它是个全局变量!!!打印出来是123
    }
    test();

先找AO,再找GO!如果AO对象中找不到这个值,那么会往GO中找
举例:

    global = 100;
    function  fn() {
      console.log(global);// AO中有global,但是第一句执行是还是undefined,所以AO中有了,就不会再去找GO中的
      global = 200;
      console.log(global);// global被200覆盖了,所以执行后为200
      var global = 300;
    }
    fn();
    var global;
    function test () {
      console.log(b);// AO中有个b :undefined,所以b为undefined
      if(a) {
        var b = 100;// a = undefined,所以跳过if
      }
      c = 234;// AO里面没C,所以c给GO
      //此时的GO对象添加C
      // GO {
      //  a : undefined,
      //  c : 234
      //}
      console.log(c);// AO中没有var C,所以去GO中找,可以找到C为234
    }
    var a;
    //因为要调用test了,所以提前生成一个AO对象预编译一下
    //注意的是:不管里面if是否会执行,只要有var声明,都给我放到AO中
    //AO {
    //  b : undefined
    //}
    test();
    a = 10;
    console.log(c);// 经过test();后,GO中有C了,所以C为234

小知识: 在现在的谷歌浏览器中,if()语句内部不能再定义function了 (可以试着在if中创建一个函数,打印这个变量看一下,会发现是undefined)

发布了56 篇原创文章 · 获赞 20 · 访问量 7400

猜你喜欢

转载自blog.csdn.net/qq_36826618/article/details/102566549