【问题标题】:Best Practices for accessing private methods in the revealing module pattern在显示模块模式中访问私有方法的最佳实践
【发布时间】:2017-03-28 15:03:32
【问题描述】:

我见过的几乎每一个揭示模块模式的教程都会展示一个公共方法访问一个私有方法不使用使用“method.call(this,...) ”。

这似乎可以很好地完成工作,但是如果您在私有方法中执行跟踪(此),它将显示“窗口”。这感觉像是一个潜在的陷阱。

我应该使用call(或apply),还是会引入不必要的复杂性?

plunk:https://plnkr.co/edit/hc3ZPJyeHcT9bLbLaLpX?p=preview

编辑:我的想法是,通过使用“调用”,它以某种方式使其“更安全”,因为上下文是 api 对象而不是全局命名空间。

var Module = (function () {

    var _privateVar = "PRIVATE!";

    var api = {
        publicMethod: publicMethod
    }

    return api;

    function publicMethod() {
        privateMethod();
        privateMethod.call(this);
    };

    function privateMethod(message) {
        console.log(_privateVar, this);
    };


})();

Module.publicMethod();

【问题讨论】:

    标签: javascript scope private revealing-module-pattern


    【解决方案1】:

    没有最佳实践。两者都有

    function privateFunction(self, message) { // also popular: `that`
        // do something with `self`
    }
    …
    privateFunction(this, …);
    

    function privateMethod(message) {
        // do something with `this`
    }
    …
    privateMethod.call(this, …);
    

    很好。只需坚持一种风格并保持您的代码库一致。但是,第一个可能更受欢迎,因为许多私有函数实际上是静态的并且不需要使用实例,因此它简化了为实例使用显式参数的事情(而不是假设/希望函数被调用)预期的this 值)。

    【讨论】:

    • 明确似乎最好。但是,我确实注意到使用 call 的一个小优势 - 使用调试器时,您可以通过“this”对象方便地立即访问 getter
    【解决方案2】:

    过去几天我一直在努力解决这个问题,很高兴遇到这个问题。然而,经过一些实验,至少有一种情况似乎没有一个令人满意的答案:在公共函数和私有函数之间传递数据。

    以下是三个示例,分别使用“调用”、“绑定”和普通调用。绑定似乎是将私有方法保持在本地范围内的唯一方法,并且还允许私有方法和公共方法之间的数据交换。将上下文传递给私有方法的另一个建议仍然会使私有方法在模块外部执行(如果我理解正确的话)。

    有趣的是,使用普通调用(带/不带严格模式)允许数据交换,即使私有方法的上下文是“未定义”/“窗口”。

    使用“绑定”:

    "use strict"; 
    var home = (function(){
    
      var bob = function(){
        console.log("Bob's scope: " + Object.keys(this));  // "Bob's scope: init"
        var bob_says = "Hello";
        var fBound = alice.bind(this);
        var alice_reply = fBound(bob_says);
        console.log("Alice answered: " + alice_reply);     // "Alice answered: Hello Bob"
      };
    
      var alice = function(bob_greeting){
        var scope = this ? Object.keys(this) : this;
        console.log("Alice's scope: " + scope);            // "Alice's scope: init"
        console.log("Bob said: " + bob_greeting);          // "Bob said: Hello"
        return bob_greeting + " Bob"; 
      };
    
      return { init : bob };
    })();
    
    home.init();
    

    使用“呼叫”:

    "use strict"; 
    var home = (function(){
    
      var bob = function(){
        console.log("Bob's scope: " + Object.keys(this));  // "Bob's scope: init"
        var bob_says = "Hello"; 
        var alice_reply = alice.call(this, bob_says);
        console.log("Alice answered: " + alice_reply);     // "Alice answered: undefined Bob"
      };
    
      var alice = function(self, bob_greeting){
        var scope = this ? Object.keys(this) : this;
        console.log("Alice's scope: " + scope);            // "Alice's scope: init"
        console.log("Bob said: " + bob_greeting);          // "Bob said: undefined"
        return bob_greeting + " Bob"; 
      };
    
      return { init : bob };
    })();
    
    home.init();
    

    无需调用或绑定:

    "use strict";
    var home = (function(){
    
      var bob = function(){
        console.log("Bob's scope: " + Object.keys(this));  // "Bob's scope: init"
        var bob_says = "Hello"; 
        var alice_reply = alice(bob_says);
        console.log("Alice answered: " + alice_reply);     // "Alice answered: Hello Bob"
      };
    
      var alice = function(bob_greeting){
        var scope = this ? Object.keys(this) : this;
        console.log("Alice's scope: " + scope);            // "Alice's scope: undefined"
        console.log("Bob said: " + bob_greeting);          // "Bob said: Hello"
        return bob_greeting + " Bob"; 
      };
    
      return { init : bob };
    })();
    
    home.init();
    

    更新和可能的答案。 我很好奇,当 Alice 在没有“调用”或“绑定”的情况下被调用,并且她的上下文未定义(或窗口)时,她是否可以访问“家”中的私有变量?是的,所以我对 OP(和我自己)的暂定答案是正常调用私有函数:没有'bind',没有'call',也不需要在上下文中传递,但是如果它困扰你,则“使用严格”查看运行私有函数时弹出的窗口上下文。似乎没有在“窗口”中创建新属性,并且如果窗口具有与私有函数同名的对象(无论严格模式是打开还是关闭),也不会发生冲突。如果有陷阱,我还没有遇到过。

    "use strict";
    
    var alice = "Hi, I'm global's Alice"; 
    
    var home = (function(){
    
      var bob = function(){
        console.log("Bob's scope: " + Object.keys(this)); // "Bob's scope: init" 
        var bob_says = "Hello";
        var alice_reply = alice(bob_says);
        console.log("Alice answered: " + alice_reply);    // "Alice answered: Hello Bob. I'm in the garage."
      };
    
      var alice_location = "in the garage.";
    
      var alice = function(bob_greeting){
        var scope = this ? Object.keys(this) : this;
        console.log("Alice's scope: " + scope);           // "Alice's scope: undefined"
        console.log("Bob said: " + bob_greeting);         // "Bob said: Hello"
        return bob_greeting + " Bob. I'm " + alice_location; 
      };
    
      return { init : bob };
    
    })();
    
    home.init();
    
    console.log(alice);
    

    【讨论】:

      【解决方案3】:

      您的Module 永远不会在它自己的上下文中,因此调用它的静态函数将导致this 成为全局对象。

      --

      您可以尝试这样解决这个问题(通过创建“新”对象):

      const Module = (function () {
          var _privateVar = "PRIVATE!";
      
          const Api = function () {
      
          };
      
          function publicMethod() {
              privateMethod();
              privateMethod.call(this);
          };
      
          function privateMethod(message) {
              console.log(_privateVar, this);
          };
      
          Api.protoype.publicMethod = publicMethod;
      
          return Api;
      });
      
      const module = new Module();
      
      console.log(module.publicMethod());
      

      【讨论】:

      • 你说“尝试”是因为它不是真正可取的(“新”一个显示模块模式)?这样做似乎很尴尬。
      • 您不应该使用new 调用不是构造函数的函数。您的 Module 是一个返回对象的工厂函数,因此使用 new 毫无意义。无论如何,它不会改变privateMethod 的上下文。
      • 它返回一个函数而不是一个对象
      • @Neal 我写“object”是因为new 关心对象与原始值,当然函数就是对象。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-10
      • 2012-02-24
      • 2011-12-18
      • 1970-01-01
      相关资源
      最近更新 更多