2014-12-20 48 views
8

如何在JavaScript模塊模式中通過私有函數調用公共函數?如何在JavaScript模塊模式中通過私有函數調用公共函數

例如,在下面的代碼,

var myModule = (function() { 
    var private1 = function(){ 
     // How to call public1() here? 
     // this.public1() won't work 
    } 

    return { 
     public1: function(){ /* do something */} 
    } 
})(); 

這個問題已經被問twicebefore,爲每個不同的接受的答案。

  1. 在返回對象之前保存對返回對象的引用,然後使用該引用訪問公共方法。見answer
  2. 保存對閉包中公共方法的引用,並使用它來訪問公共方法。見answer

儘管這些解決方案有效,但從OOP的角度來看,它們並不令人滿意。爲了說明我的意思,讓我們用這些解決方案中的每一個解答一個雪人的具體實現,並將它們與一個簡單的對象字面值進行比較。

雪人1:保存參考返回對象

var snowman1 = (function(){ 
    var _sayHello = function(){ 
    console.log("Hello, my name is " + public.name()); 
    }; 

    var public = { 
    name: function(){ return "Olaf"}, 
    greet: function(){ 
     _sayHello(); 
    } 
    }; 
    return public; 
})() 

雪人2:保存參考公共職能

var snowman2 = (function(){ 
    var _sayHello = function(){ 
    console.log("Hello, my name is " + name()); 
    }; 
    var name = function(){ return "Olaf"}; 

    var public = { 
    name: name, 
    greet: function(){ 
     _sayHello(); 
    } 
    }; 
    return public; 
})() 

雪人3:對象常量

var snowman3 = { 
    name: function(){ return "Olaf"}, 
    greet: function(){ 
     console.log("Hello, my name is " + this.name()); 
    } 
} 

我們可以看到,三者功能完全相同,並具有完全相同的公共方法。

如果我們運行簡單的壓倒一切的考驗,但是

var snowman = // snowman1, snowman2, or snowman3 
snowman.name = function(){ return "Frosty";} 
snowman.greet(); // Expecting "Hello, my name is Frosty" 
       // but snowman2 says "Hello, my name is Olaf" 

我們看到,#2失敗。

如果我們運行原型壓倒一切的考驗,

var snowman = {}; 
snowman.__proto__ = // snowman1, snowman2, or snowman3 
snowman.name = function(){ return "Frosty";} 
snowman.greet(); // Expecting "Hello, my name is Frosty" 
       // but #1 and #2 both reply "Hello, my name is Olaf" 

,我們看到,無論是#1和#2失敗。

這是一個非常醜陋的情況。僅僅因爲我選擇以這種或那種方式重構我的代碼,返回對象的用戶必須仔細查看我是如何實現一切的,以確定他/她是否可以重寫我的對象的方法並期望它可以工作!雖然意見不同,但我自己的看法是,正確的重寫行爲是簡單對象字面值的行爲。

所以,這是真正的問題:

有沒有辦法從一個私人調用一個公共方法,這樣得到的對象的行爲像一個對象文本相對於覆蓋的行爲呢?

+2

在你討論的陷阱中有一個重要的註釋:我對模塊模式的理解是,它真的是JS的原型/ OOP多態論方法的替代。因此,我認爲你應該避免使用這兩者 - 我不認爲這是一個陷阱*本身*。 – EyasSH

+0

@ EyasSH - 它可以這樣使用,但我認爲它更像是一種可以用於繼承而不是故意特徵的人工製品。它似乎主要用於模擬私人成員(或者不公開別人不應該玩的東西),也可以提高性能。 – RobG

+1

@RobG,沒錯,但是你可以在閉包中聲明私有函數,同時堅持原型模式。在那種情況下,使用'''this'''很好。我的觀點是,如果你正在使用模塊模式,我認爲看模塊<->原型交互怪異不是一個真正的理由不使用一種方法。如果你認爲誰在使用你的代碼將會這樣做,那麼這可能是一個堅持原型的原因。 – EyasSH

回答

2

您可以使用this來獲取您的特權方法greet被調用的對象。

然後,您可以將該值傳遞給您的私有方法_sayHello,例如,使用callapply,或者作爲一個參數:

var snowman4 = (function() { 
    var _sayHello = function() { 
     console.log("Hello, my name is " + this.name); 
    }; 
    return { 
     name: "Olaf", 
     greet: function() { 
      _sayHello.call(this); 
     } 
    }; 
})(); 

現在你可以做

var snowman = Object.create(snowman4); 
snowman.greet(); // "Hello, my name is Olaf" 
snowman.name = "Frosty"; 
snowman.greet(); // "Hello, my name is Frosty" 

而且還

snowman4.greet(); // "Hello, my name is Olaf" 
snowman4.name = "Frosty"; 
snowman4.greet(); // "Hello, my name is Frosty" 
+0

這個答案似乎沒有解決如何在模塊模式中的私有函數內調用公共函數的問題 - 特別是_sayHello()應該如何調用name()。在IIFE中包裝對象字面值不會使greet()具有特權。 –

+0

@ I-LinKuo是的,很榮幸。它可以訪問自執行功能中聲明的私有數據。 – Oriol

+0

是的,有特權:它可以訪問自執行函數中聲明的私有數據。我用一種特權方法而不是私有方法來簡化,但它確實沒有直接回答問題,所以我修復了它。 – Oriol

1

隨着模塊模式,你隱藏對象的所有innates局部變量/函數和通常僱用那些在您的公共職能。每次使用模塊模式創建新對象時,都會創建一組新的暴露函數(具有自己的作用域狀態)。

使用原型模式,您可以爲某些類型的所有對象提供相同的一組方法。這些方法的變化是this對象 - 換句話說,這是他們的狀態。但是this從不隱藏。

不用說,混合這些很難。一種可能的方法是將私有方法使用的方法抽取到模塊的結果對象的原型中,其格式爲Object.create。例如:

var guardian = function() { 
    var proto = { 
     greet: function() { 
      console.log('I am ' + this.name()); 
     }, 
     name: function() { 
      return 'Groot'; 
     } 
    }; 
    var public = Object.create(proto); 
    public.argue = function() { 
     privateGreeting(); 
    }; 

    var privateGreeting = public.greet.bind(public); 
    return public; 
}; 

var guardian1 = guardian(); 
guardian1.argue(); // I am Groot 
var guardian2 = guardian(); 
guardian2.name = function() { 
    return 'Rocket'; 
}; 
guardian2.argue(); // I am Rocket 
var guardian3 = guardian(); 
guardian3.__proto__.name = function() { 
    return 'Star-Lord'; 
}; 
guardian3.argue(); // I am Star-Lord 
相關問題