如何从JavaScript模块模式中的私有函数中调用公共函数

时间:2014-12-20 22:44:40

标签: javascript module-pattern

如何在JavaScript模块模式中的私有函数中调用公共函数?

例如,在以下代码中,

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

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

此问题已被问及twice before,每个问题都有不同的接受答案。

  1. 在返回对象之前保存对返回对象的引用,然后使用该引用访问公共方法。见answer
  2. 在闭包中保存对public方法的引用,并使用它来访问public方法。见answer
  3. 虽然这些解决方案有效,但从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 个答案:

答案 0 :(得分: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"

答案 1 :(得分:1)

使用模块模式,您可以隐藏对象在本地变量/函数中的所有内容,通常在您的公共函数中使用它们。每次使用模块模式创建新对象时,都会创建一组新的公开函数 - 具有自己的作用域状态。

使用原型模式,您可以为某些类型的所有对象提供相同的方法集。这些方法的变化是this对象 - 换句话说,就是它们的状态。但this永远不会被隐藏。

毋庸置疑,混合这些很难。一种可能的方法是使用Object.create将privates使用的方法提取到模块的结果对象的原型中。例如:

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