Javascript中的可变隐私';委托模式

Javascript中的可变隐私';委托模式,javascript,design-patterns,functional-programming,prototypal-inheritance,Javascript,Design Patterns,Functional Programming,Prototypal Inheritance,从我上学期开始,我一直在研究Javascript的原型模型,并试图摆脱从其他语言继承的OOP愿景(有点双关语) 我回到了基础知识,阅读了克鲁克福德的书和材料,并决定坚持所谓的行为代表团 重构我以前实现行为委派和名称空间的示例,我写道: var GAME = {}; (function(namespace) { var Warrior = {}; Warrior.init = function(weapon) { this.setWeapon(weapon);

从我上学期开始,我一直在研究Javascript的原型模型,并试图摆脱从其他语言继承的OOP愿景(有点双关语)

我回到了基础知识,阅读了克鲁克福德的书和材料,并决定坚持所谓的行为代表团

重构我以前实现行为委派和名称空间的示例,我写道:

var GAME = {};

(function(namespace) {
    var Warrior = {};

    Warrior.init = function(weapon) {
        this.setWeapon(weapon);
    };

    Warrior.getWeapon = function() {
      return this.weapon;
    };

    Warrior.setWeapon = function(value) {
      this.weapon = value || "Bare hands";
    };

    namespace.Warrior = namespace.Warrior || Warrior;
})(GAME);

(function(namespace) {
    var Archer = Object.create(namespace.Warrior);

    Archer.init = function(accuracy) {
        this.setWeapon("Bow");
        this.setAccuracy(accuracy);
    };

    Archer.getAccuracy = function() {
        return this.accuracy;
    };

    Archer.setAccuracy = function(value) {
      this.accuracy = value;
    };

    namespace.Archer = namespace.Archer || Archer;
})(GAME);
所以,每次我复制一个新的弓箭手对象:

var archer1 = Object.create(GAME.Archer);
将仅创建此对象,以节省内存

但如果我不想暴露“准确性”属性呢?该属性只能通过调用“training()”方法或类似方法来增加。我试图在匿名函数中使用
var-accurity
,但它变成了一种静态变量,Archer的所有实例将共享相同的值

问题是:有没有办法在保持行为委托/原型模式的同时将变量设置为私有的?

我也知道功能模式,在这里,我以内存为代价成功地实现了可变隐私。通过功能化,每个新的“archer”实例生成一个新的“Warrior”,然后生成一个新的“archer”。即使考虑到Chrome和Firefox有不同的选项,对这两种浏览器的测试都表明委托/原型模式更有效:


如果我使用纯对象委托模式,我是否应该忘记经典的封装概念,接受属性的自由变化特性?

我会尝试用一些与此稍有不同的东西来回答您的问题,这些东西告诉您如何使用库。 不同的是,它将(希望)给你一些想法,我们如何解决OLOO中的私人VAR问题。至少在某种程度上,对于我们自己的代码,不需要外部库,这在某些场景中可能很有用


为了使代码更清晰,我已经对匿名包装函数进行了条带化,因为它们与问题没有任何关系

var Warrior = {};

Warrior.warInit = function (weapon){
   this.setWeapon(weapon);    
}

Warrior.getWeapon = function(){
   return this.weapon;
}

Warrior.setWeapon = function (value){
   this.weapon = value || "Bare hands";
}

var Archer = Object.create(Warrior);

Archer.archInit = function (accuracy){
   this.setWeapon("Bow");   
   this.setAccuracy(accuracy); 
}

Archer.getAccuracy = function (pocket) {
   return pocket.accuracy;
}
Archer.setAccuracy = function (value, pocket){
   pocket.accuracy = value;
}

function attachPocket(){ 

   var pocket = {};

   var archer = Object.create(Archer); 

   archer.getAccuracy = function(){
      var args = Array.prototype.slice.call(arguments);
      args = args.concat([pocket]); 

      return Archer.getAccuracy.apply(this, args)
   }; 
   archer.setAccuracy = function(){ 
      var args = Array.prototype.slice.call(arguments);
      args = args.concat([pocket]); 

      return Archer.setAccuracy.apply(this, args);
   }; 

   return archer;
}


var archer1 = attachPocket();  

archer1.archInit("accuracy high"); 
console.log(archer1.getAccuracy()); // accuracy high
archer1.setAccuracy("accuracy medium");
console.log(archer1.getAccuracy()); // accuracy medium
上面的测试代码。(并打开浏览器控制台)

用法 1)OLOO中关于原型链不同级别上的命名函数的一般实践与OOP是一致的。我们需要更具描述性和自文档化的不同名称,这将使BRGS代码更清晰、可读性更强。更重要的是,通过使用不同的名称,我们可以避免递归循环:

Archer.init = function(accuracy, pocket){
     this.init() // here we reference Archer.init() again, indefinite recurson. Not what we want 
      ...
}
Archer.archInit = fucntion (accuracy, pocket){ // better,
     this.warInit() // no name "collisions" .
}
2)我们创建了一个attachPocket()函数,用于创建内部变量pocket。使用object.create()创建新对象,并将其原型设置为指向Archer。暂停。如果您注意到,我们已经定义了一些函数,这些函数需要一个私有变量,以便每个函数都接受一个以上的参数(pocket),有些函数只使用pocket。这里是诀窍

通过使用archer.setAccurance()和archer.getAccurance()等包装函数 ... 我们可以创建闭包并直接调用需要的函数 private var(此处为pocket)并将其作为参数传递给他们

像这样:

 function AtachPocket(){
   ...
   var pocket = {};

   archer.setAccuracy = function(){ 
     var args = Array.prototype.slice.call(arguments);
     args = args.concat([pocket]); // appending pocket to args
     return Archer.setAccuracy(this, args); 
   }; 
   ...
 }
基本上,通过这样做,我们绕过了原型链中函数的正常搜索,只针对需要私有变量的函数。这就是“直接调用”所指的。 通过为
archer
(“实例”)中的函数设置与原型链(
archer
)中相同的名称,我们可以在实例级别跟踪该函数。没有不确定循环的危险,因为我们像上面所说的那样“直接调用”。同样,通过使用相同的函数名,我们保持了在“实例”中访问相同函数的正常预期行为,就像在原型链中一样。这意味着在
var archer=Object.create(archer)
之后,我们可以访问函数
setaccurity
,就像在原型链中搜索函数一样

3)每次调用attachPocket()时,它都会创建一个新的“实例”,其中包含传递pocket参数的包装函数(所有这些都作为实现的内部细节)。因此,每个实例都有自己的、唯一的、私有的变量

您通常会在“实例”中使用函数:

archer1.archInit("accuracy high"); // Passing needed arguments.
                               // Placed into pocked internally.
archer1.getAccuracy(); // Getting accuracy from pocket.
可伸缩性 到目前为止,我们所拥有的功能是“附加一个口袋”,带有硬编码值,如
Archer.setaccurity
Archer.getaccurity
。如果我们想通过引入一种新的对象类型来扩展原型链,比如
var-advancedcacher=object.create(Archer)
,那么如果我们将
attachPocket
传递给它,它将如何运行,而这个
advancedcacher
对象甚至可能没有
setaccurity()
函数?每次在原型链中引入一些更改时,我们是否都要更改
attachPocket()

让我们试着通过使
attachPocket()
更一般化来回答这些问题


首先,扩展原型链

var AdvancedArcher = Object.create(Archer);

AdvancedArcher.advInit = function(range, accuracy){
    this.archInit(accuracy);
    this.setShotRange(range);
}
AdvancedArcher.setShotRange = function(val){
    this.shotRange = val;
}
更通用的
attachPocket

function attachPocketGen(warriorType){

   var funcsForPocket = Array.prototype.slice.call(arguments,1); // Take functions that need pocket
   var len = funcsForPocket.length; 

   var pocket = {};
   var archer = Object.create(warriorType); // Linking prototype chain

   for (var i = 0; i < len; i++){ // You could use ES6 "let" here instead of IIFE below, for same effect
      (function(){  
         var func = funcsForPocket[i]; 
         archer[func] = function(){ 
             var args = Array.prototype.slice.call(arguments);
             args = args.concat([pocket]); // appending pocket to args

             return warriorType[func].apply(this, args);
         }
      })()
   }

   return archer;
}

 var archer1 = attachPocketGen(Archer,"getAccuracy","setAccuracy");  

archer1.advInit("11","accuracy high"); 
console.log(archer1.getAccuracy()); // "accuracy high";

archer1.setAccuracy("accuracy medium");
console.log(archer1.getAccuracy());
我们看到它使用了“pocket”函数
setAccurance
。但我们并没有在这里添加pocket作为它的最后一个参数,因为被调用的
setaccurity
是隐藏版本,即来自实例的版本。因为它将仅从一个实例调用,例如so
archer1.archInit(…)
,该实例已经添加了一个pocket作为最后一个参数,因此不需要这样做。这有点好,但它的定义是:

Archer.setAccuracy = function (value, pocket){ ...
当在原型链中创建像上面的
Archer.archInit
这样的对象时,这可能会让人困惑。如果我们看一下
setaccurity
的定义,我们似乎应该这样做。因此,为了不必记住,我们不必在使用其他pocket函数的函数(如
archInit
)中添加pocket作为最后一个arg,也许我们应该这样做:

Archer.setAccuracy = function (value){  
   var pocket = arguments[arguments.length -1];

   pocket.accuracy = value;
}
测试代码

函数定义中没有口袋作为最后一个参数。现在是
Archer.setAccuracy = function (value){  
   var pocket = arguments[arguments.length -1];

   pocket.accuracy = value;
}