js的一道經典題

function Foo(){
        getName = function () {alert(1);}
        return this;
    }
    Foo.getName = function() {alert(2);};
    Foo.prototype.getName = function(){alert(3);};
    var getName = function(){alert(4);};
    function getName(){alert(5)};

    Foo.getName();
    getName();
    Foo().getName();
    getName();
    new Foo.getName();
    new Foo().getName();    
    new new Foo().getName();

2 ----Foo.getName();
    4 ----getName();
    1 ----Foo().getName();
    1 ----getName();
    2 ----new Foo.getName();
    3 ----new Foo().getName();
    3 ----new new Foo().getName();

概念

爲了閱讀效益最大化,請先了解執行環境變量對象文字性概念。html

執行環境挺常見的,它是長這樣的。它的特性是先進後出(stack)。函數

clipboard.png

上述圖片是來源於代碼執行到Foo( ).getName( )。this

(anonymous function) 其實就是global contextspa

表示方法:prototype

ECStack = [
        functionContext(Foo),    /*這不是函數*/
        GlobalContext    
    ]

變量對象也挺常見,在Chrome中長這樣。指針

clipboard.png

表示方法一般是:code

//以上述代碼還沒有執行時爲例
    VO = {    
        Foo: {
            <reference to function>,
            return this
        }
        getName: <reference to function>
    }

分析

代碼執行有兩個階段htm

  1. 進入環境(代碼未執行,已編譯)對象

  2. 代碼執行blog

如今一塊兒來看看每一行代碼的執行

上述代碼未執行時,變量對象是這樣的。

VO = {
        Foo: {
            <reference to function>,
            return this
        }
        getName: <reference to function(){alert(5)}>
    }

執行"第一行"代碼

clipboard.png

該行代碼執行後VO會變成這樣。

VO = {
        Foo: { 
            <reference to function>,
            getName: <reference to function() {alert(2)},
            return this
        }
        getName: <reference to function(){alert(5)}>
    }

VO的Foo圖示是這樣的
clipboard.png

執行"第二行"代碼

clipboard.png

該行代碼執行後VO會變成這樣。

VO = {
        Foo: { 
            <reference to function>,
            getName: <reference to function() {alert(2)},
            prototype: {
                getName: <reference to function(){alert(3)}
                },
            return this,
                
        }
        getName: <reference to function(){alert(5)}>
    }

VO的Foo圖示是這樣

clipboard.png

執行"第三行"代碼

clipboard.png
該行代碼執行後VO會變成這樣。

VO = {
        Foo: { 
            <reference to function>,
            getName: <reference to function() {alert(2)},
            prototype: {
                getName: <reference to function(){alert(3)}
                },
            return this
        }
        getName: <reference to function(){alert(4)}>
    }

執行"第四行"代碼(第一問)

clipboard.png
直接去VO裏尋找結果發現alert 2。

執行"第五行"代碼(第二問)

clipboard.png

getName() //等於
window.getName();

一樣直接在VO裏能夠直接找到答案 alert 4。

執行"第六行"代碼(第三問)

clipboard.png
這裏比上述多了一個函數調用,Foo()調用返回this(指向window),因此此時

Foo().getName(); //等於
window.getName()

同時注意:
因爲Foo()調用,致使VO發生了變化。最後alert 1.

VO = {
        Foo: { 
            <reference to function>,
            getName: <reference to function() {alert(2)},
            prototype: {
                getName: <reference to function(){alert(3)}
                },
            return this
        }
        getName: <reference to function(){alert(1)}>
    }

執行"第七行"代碼(第四問)

clipboard.png

結果同上

執行"最後三行"代碼(第5、6、七問)

clipboard.png

最後三問考察了new、屬性訪問符、及函數調用優先順序和new運算符的做用。

優先順序參考以下,或點這

clipboard.png

不管是用哪一種屬性訪問表達式,在"."和"[]"以前的表達式老是會先計算的。

對於new Foo.getName()來講,點運算級最高,因此先進行計算表達式Foo的值。若是先運算new的話,就證實了new優先級高於點號,與規範不和。

而對於new Foo().getName()來講,Foo()與new Foo()相比,new優先級更高,因此先算new Foo(),new出來的新對象繼承了Foo.prototype屬性,所以新對象訪問getName時會彈出3.

此處要明白new的做用

  1. 建立新的空對象

  2. 將新對象繼承構造函數的prototype屬性,並調用構造函數初始化

  3. 將構造函數的this指針指向新建對象。

最後一個問題同理倒數第二個。


謝謝ID:李衛的提醒。

原題和分析連接

相關文章
相關標籤/搜索