《深刻理解javascript原型和閉包系列》 知識點整理

深刻理解javascript原型和閉包系列 對原型和閉包等相關知識的講解,由淺入深,通俗易懂,每一個字都值得細細研究。javascript

1、一切都是對象html

1. typeof操做符輸出6種類型:string boolean number undefined function object
2. 數組、null object都是object類型
3. 對象:若干屬性的集合。js中,數組是對象,函數是對象,對象是對象
4. 函數和對象的關係:
    1. 函數生成對象:經過new構造函數生成實例對象
    2. 函數是一種對象:
        1. 全部的函數都有一個prototype(原型)屬性,是一個對象,這個對象中默認的存在一個constructor屬性指向函數自己。
        2. 每一個對象都有一個隱藏的屬性:`__proto__`,指向建立這個對象的構造函數的prototype
        3. 函數也是對象,固然也有`__proto__`屬性,指向Function.prototype(大寫Function的原型)
        4. 總結:
            1. 自定義構造函數(也是對象)的`__proto__`指向Function.prototype;
            2. Object.`__proto__`也指向Function.protorype;
            3. Function.`__proto__`指向Function.prototype(Function這個對象,固然是被Function函數自身建立的,因此指向自身的prototype)
        5. Function.prototype這個對象,是被Object建立的對象,它的隱藏`__proto__`屬性,指向Object.prototype,最後,Object.prototype指向Null!前端

 

 2、 繼承

1. 訪問一個對象的屬性時,先在基本屬性中查找,若是沒有找到,沿着`__proto__`這個屬性(指向構造函數的prototype)往上找
2. 實際應用中如何區分一個屬性是自有的仍是在原型中的呢?——obj.hasOwnProperty(proName)

3.每一個函數都有call、apply方法,length、arguments等屬性,確定是繼承的,都是繼承自Function.prototype

4. 原型的靈活性: 

        在Java和C#中,你能夠簡單的理解class是一個模子,對象就是被這個模子壓出來的一批一批月餅。
        壓個啥樣,就得是個啥樣,不能隨便動,動一動就壞了。
        而在javascript中,就沒有模子了,月餅被換成了麪糰,你能夠捏成本身想要的樣子。

3、執行上下文

1. **一段代碼**在正真一行一行執行前,瀏覽器事先作了準備工做:
    1. 對變量的**聲明**,但不賦值,賦值是在執行到賦值語句時進行的
    2. 直接對this進行**賦值**
    3. 函數:
        1. 函數聲明:function foo(){}        //把函數名賦值了!
        2. 函數表達式: var foo = function(){}  //因爲是var的變量,與第一種狀況同樣,只聲明,不賦值
    4. 總結:
        1. var出來的變量,函數表達式,只聲明,不賦值,默認undefined
        2. this:賦值
        3. 函數聲明:賦值

2. 執行**代碼段**前的這個準備工做,就叫作執行上下文/上下文環境/執行環境
3. 這個**代碼段**其實又分爲三種狀況:
    1. 全局代碼
    2. eval()
    3. 函數【重點↓】

4. 函數代碼段中的執行上下文有這些數據:
    1. var 出來的變量只聲明,沒賦值
    2. this和函數聲明已經賦值
    3. 特殊的地方:
        1. arguments變量和函數的參數都已經被賦值java

function foo(x){
                    console.log(arguments);
                    console.log(x);
                }
                foo(10);
                //在進入函數體內部開始執行以前,函數體內部的arguments和參數x已經被賦值了

   2. **自由變量的取值做用域:賦值**數組

 

5. 由此可知,函數每調用一次,都會產生一個新的執行上下文環境,由於不一樣的調用可能會有不一樣的參數傳入

6. 函數在定義的時候(不是調用的時候),就已經肯定了函數體內部**自由變量**的**做用域**瀏覽器

        var a =10;
        function fn(){
            console.log(a);
            //本函數在建立的時候就決定了這個a要取值的做用域:全局做用域
        }

        function bar(f){
            var a = 20;
            f();
        }

        bar(fn);    //10

7. 關於this的一點小問題閉包

        var obj  = {
          x:10,
          fn:function(){
               function f(){
                 console.log(this);
                 console.log(this.x);
               }
               f();
          }
        };
        obj.fn();    //this指window
        /*
            obj.fn保存了指向
                    function(){
                       function f(){
                         console.log(this);
                         console.log(this.x);
                       }
                       f();
                   }
            的指針,加一個():   =>obj.fn()表示開始調用:
                function f(){
                         console.log(this);
                         console.log(this.x);
                       }
                f();
            這個f函數在window環境調用執行,因此this指向window
        */

8. 上下文環境的執行順序
    1. 執行全局代碼時,會產生一個全局上下文環境。
    2. 每次調用函數時,會產生函數內部的執行上下文環境。
    3. 當函數調用完成時,這個上下文環境以及其中的數據都會被消除,再從新回到全局上下文環境。
    4. 處於活動狀態的執行上下文環境只有一個。app

            var a=10;
            function bar(x){
                var b = 5;
                fn(x+b);
            };
            function fn(y){
                var c = 5;
                console.log(y+c);
            };

            bar(10);
            ----------------解析----------------------
            /*
            1. 產生全局上下文執行環境,該聲明的聲明,該賦值的賦值
                1. a = 10 ; fn = function();  bar  = function()
            
            2. 到調用bar(10)函數時,進入bar函數內部,產生函數內部的執行上下文環境,該聲明的聲明,該賦值的賦值
                1. b = 5; x = 10; arguments = [10]
            
            3. 接着調用fn函數,進入fn函數內部,產生函數內部的執行上下文環境,該聲明的聲明,該賦值的賦值
                2. c = 5 ; y = 15;
        
            4. fn函數執行完畢,它對應的上下文執行環境都會被銷燬,裏面保存的數據都沒有了
            5. bar函數一樣如此,它對應的上下文執行環境都會被銷燬,數據清除
            */

 

 4、自由變量和做用域

1. 做用域:javascript除了全局做用域以外,只有函數能夠產生做用域。因此,在聲明變量時,全局代碼要在代碼前端聲明,函數中要在函數體一開始就聲明好。除了這兩個地方,其餘地方都不要出現變量聲明。並且建議用「單var」形式。
2. 做用域就是一個地盤,最大的用處就是隔離變量,使不一樣做用域下相同的變量名不會產生衝突
3. 每一個函數都會建立本身的做用域,做用域在函數**定義**的時候就已經肯定了,而不是在調用的時候肯定函數

        var a = 10,b=20;
        
        function fn(x){
            var a =100, c = 300;
            
            function bar(x){
                var a = 1000,d = 4000;
                console.log(x);
                console.log(a);
                console.log(b);
                console.log(c);
                console.log(d);
            }

            bar(100);
            bar(200);
        }
        
        fn(10);
        ----------------------解析:--------------------------
        /*
        
        1. 產生全局上下文執行環境,該聲明的聲明,該賦值的賦值
            1. a = 10;  b=20; fn = function();  

        2. 到調用fn(10)函數時,進入fn函數內部,產生函數內部的執行上下文環境,該聲明的聲明,該賦值的賦值
            1. a = 100 ; c = 300 ; x = 10 ; arguments = [10]  ; bar = function()
        
        3. 到調用bar(100)函數時,進入bar函數內部,產生函數內部的執行上下文環境,該聲明的聲明,該賦值的賦值
            1. a = 1000 ; d = 4000 ; x = 100;
        4. 到調用bar(200)函數時,進入bar函數內部,產生函數內部的執行上下文環境,該聲明的聲明,該賦值的賦值
            1. a = 1000 ; d = 4000 ; x = 200;
            2. 與bar(100)函數相比較,同一個做用域,不一樣的調用,產生不一樣的執行上下文環境
        
        5. 總結:
            1. 做用域只是一個「地盤」,一個抽象的概念,其中沒有變量。
            2. 要經過做用域對應的執行上下文環境來獲取變量的值。
            3. 同一個做用域下,不一樣的調用會產生不一樣的執行上下文環境,繼而產生不一樣的變量的值。
            4. 做用域中變量的值是在執行過程當中產生的肯定的,而做用域倒是在函數建立時就肯定了。
            5. 因此,若是要查找一個做用域下某個變量的值,就須要找到這個做用域對應的執行上下文環境,再在其中尋找變量的值。
    
        */

4. 自由變量:在函數內部中使用的變量x,卻沒有在函數內部聲明,也就是在其餘做用域中聲明的,對這個函數來講,x就是自由變量this

        var x= 10;
        
        function fn(){
            var b = 20;
            console.log(x+b);
            //x就是自由變量
        }
        /*
            1. b從本做用域中取,x就要到另外的做用域中取,
            2. 是到fn的父級做用域取嗎?不是的!
        */
        --------------------分割線---------------------------

        var x=  10;
        
        function fn(){
            console.log(x);
        }

        function show(f){
            var x = 20;

            function foo(){
                f();    //10
            }
            foo();
        }

        show(fn);
        //所以:要到建立這個函數的那個做用域中取值——是「建立」,而不是「調用」,切記切記。
        //建立fn函數的做用域是window,因此,x = 10;
        /*
            總結一下:
                1. 先在當前做用域查找變量x,沒有找到則繼續;

                2. 若是當前做用域是全局做用域,則證實x未定義,結束;不然繼續;

                3. 不是全局做用域,那就是函數做用域,將**建立該函數的做用域**做爲當前做用域;
                
                4. 跳到第一步循環……

        */

5. 必須屢次重複強調:要去**建立**這個函數的做用域取值,而不是「父做用域」。

 

5、最後的閉包:

1. 通常狀況下,當一個函數被調用完成以後,其執行上下文環境將被銷燬,其中的變量也會被同時銷燬。
2. 但有些狀況下,函數調用完,其執行上下文環境沒有銷燬,其中的變量也還在內存中,這就是閉包的核心內容。

        function fn(){
            var max = 10 ; 
            
            return function bar(x){
                if(x>max){console.log(x);}
            };
        }

        var f1 = fn(),max = 100 ;

        f1(15);

        ----------------解析:---------------------
        /*
        1. 執行全部代碼前,先生成全局上下文執行環境,該聲明的聲明,該賦值的賦值
            1. fn = function() ; f1 = undefined ; max = 100;

        2. 執行fn()函數,進入fn函數的上下文執行環境,聲明、賦值
            1. max = 10 ; bar = function() ;
            2. 執行fn函數裏的語句,把bar函數return出去,賦值給fn

        3. 【重點】此時,fn函數執行完畢了,按理來講,fn的上下文執行環境應該被銷燬,變量從內存清除,但在這裏不能這麼作!!
            1. 由於執行fn函數的過程當中返回了一個bar函數,這個bar函數的特別之處在於, 它建立了一個本身獨立的做用域。
            2. 在bar函數內部,有一個max自由變量,若是本身沒有,就要到建立這個函數的做用域中找,就是fn函數做用域中的max
            3. 因此,這個max不能被銷燬,fn函數的上下文執行環境不能被銷燬,依然存在於執行棧中。

        4. 也就是說,執行完var f1 = fn() ,max  = 100;以後,全局上下文環境將變爲活動狀態,可是fn()上下文環境依然會在執行上下文棧中。
        5. 建立bar函數是在執行fn函數時建立的。fn()早就執行結束了,可是fn()執行上下文環境還存在與棧中,裏面的變量聲明、賦值都還在,
        6. 所以bar(15)時,max能夠查找到。若是fn()上下文環境銷燬了,那麼max就找不到值了。
        */

 6、上下文環境和做用域的關係 1. 上下文環境:就是一個對象,裏面保存了許多屬性,全部變量都在這個對象裏面存着。對於函數來講,上下文環境是在調用函數的時候建立 2. 做用域:是一個地盤,一個抽象的概念,規定了變量起做用的範圍。除了全局做用域以外只有函數才能建立做用域,做用域在函數定義時肯定,而不是在函數調用時肯定。 3. 關係:     1. 要經過做用域對應的執行上下文環境來獲取變量的值。     2. 同一個做用域下,不一樣的調用會產生不一樣的執行上下文環境,繼而產生不一樣的變量的值。     3. 做用域中變量的值是在執行過程當中產生的肯定的,而做用域倒是在函數建立時就肯定了。      4. 若是要查找一個做用域下某個變量的值,就須要找到這個做用域對應的執行上下文環境,再在其中尋找變量的值。

相關文章
相關標籤/搜索