一段關於JS中this應用奇葩程式碼引發的思考

weixin_33766168發表於2017-11-16
function DemoFunction(){
    this.init = function(){
        var func = (function(va){
        this.va = va;
        return function(){
            va += this.va;
            return va;
        }
        })(function(va1, va2){
            var va3 = va1 + va2;
            return va1;
        }(1,2));
        
        console.log(func(20));

        this.func = func;
        console.log(this.func(100));
    }
}
var a = new DemoFunction();
a.init();

首先我們得有如下幾個概念:

詳細解釋此段程式碼

一、首先看DemoFunction的建構函式

這是程式碼的重點,第一層程式碼可以縮減為如下:

function DemoFunction(){
    this.init = function(){
        //省略程式碼....
    }
}

表示為DemoFunction的例項提供init方法(宣告:此處有誤導成份,方法應儘可能放在原型連結上,也就是prototype上。),對外公開的介面。

二、在init方法中,再次省略程式碼如下:

var func = (function(va){
    this.va = va;
    return function(){
        va += this.va;
        return va;
    }
})(/*省略程式碼...*/);

//省略程式碼....

上面程式碼介紹:

  • 首先定義了一個立即執行函式,並把此函式的執行結果賦值給func。

  • 需要注意立即執行函式中this.va=va這行程式碼,由於立即執行函式沒有呼叫者,所以在進入可執行上下文時,this會被賦值為Global(瀏覽器中為window物件)。

  • 更需要注意立即執行函式,返回的是一個匿名函式,也是一個閉包,在這裡一定要注意一個問題:this是在進入可執行上下文時建立的。

三、在init方法中,注意如下程式碼:

var func = (function(va){
    this.va = va;
    return function(){
        va += this.va;
        return va;
    }
})(function(va1, va2){
    var va3 = va1 + va2;
    return va1;
}(1,2));
//省略程式碼....

va的實際引數是一個自執行匿名函式,這個匿名函式接受了兩個引數va1,va2,但只返回了va1。以此為據,那麼可以確定va的值也就為1。接著就執行this.va=va這句程式碼,由於當前this為window,所以引數va的值被賦值到了window的一個叫va的屬性上。

四、在init方法中,加上輸出語句:

var func = (function(va){
    this.va = va;
    return function(){
        va += this.va;
        return va;
    }
    })(function(va1, va2){
        var va3 = va1 + va2;
        return va1;
    }(1,2));
    
    console.log(func(20));

    this.func = func;
    console.log(this.func(100));
}

結果分析:

  • 第一個console.log輸出的是func(20),這裡一定要注意呼叫者是沒有具體指定的,此時預設的就是Global(也就是widnow物件),因此輸出為:2

  • 第二個console.log輸出的是this.func(100),可以看到this.func與func是指向同一個函式的引用,但此時的呼叫者則指定為this,也就是當前物件的例項,因此輸出為:NaN。原因:this(當前物件的例項)作為呼叫者,在func的函式中va += this.va這句程式碼中的this是指向當前物件的例項,但當前物件的例項上是沒有va屬性的。但是va是有值的,當前值為2了。是因為閉包把va值存到記憶體中了。那麼如何讓第二次得到的值也是2呢,結果很簡單,如下:

    function DemoFunction(){
        this.va = 0;
        this.init = function(){
            var func = (function(va){
            this.va = va;
            return function(){
                va += this.va;
                return va;
            }
            })(function(va1, va2){
                var va3 = va1 + va2; 
                return va1;
            }(1,2));
            console.log(func(20));
            
            this.func = func;
           
            console.log(this.func(100));
        }
    }
    var a = new DemoFunction();
    a.init();

相關文章