可以共享范围并访问实例的私有原型方法

时间:2015-07-22 13:49:01

标签: javascript design-patterns prototype private private-members

我正在寻找一种模式,它允许我创建一个我的函数原型可以访问的私有范围,我需要能够从该范围内访问该实例。

例如,这就是我目前正在实现“私有方法”的方式(忽略代码实际执行的操作,只需查看结构。)

function InfoPreview() {
   this.element = document.createElement('div');
}

//Private Methods
InfoPreview.prototype.__newLine = function () {
  this.element.appendChild(createElement({tagName:'br'}));
}; 

InfoPreview.prototype.__padLeft = function(level) {
  var padding = createElement({tagName: 'span'});
  this.element.appendChild(padding);
  $(padding).width(level * 10);
};

InfoPreview.prototype.__print = function(string) {
  var span = createElement({ tagName: 'span', textContent: string });
  this.element.appendChild(span);
  this.element.style["margin-right"]='10px';
};

InfoPreview.prototype.__puts = function(string) {
  this.__print(string);
  this.__newLine();
};

//Public Methods
InfoPreview.prototype.update = function(info) {
  $(this.element).empty();
  for (var record in info) {
    this.__puts(record);
  }
};   

请注意,我根本不使用命名约定来创建私有方法。另请注意,我无法缓存链查找,例如this.element

我想通过利用一个揭示模块模式来创建一个私有范围,如下所示:

InfoPreview.prototype = (function() {
  var self = this, //<- `this` is actually the global object now.
      el = self.element;

  var newLine = function () {
    el.appendChild(createElement({tagName:'br'}));
  }; 

  var padLeft = function(level) {
    var padding = createElement({tagName: 'span'});
    el.appendChild(padding);
    $(padding).width(level * 10);
  };

  var print = function(string) {
    var span = createElement({ tagName: 'span', textContent: string });
    el.appendChild(span);
    el.style["margin-right"]='10px';
  };

  var puts = function(string) {
    print(string);
    newLine();
  };

  var update = function(info) {
    $(el).empty();
    for (var record in info) {
      puts(record);
    }
  };

  return {
    update: update
  }; 
})();

然而,上述方法不起作用,因为IIFE中this的值是全局对象,而不是实例。我需要一种方法来访问实例。

4 个答案:

答案 0 :(得分:1)

每个函数中,您可以访问所需的this值。

&#13;
&#13;
var Example = function() {};

Example.prototype = (function() {
  var privateUpdate = function() {
    document.getElementById('answer').innerHTML = this.foo;
  }
  
  return {
    update: privateUpdate
  }
})();

var e = new Example();
e.foo = 'bar';
e.update();
&#13;
<div id="answer"></div>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

作为Pointy建议的变体,你可以尝试这种模式;

infoPreview.prototype = (function() {
  var self = null;

  var update = function(info) {
      ....
  };

  var firstUpdate = function(info) {
      self = this;
      functions.update = update;
      update(info);  
  }

  var functions = {
    update: firstUpdate
  }; 
  return functions;
})();

答案 2 :(得分:1)

使用构造函数模式有什么缺点吗?

&#13;
&#13;
function Foo(constructorArg) {
    
    /* private variables */
    var privVar = 'I am private',
        cArg = constructorArg;
    
    /* public variables */
    this.pubVar = 'I am public';
    
    /* private function */
    function privFunc() {
        return 'I am a private function';
        
    }
    
    /* public function */
    this.publicFunc = function() {
        return 'I am a public function and I call privVar->"' + privVar + '" and privFunc->"' + privFunc() + '"';
    }
}

var foo = new Foo('something');

console.log('foo.pubVar', foo.pubVar); //ok
console.log('foo.publicFunc()', foo.publicFunc()); // ok

console.log('foo.privVar', foo.privVar); // undefined
console.log('foo.privFunc', foo.privFunc()); //error
&#13;
&#13;
&#13;

为什么要使用它(按照评论中的要求):

简单地说,因为它是创建一个真正的私人范围&#34;的唯一(理智)方式,这是你的问题。

另一种方法是使用一种约定,告诉开发人员哪些属性和方法是私有的,通常是在前面添加一个你已经实现但不喜欢的下划线_

请注意,构造函数和原型是不同的东西,使您可以执行不同的操作。没有什么可以阻止你混合起来。

内存使用

关于内存使用情况,在现代js引擎中,例如Google的V8 JavaScript引擎,the constructor pattern might actually be faster

  

V8具有在运行时为对象内部创建的隐藏类型;具有相同隐藏类的对象可以使用相同的优化生成代码。

例如:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

var p1 = new Point(11, 22);
var p2 = new Point(33, 44);
// At this point, p1 and p2 have a shared hidden class
p2.z = 55;
// warning! p1 and p2 now have different hidden classes!

原型链总是需要两次查找,因此它甚至可能是一个微小的inny LITTLE位慢。 注意:无法支持,jsperf.com已关闭!

构造函数模式很脏(原文如此)

  

表现是我的理由。我没有意识到这一点。然而它对我来说仍然感觉很脏

我不知道为什么你觉得构造函数模式很脏。也许是因为它有一些特定的&#34;,限制和潜在的陷阱你应该知道

  1. this可能意味着不同的事情
  2. 很容易忘记新关键字导致奇怪且难以调试因共享状态而导致的错误
  3. 您无法轻松地将对象拆分为多个文件(无需借助构建工具或某些第三方注入器)。
  4. 然而,对于原型声明样式,1和2也是如此......

    如果您认为这还不够,您可能需要查看模块模式。

答案 3 :(得分:0)

也许是这样的,没有原型设计:

https://jsfiddle.net/ynwun1xb

var Fn = function(el) {
    this.el = el;

    var myMethod = function() {
        console.log('do something in method with element', this.el);
    }.bind(this);

    return {
        myPublicMethod: function() {
            return myMethod();
        }
    }
}

var instancedFn = new Fn('first instance element')
    .myPublicMethod()
;

var instancedFn2 = new Fn('second instance element')
    .myPublicMethod()
;