【问题标题】:How to create private variable accessible to Prototype function?如何创建原型函数可访问的私有变量?
【发布时间】:2011-06-10 14:22:21
【问题描述】:

我试图更深入地掌握原型继承和类创建(我知道,还有其他方法,但为此我试图掌握原型。)我的问题是:使用以下代码例如,有没有办法在 TreeFruit 内部创建私有变量,这些变量不会随函数返回,但原型函数 genusbulk 仍然可以访问?

var Tree = function ( name, size ) { 
    this.name = name;
    this.size = size;
};

Tree.prototype.genus = function(){
    return ((typeof this.name !== 'undefined') ? this.name : 'Hybridicus Maximus');
};
Tree.prototype.bulk = function(){
    return ((typeof this.size !== 'undefined') ? this.size : '8') + ' ft';
};


var Fruit = function( name, size ) { 
    this.name = name;
    this.size = size;
};

Fruit.prototype = new Tree();
// Fruit.prototype = Tree.prototype; -- I know this can be used, too.

Fruit.prototype.bulk =  function(){
    return ((typeof this.size !== 'undefined') ? Math.floor(this.size / 2) : '4') + ' lbs';
};

var pine = new Tree('Pine', 9);
var apple = new Fruit('Apple', 6);

console.log(pine.genus(), pine.bulk()); // Outputs: "Pine 9 ft"
console.log(apple.genus(), apple.bulk()); // Outputs: "Apple 3 lbs"

编辑:我正在尝试将this.namethis.size 替换为可以在原型函数中访问的私有变量。很抱歉没有说清楚!

【问题讨论】:

  • 你能不把语言问题标记为“强化学习”吗?
  • @Don-Reba,是的,很抱歉。我把它标记为“学习”,它一定是为了强化它。 EDIT 他们现在有一个合适的标签用于原型继承,当我制作这个时它不可用。切换到它。很抱歉造成混乱。
  • @ecampver 是的,他们问了我同样的问题。
  • 其实是有办法的。检查this answer

标签: javascript prototypal-inheritance


【解决方案1】:

是的。你可以这样做:

(function() {
  var private = "hi";

  Tree.prototype.genus = function(){
    return ((typeof this.name !== 'undefined') ? this.name : 'Hybridicus Maximus');
  };
  Tree.prototype.bulk = function(){
    return ((typeof this.size !== 'undefined') ? this.size : '8') + ' ft';
  };
})();

现在,这将提供一个这些函数可以看到的私有变量,但它将是一个私有“类”变量——换句话说,所有实例都将共享同一个变量。如果您希望每个实例都有一个私有变量,则必须在构造函数(或“init”方法或其他任何方法)中执行此操作,这意味着也必须在那里创建共享这些私有变量的方法。 (您当然可以在原型上放置一个函数,在构造时创建实例方法。)

edit — 你可以做的一件事是使用这样的技术来构建一个类似 jQuery 的“.data()”的机制,这样你就有一个类变量作为一个地方保留每个实例的值。这有点笨拙,但它是可行的。

【讨论】:

  • 抱歉——添加了清晰的编辑。试图用私有变量替换 this.name 和 this.size。
  • 确实喜欢这样,这对于其他一些使用原型的项目来说是一个非常有用的模式。谢谢! -- 赞成。
【解决方案2】:

这就是我在一篇关于 Classes, Private Members, & Prototypal Inheritance in JavaScript 的博客文章中所写的内容。基本上你想为每个对象创建一个私有变量访问器函数,然后让这些原型方法调用该私有访问器函数,并为其提供仅在闭包中可用的密钥:

(function(_) {
  Tree = function ( name, size ) { 
    var hidden = {
      name: name,
      size: size
    };
    this._ = function($) {
      return _ === $ && hidden;
    };
  };

  Tree.prototype.genus = function(){
    return ((typeof this._(_).name !== 'undefined') ? this._(_).name : 'Hybridicus Maximus');
  };
  Tree.prototype.bulk = function(){
    return ((typeof this._(_).size !== 'undefined') ? this._(_).size : '8') + ' ft';
  };

  Fruit = function( name, size ) { 
    Tree.apply(this, arguments);
  };
  Fruit.prototype = new Tree();
  // Fruit.prototype = Tree.prototype; -- I know this can be used, too.

  Fruit.prototype.bulk =  function(){
    return ((typeof this._(_).size !== 'undefined') ? Math.floor(this._(_).size / 2) : '4') + ' lbs';
  };
})({});



var pine = new Tree('Pine', 9);
var apple = new Fruit('Apple', 6);

console.log(pine.genus(), pine.bulk()); // Outputs: "Pine 9 ft"
console.log(apple.genus(), apple.bulk()); // Outputs: "Apple 3 lbs"

console.log(pine._(), pine._({})); // Outputs: "false false" because outside of closure

你会注意到最后一行显示私有变量在闭包之外是不可访问的,因此第三方代码无法检索到,除非由访问函数提供。

【讨论】:

  • 您仍然可以通过覆盖 ._ 方法劫持该本地密钥对象 (_),然后访问所有“隐藏”数据。
  • 这是真正的原型继承吗? “Tree”的每个实例都将基于不同的构造函数(因为它是不同的闭包),因此没有两个 Tree 实例将共享相同的原型。我认为内存开销基本上与将所有这些函数直接添加到构造函数中的this 相同(然后您可以在构造函数中为私有字段使用局部变量,这更简单)。
【解决方案3】:

这样就可以轻松实现

function SharedPrivate(){
  var private = "secret";
  this.constructor.prototype.getP = function(){return private}
  this.constructor.prototype.setP = function(v){ private = v;}
}

var o1 = new SharedPrivate();
var o2 = new SharedPrivate();

console.log(o1.getP()); // secret
console.log(o2.getP()); // secret
o1.setP("Pentax Full Frame K1 is on sale..!");
console.log(o1.getP()); // Pentax Full Frame K1 is on sale..!
console.log(o2.getP()); // Pentax Full Frame K1 is on sale..!
o2.setP("And it's only for $1,795._");
console.log(o1.getP()); // And it's only for $1,795._

显然,关键点是通过使用闭包来创建到私有变量的访问路径,然后在要创建的对象之间共享该访问点。使用接入点作为自然共享对象的原型是理想的情况。因此,通过使用工厂模式和Object.create() 可以实现相同的功能,如下所示;

function SharedPrivate(){
var priv = "secret";
return {gp : function(){return priv},
        sp : function(v){priv = v}
       }
}
sharedProto = SharedPrivate(); // priv is now under closure to be shared
var p1 = Object.create(sharedProto); // sharedProto becomes o1.__proto__
var p2 = Object.create(sharedProto); // sharedProto becomes o2.__proto__

JavaScript 原型结构是金..!

【讨论】:

  • 从不。曾经。从构造函数中分配给原型属性。
  • @Bergi 感谢您的评论...我时常听到这个陈词滥调,但我自己也想不出一个合理的理由,也没有听到任何人说的比这句话更多。我倾向于相信这只不过是空中的八卦。您能否详细说明在我自己创建的函数原型中添加两​​个小函数有什么危险。
  • 这不是陈词滥调:只是没有意义,it has undesirable behaviour。您似乎已经理解这一点,因为您将该类命名为“SharedPrivate”,甚至似乎预计o1 干扰o2 的通常不受欢迎的行为;但即使你想创建一个静态的共享变量,你的方法也行不通。只需尝试在该调用序列中的某处创建一个o3...问题不在于添加到原型,而在于在构造函数中这样做。
  • 感谢您花时间解释。我的错..我忘了提到我对“从不。永远。从构造函数中分配给原型属性”句子的批评者仅根据我上面显示的模式相关。否则当然你不应该在构造函数中修改原型。但是,无论是否有意义,如果您需要在多个对象之间创建共享私有,那么它们必须立即一起创建,因为您也已经弄清楚了。如果不触及原型本身,就无法使用构造函数来做到这一点。
  • 不,这句话不仅仅与您的特定代码有关。确实从不有充分的理由这样做——“当然,否则你应该从构造函数中修改原型。”显然是错误的。或者你能说出一个原因吗?如果要跨实例共享状态,则不应在构造函数(最后一次调用)中创建该状态。只需将其放在构造函数之外,如果您需要隐私,请将所有内容包装在 IIFE 中。
【解决方案4】:

我是根据您的问题标题而不是您提供的内容来编造这个的。我在这里有同样的问题。

var Tree = function(){

    var outprivatename = "bigsecret"

    var Tre = function(nickname){
        var privatename = "secret that no one should know" 
        this.nickname = nickname
        outprivatename = outprivatename + "-->" + nickname
        this.deepsecret=function(){return privatename + "---" + nickname}
    }

    Tre.prototype.getname=function(){
        console.log(outprivatename+'-----'+this.nickname)
        // console.log(privatename)
      return this.nickname
    }
    return Tre

}
//case one: each has unique closure; `outprivatename` is different for them
var pine = new (Tree())('pine')
var apple = new (Tree())('apple')
pine.getname() //bigsecret-->pine-----pine
apple.getname() //bigsecret-->apple-----apple
console.log(pine.deepsecret()) //secret that no one should know---pine
console.log(apple.deepsecret()) //secret that no one should know---apple

//case one: two share one closure; `outprivatename` is same for both
var Hybrid = Tree()
var hybrid1 = new Hybrid("pinapple1")
var hybrid2 = new Hybrid("pinapple2")

hybrid1.getname() //bigsecret-->pinapple1-->pinapple2-----pinapple1
hybrid2.getname() //bigsecret-->pinapple1-->pinapple2-----pinapple2
console.log(hybrid1.deepsecret()) //secret that no one should know---pinapple1
console.log(hybrid2.deepsecret()) //secret that no one should know---pinapple2

基本上Tree 函数为它内部的构造函数Tre 提供了一个闭包。您可以使用在Tre 之外但在匿名函数Tree 内部的那个,而不是使用最深的privatename

【讨论】:

    猜你喜欢
    • 2013-08-03
    • 2011-12-08
    • 2010-09-30
    • 2013-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-20
    相关资源
    最近更新 更多