JS 预编译 作用域链 简单闭包 以及 立即执行函数

JS 预编译:
JS 在执行的时候分为以下三步:
1.语法分析
2.预编译
3.解释执行

1.预编译的特点

特点:1.函数声明整体提升,而对于变量而言,只有声明提升。
例如:

<script>
  console.log(a);         //undefined
   console.log(b);        // function b() {.....}
   var a = 1;
   function b () {
       var c = 0;
   } 
   </script>


2.任何未经声明而直接赋值的变量为全局变量:当声明一个全局变量的时候,实际上是定义了全局对象window的一个属性.

<script>
function test() {
    var a = b =100;
}
test();
console.log(window.b);   //100
    </script>

2.预编译的过程
(预编译发生在函数执行的前一刻,分为以下四个过程)
1.创建AO对象(active object)(执行期上下文)。
2.寻找实参和变量声明,将变量和形参作为AO的属性名,赋值为undefined。
3.将实参和形参统一。
4.在函数体内找函数声明,并将值赋予函数体。

举一个例子:

 <script>
    function fn(a){
        console.log(a); //function a () {}
        var a = 123;
        console.log(a); //123
        function a () {

        };
        console.log(a); //123
        var b = function () {

        };
        console.log(b); // function () {}
        function d(){

        };
    }
    fn(1);
    </script>

预编译 过程如下:

AO{
    a:undefined → 1 → function a() {} 
    b:undefined → function () {}
    d:function d() {}
}

预编译完成后各变量如上,而在函数执行时,预编译发生的过程便不再执行:

再举一个例子:
当有全局变量存在时:预编译会先创建一个GO对象,也就是window对象

<script>
console.log(test);  // function test (test) {....}
    function test(test) {
        console.log(test); //function test () {}
        var test = 234;
        console.log(test);  //234
        function test() {

        }
    }
    test(1);
    var test = 123;
    console.log(test) //123
    </script>

预编译 过程如下:

GO{
    test:undefined
}
AO{
    test:undefined → 1 → function test(){}
}

JS作用域问题:

[[scope]] 作用域链

function a() {
}
var glob = 100;
a();
a.[[scope]]  定义的时候产生一个 GO 放在自己的作用域链的顶端
当a执行的时候,会产生一个自己的AO 放在自己的作用域链的顶端,而GO会放在再后面,类似于数组 
a.[[scope]] -- 0  - AO{}
a.[[scope]] -- 1  - GO{}

每个函数在执行完之后会销毁自己的执行期上下文

举个例子:

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

            }
            c();
        }
        b();
    }
    a();

作用域链如下:

a 定义 a.[[scope]] --> 0 :GO{}
a 执行 a.[[scope]] --> 0 : AO{}
                      1 : GO{}
           
b 定义 b.[[scope]] --> 0:aAO{}      //b在定义的时候继承a所产生的作用域链,
                       1:GO{}
b 执行 b.[[scope]] --> 0:bAO{}
                       1:aAO{}
                       2:GO{}

JS闭包问题

1.什么是闭包?
当内部函数被保存到外部来使用时,将会生成闭包。
先来一个例子:

<script>
function a () {
var num = 100;
   function b(){
   num ++;
     console.log(num);
     }
     return b;
    }
    var demo = a() 
    demo()   //101;
    demo();  //102
    
a 定义 执行 生成执行期上下文   a.[[scope]]   -->  0:AO
                                                1:GO
b 定义 执行  生成执行期上下文 b.[[scope]]    --> 0;bAO
                                               1:aAO
                                               2:GO     
 函数执行两次结果不一样是因为a执行完之后销毁了自己的执行期上下文,但是b在定义时继承了a的执行期上下文,依旧可以对a生成的AO进行操作。

2.闭包的特点
缺点: 闭包会导致原有的作用域链不释放,造成内存泄露(也可以理解为内存占有)

好处:1.实现共有变量 (eg:函数累加器)

function add(){
var count = 0;
function demo(){
count ++;
console.log(count);
}
return demo;
}
var myadd()  =  add ();
myadd();    //1
myadd();    //2
myadd();    //3

//可以测试一个函数执行了几次。

2.可以做缓存:(存储结构)

function eater(){
var food = “”;
var obj = {
       eat : function (){
       console.log(“i am eating” + food);
       food = "";
       }
       push : function(myfood){
       food = myfood;
       }
       }
       return obj;
       }
       var eater1 = eater();
       eater1("apple");      //i am eating apple.
       eater1("banana"):  //i am eating banana.
       //一个对象封装了两个并列函数,相当于一个1对2的闭包。 闭包在这里充当了一个缓存的效果

3.实现封装,属性私有化。
4.模块化开发,防止污染变量。
(3,4学习的不够深,下篇博客补上)
3.闭包一个比较优秀的问题:

 function test() {
        var arr = [];
        for (var i = 0;i < 10;  i++)
        {
            arr[i] = function () {
                console.log(i + " ");
            }
        }
        return arr;
       }
        var myArr = test();
        for(var j =0; j < 10; j++)
        {
            myArr[j]();
        }
        //上述问题会打印处0-9吗?
          实际结果为 10 个 10;
          相当于1对10的闭包,都相应的改变了继承来的AO,最后返回最终的结果。

如果说我就是想打印出0-9呢?
下一个知识点来了:

立即执行函数

举个例子:

{function a() {
  .........此处省略十万行代码
  }()}
  立即执行函数,只要执行一次后便销毁,不会继续占有空间。
  格式: (function () {} ())
  
 (function add() {
        var a = 1;
        var b = 2;
        console.log(a+b);  //3
    }())
    add();  //undefined

    //传参
    (function add(a,b) {
        console.log(a+b);  //3
    }(1,2))
    add();  //undefined

立即执行函数的底层原理:对于立即执行函数而言,只有表达式可以被执行:

 var a = function() {
 console.log ("123");  //123
} 
 而 function(){
 console.log(“123”;
 }();   //不能执行

但是给其加上数学运算符就可以变为一个表达式从而顺利执行:
例如 + - () 等等。

//对于如上例子:
1.    + function a() {
      console.log("456");
      }();   //456
2.     - function a() {
      console.log("789");
      }();     //789
3.      (function a() {
      console.log("xiaolaji");
        }());     //立即执行函数 

问题解决:
而对于刚才打印10个10问题的闭包而言,即生成了1对10的闭包,10个函数公用了一个执行期上下文,导致作用域没有被释放,最终返回10个10的问题,利用立即执行函数可以很好的解决。
代码如下:

function test() {
        var arr = [];
        for (var i = 0;i < 10;  i++)
        {
            (function (j){
                arr[j] = function (){
                    console.log(j);
                }   
            }(i))    //使用快速执行函数,相当于把问题变成了10对10的闭包。 最终打印 0 -9 
        }
        return arr;
       }
        var myArr = test();
        for(var j =0; j < 10; j++)
        {
            myArr[j]();
        }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值