【问题标题】:Is there anyway to have instances share the same function yet at the same time have private variables?是否有实例共享相同的功能但同时具有私有变量?
【发布时间】:2011-08-25 07:15:54
【问题描述】:

我有这段代码:

var Human=function(name){
  this._name=name;
};
Human.prototype.Shout=function(){
  alert(this._name);
};

var tom=new Human("tom");
var john=new Human("john");
alert(tom.Shout===john.Shout);

现在 ._name 不是“私有的”。我想将 ._name 设为“私有”,但同时我不希望为 Human 的每个实例创建附加函数(换句话说,tom.Shout 必须 === 到 john.Shout),因为为每个实例都很好.. 不必要(好的题外话 - 我们可以在另一个线程上讨论这个)

我的结论是,我想要实现的目标(让 ._name 为“私有”,同时拥有 tom.Shout===john.Shout)是不可能的。

但在得出任何结论之前,我只想 200% 确定。

(只要满足要求,我欢迎任何 hack,即不为每个实例创建额外的函数)

如果我们必须创建额外的函数来进行范围界定,那很好,但该数字应该是一个固定数字,并且该数字不应随着 Human 的每个额外实例而增加。

【问题讨论】:

  • 也许我遗漏了一些东西,但是在 Chrome、Firefox 和 IE 以及 nodejs (v8) 中运行上面的代码 - 包裹在 中 - 都警告'true'。跨度>
  • @Rob Raisch 是的,它应该是“真”,因为这些函数是同一个实例。换句话说,我们只有 1 个功能被两个人“共享”
  • @Pacerier,由于“this”是为每个实例创建的特殊上下文,所有实例都有自己的“_name”。
  • @Pacerier 您严重高估了创建函数(在浏览器中)的成本。
  • @Pacerier,嗯...好吧,从我在其他 cmets 中读到的内容,您想将 _name 设为私有,而不是“私有”,这意味着类似于私有但实际上并非如此?您是否正在寻找一种确保实例变量真正私有的可靠方法,因为它们不能从实例外部访问?如果是这样,则没有。 Javascript 允许反射,这将“揭开真正隐私的面纱”。

标签: javascript function ecmascript-5


【解决方案1】:

这个怎么样?

    var Human = function (name) {
        var _name = name;

        this.getName = function () {
            return _name;
        }
    };
    Human.prototype.Shout = function () {
        alert(this.getName());
    };

    var tom = new Human("tom");
    var john = new Human("john");

    tom.Shout(); // tom
    john.Shout(); // john
    alert(tom.Shout === john.Shout); // true

编辑: 前者为 GET 属性创建另一个函数, 如果不创建附加功能,这是不可能的。

【讨论】:

  • 这为 Human 的每个实例创建了一个附加函数.. 违反了第一个、最后一个和唯一的规则
  • 没错,但它使 _name 变量保持私有,并且访问仅限于公共属性
  • 我知道,但这个问题的全部原因是要找出是否有任何黑客允许我们将 ._name 设为“私有”,但同时我不希望创建每个 Human 实例的附加功能[/quote]
  • 另外,我们设法使界面干净(使_name私有),但代价是使其变脏(添加另一个公共成员getName),这有点违背了目的,但公平地说,我没有说明问题中不允许这样做
  • @Pacerier 我用来使界面干净的一种模式是拥有一个变量this._ = {},并将所有局部变量都设置为this._.name。除非您查看this._,否则这会显示更少的混乱
【解决方案2】:

更新

您正在寻找@name,它是一个实例变量。祈祷它在 es.next 中,但我们还没有。也许两年后。

如果您关心干净的 API,那么这就是您的解决方案:

function Class(foo) {
    Class.priv(this).foo = foo;
}

Class.priv = (function() {
    var cache = [],
        uid = 1;

    return function(obj) {
        if (!this.__id) {
            this.__id = uid;
            cache[uid++] = {};
        }
        return cache[this.__id];
    };

}());

Class.prototype.bar = function() {
    console.log(Class.priv(this).foo);
}

将所有数据作为构造函数的函数存储在缓存中。对象上没有杂乱的数据。

原创

但是没有“私人”这样的东西。

您所能做的就是在函数中创建一个局部变量。

构造函数

var Human = function(name) {
    // local variable.
    var _name = name;
}

有一个局部变量,根据本地的定义在构造函数之外是不可用的。

这意味着您无法在原型等外部代码中访问它。

但是,您可以做的是使用 ES5 将其设为只读

var Human = function(name) {
    Object.defineProperty(this, "name", { value: name });
}

如果您能真正实现您的要求,那么您将在 js 方面取得巨大突破。我已经尝试了好几个小时。

另一种模式是:

var Human = function(name) {
   this.name = name;

   return {
       Shout: this.Shout.bind(this)
   };
}

Human.prototype.Shout = function() {
   console.log(this.name);
}

这有调用.bind 并为每个实例创建一个新对象的开销。

【讨论】:

  • 是的,.bind 实际上在每次调用时都会创建一个新函数。实际上,我对附加对象的开销感到满意,因为当我们将其与那些附加功能的扩展方式进行比较时,它的扩展性非常好。我想说的是,如果 Human 类有 6 个方法,则每个新实例都有 1 个对象和 6 个新函数的开销。如果 Human 类有 12 个方法,则每个新实例都有 1 个对象的开销(仍然)但有 12 个新函数
  • @Pacerier 你的正确,绑定确实创建了一个新方法。你意识到你不应该关心开销对吗?任何少于 10k 的实例都没有问题,任何超过 10k 的都需要分析。担心这是一个micro优化。
  • 是的。我正在寻找更好的替代品,以防有我不知道的替代品
  • @Pacerier 我已经实现了一个抽象来清理你的对象。您的数据仍然存储在一个对象中。这是每个对象开销的一个额外对象。
【解决方案3】:

看了问题,没看懂,因为this._name 只是不是私人的,所以问题有点奇怪。这就是在my test 中,原型方法被添加一次并且可用于所有实例的方式。我再说一遍:this._name 在这里不是私人的。如果你添加了一个局部变量,并希望通过原型方法中的闭包来访问它,那么调用局部变量的值将导致所有实例的值相同。

无论如何,通过这个构造函数,this._name getter 和shout 方法被添加到原型链中一次,因此可用于Human 的所有实例。

function Human(name) {
    if (!(this instanceof Human)){
        return new Human(name);
    }
    this._name = name;

    if (!Human.prototype.Name){
         Human.prototype.Name = function(val){
            if (val){
               this._name = val;
               return this;
            }
            return this._name;
          };
          Human.prototype.shout = function(){
              alert(this._name);
          }
    }
}

【讨论】:

  • @Kooilnc alert(tom.shout === john.shout); // false
  • 实际上,当我说 我想将 ._name 设为“私有” 时,我认为我的意思很明显:现在。 _name 不是“私人的”。我想将 ._name 设为“私有”。无论如何,我已经编辑了问题的措辞,以防其他人感到困惑
猜你喜欢
  • 2013-10-16
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多