180330 預編譯

JS編譯過程

  1. 語法分析:JS引擎通篇掃描,有低級錯誤直接報錯不執行。
  2. 預編譯
  3. 解釋執行

執行期上下文

  • 當函數執行時,會創建一個稱爲執行期上下文的內部對象。一個執行期上下文定義了一個函數執行時的環境,函數每次執行時對應的執行上下文都是獨一無二的,所以多次調用一個函數會導致創建多個執行上下文,當函數執行完畢,執行上下文被銷燬。

預編譯

window是全局對象,在全局範圍聲明一個變量,則該變量成爲window的一個屬性。
imply global暗示全局變量:任何變量如果未經聲明就賦值,都會變成window全局對象的屬性。

var age = 18;
function (){
    var haha = age = 20;
}
show();    
console.log(a)  // age  ->  20

函數預編譯過程

  1. 創建執行期上下文 AO = { };
  2. 找出全部變量(變量聲明!var)及形參,並賦值undefined。
    AO –> {a:undefined,b:undefined,c:undefined};
  3. 形參實參相統一
    AO –>{a:3,b:4,c:undefined};
  4. 尋找函數聲明(函數表達式不好使!),值賦給函數體
    AO –>{a:3,b:4,c:function c () {} };
function show(a,b){
    console.log(a);  //3
    console.log(b);  //4
    console.log(c);  //function c(){}
    var c = 20;
    function c (){};
    c = 30;
    a = 20;
}
show(3,4);

全局預編譯過程:

  1. 創建全局執行期上下文GO = { }
  2. GO -> { this:window,document:,a:,b: }
  3. GO -> {this:window,document:,a:undefined,b:function(){} }

作用域[[scope]]:

只有JS引擎有權限訪問。
存儲了執行期上下文的集合。
一個函數被定義時,它的[[scope]]指向的對象(scope chain)的第0位存儲的就是該函數所在的執行環境。

作用域鏈(scope chain):

[[scope]]中所存儲的執行期上下文對象的集合,這個集合呈鏈式鏈接,我們把這種鏈式鏈接叫做作用域鏈。

a{[[scope]]:{}}
//   屬性(作用域scope)    值是一個對象(作用域鏈scope chain)

1.語法分析後創建全局執行期上下文GO -> {...}
2.此時定義 a.[[scope]] -> {0:GO}
3.函數執行時創建函數執行期上下文AO -> {...}
4.此時執行 a.[[scope]] -> {0:AO,GO}


這裏寫圖片描述



var global = 300;

function test() {
    console.log(global); // AO: undefined
    var global = 400;
    console.log(global); // 400
}
test();


var global = 100;

function test() {
    console.log(global); // AO :沒有global  GO:100
    global = 200; //不是變量聲明  
    console.log(global); // 200
}
test();
console.log(global); // 200
function a(){
    function b(){  //  b.[[scope]] -> {0:AO2,1:AO1,2:GO}
        var b = 234;
    }
    var a = 123;
    b();
}
var glob = 100;
a();

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

這裏寫圖片描述


閉包

  • 當內部函數(內部引用值)被保存到外部時,將會生成閉包。
  • 閉包會導致原有作用域鏈不釋放,造成內存泄露。
function a() {
    function b() {
        var bbb = 234;
        document.write(aaa);
    }
    var aaa = 123;
    return b;
}

var glob = 100;
var demo = a();
demo();  // 函數b被保存到外部,因此產生閉包
//此時延用之前的作用域鏈,函數執行期上下文不會被刪除

// 用處,原本輸出aaa的值是undefined,但是這樣之後是111

// b.[[scope]] -> {0:aAO,1:GO}
// b/demo.[[scope]] ->{0:bAO,1:aAO,2:GO}
// demo.[[scope]] -> {0:aAO,1:GO}

這裏寫圖片描述

使用閉包

  • 緩存
function add() {
    var num = 0;

    function get() {
        num++;
        console.log(num);
    }
    return get; 
} 

//get函數被保存到外部,
//它的作用域鏈scope chain -> {0:getAO,1:addAO,2:GO}

var outerGet = add(); 
outerGet(); // 1   
outerGet(); // 2
outerGet(); // 3  
//   一直操作的是作用域鏈當中的num值,即 接着上一次的num運算結果
//    每次執行,使用的都是同一個AO, AO沒有被銷燬

//---------------------------------------------------------
function add() {
    var num = 0;
    num++;
    console.log(num);
}
add(); // 每次執行會創建一個新的AO
add();
add();

// ----------------------------------------------------------

function add(){
    var num1 = 1;
    var num2 = 1;
    function get1(){
        num1++;
        console.log(num1);
    }
    function get2(){
        num2++;
        console.log(num2);
    }
    return [get1,get2];   // 注意返回多個值時的技巧
}
var outerGetALL = add();
outerGetALL[0]();
outerGetALL[0]();
outerGetALL[1]();
outerGetALL[1]();
  • 私有化變量

立即執行函數

  • 此類函數沒有聲明,在一次執行過後即釋放。
  • 可以做初始化工作。
(function () {}());
(function () {})();
//函數聲明被()包裹,被強制轉換成函數表達式
//函數([圓括號包裹函數聲明形式],[函數表達式形式])定義時就用()執行,
//如果不產生閉包,函數就會被銷燬。

+function() show(){}() 
// + 等其它運算符 可以將函數聲明轉換成函數表達式
fucntion show (){

}();
//不能執行,只有函數表達式可以被()直接執行
--------------------------------------------------------------
var test = function(){

}();
//可以執行,因爲是函數表達式
--------------------------------------------------------------
var test = function show (){

}();
//可以執行,因爲是函數表達式
--------------------------------------------------------------
var test = function(){
}
//執行方式有兩種:
變量表達式執行:test();
非變量表達式執行(定義時直接執行):[var test(){}] ();
//當函數表達式選擇執行方式----(執行非變量的函數表達式)方式時
//表達式會在執行後銷燬
--------------------------------------------------------------
var test2 = function show (){
    console.log(1);
    return show;
}();
test2();// 可以執行,形成閉包纔沒被銷燬

應用

var a = [];
for (var i = 0; i < 10; i++) {
    a[i] = function() {
        console.log(i); // i在GO中
    }
}
a[0](); // 10
a[1](); // 10


var a = []; //定義了一個數組,每次循環都會向數組各索引存儲一個輸出函數
for (var i = 0; i < 10; i++) {
    (function(i) { // [[scope]] -> {0:nAO -> {i:0} 1:GO -> {}} 保存到外部,形成閉包,存檔
        a[i] = function() { // a[i].[[scope]] -> {0:a[i]AO,1:nAO,2:GO}  a[i]AO每次執行完銷燬
                console.log(i);
            }
            // return  因爲將函數存到了全局的數組當中,相當於將函數return到外部
    })(i);
}

a[1](); // 產生閉包,立即執行函數不會被銷燬,相當於普通函數
a[9](); // 每一次循環,將i=1~9傳入函數

對象知識點補充

對象屬性的調用:o.attributes

function test (){

}
test();

// 相當於

function test (){
    this:window
}
window.test();

//誰(對象)通過.方式調用了函數,誰就是this

如果一個函數是構造函數,則會隠式的創建this對象,並返回this

function CarFactory (){
    // var this = {
    //  wheel:4,
    //  door:2,
    //  distance:0,
    //  run:function(){}
    //  }
    this.wheel = 4;
    this.door = 2;
    this.distance = 0;
    this.run = function (){
        this.run = function(){
            this.distance += 100;
        }
    }
    // return this
}
發佈了44 篇原創文章 · 獲贊 2 · 訪問量 5026
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章