【问题标题】:How to set javascript private variables in constructor?如何在构造函数中设置javascript私有变量?
【发布时间】:2011-10-11 13:30:33
【问题描述】:

假设我有一个名为Foo 的javascript 函数/类,它有一个名为bar 的属性。我希望在实例化类时提供bar 的值,例如:

var myFoo = new Foo(5);

myFoo.bar 设置为 5。

如果我将 bar 设为公共变量,则此方法有效,例如:

function Foo(bar)
{
    this.bar = bar;
}

但如果我想将其设为私有,例如:

function Foo(bar)
{
   var bar;
}

那么如何设置私有变量bar 的值,使其可用于foo 的所有内部函数?

【问题讨论】:

  • FWIW,你不能拥有真正的私有变量并利用原型。我个人会提供proper documentation 而不是让代码更复杂...
  • @FelixKing 这是真的吗?

标签: javascript oop scope


【解决方案1】:

关于 javascript 中的私有和受保护访问的最佳教程之一在这里:http://javascript.crockford.com/private.html

function Foo(a) {
    var bar = a;                              // private instance data

    this.getBar = function() {return(bar);}   // methods with access to private variable
    this.setBar = function(a) {bar = a;}
}

var x = new Foo(3);
var y = x.getBar();   // 3
x.setBar(12);
var z = x.bar;        // not allowed (x has no public property named "bar")

【讨论】:

  • 谁投了反对票,你能解释一下原因吗?这对我来说是正确的。 +1
  • 我没有,但在许多语言中,能够使用相同的参数名称而不是像“a”这样的名称非常好。也就是说,有一个名为“bar”的参数并将其分配给一个名为“bar”的变量
  • @GabrielLlamas - 我想你不明白这段代码的意义。如果 bar 是真正私有的,因此不能从外部访问(这是 OP 要求的),那么它必须像我所做的那样声明。而且,如果您像我一样声明bar,那么您将无法从原型上定义的方法访问它。你必须在bar 的范围内定义你的函数。是的,如果您要实例化大量 Foo() 对象,它的效率就不那么高了,但这是将 bar 设为私有的方法,这是被问到的问题。请删除您的反对票。
  • @GabrielLlamas - 这种方法没有不好的做法。这只是一个权衡。为了实现隐私,您可以在创建对象时接受微小的性能损失。一旦创建,对象就可以正常运行。原型是为了方便。没有理由你必须使用它来实现你的目标。如果您要创建大量 Foo() 对象并且性能至关重要,那么这将是一个糟糕的权衡。但是,如果您只创建一对或此方法的性能非常好,并且您想要可变隐私,则此方法是一个很好的做法。
  • @GabrielLlamas - 这种获取隐私的方法不是黑客攻击。它使用语言的支持特性(闭包)来解决最初没有设计到语言中的问题。这不是 hack - 这是一个创造性的解决方案,如果需要它提供的功能,使用它绝对没有错。
【解决方案2】:

你必须把所有需要访问私有变量的函数都放在构造函数中:

function Foo(bar)
{
  //bar is inside a closure now, only these functions can access it
  this.setBar = function() {bar = 5;}
  this.getBar = function() {return bar;}
  //Other functions
}

var myFoo = new Foo(5);
myFoo.bar;      //Undefined, cannot access variable closure
myFoo.getBar(); //Works, returns 5

【讨论】:

    【解决方案3】:
    function Foo(b)
    {
       var bar = b;
    
       this.setBar = function(x){
            bar = x;
       }
    
       this.alertBar = function(){
            alert(bar);
       }
    }
    
    var test = new Foo(10);
    alert(test.bar); // Alerts undefined
    test.alertBar(); // Alerts 10
    

    【讨论】:

    • +1 很好的例子。仍然对一件事感到困惑。是什么让barvar bar = b; 中持久存在?
    • 嗨@Gary,希望我能解释一下:每次构造new Foo 时,都会在Foo 的构造函数的函数范围内创建一个新的bar。只要Foo 存在于内存中,bar 就将存在于内存中(或对该Foo 中的任何函数的引用,它们都可以访问bar
    • 正如Foo 的每个实例都有自己的setBaralertBar,它也有自己的bar,事实上它也有自己的b。请参阅此 JSFiddle 以获得更简洁的版本:jsfiddle.net/fzXZ6/4
    【解决方案4】:

    我能想到的一种方法是使用一个分配给名称并返回一个新对象的闭包。您可以通过对闭包的调用将任何参数传递给构造函数。最终会是这样的:

    var fooFactory = function (a, b) {
        var c = 5,
            d = 6,
            foo;
    
        foo = function (a, b) {
            this.a = a;
            this.b = b;
            this.bar();
        }
    
        foo.prototype.bar = function () {
            //do something with c and d
            this.c = c + d;
        }
    
        foo.prototype.getC = function () {
            return c;
        }
    
        foo.prototype.getD = function () {
            return d;
        }
    
        return new foo(a, b);
    };
    

    这样,a 和 b 总是被唯一声明。然后你会像这样构造你的对象:

    var obj = fooFactory(1, 2);
    //obj contains new object: { a: 1, b: 2, c: 11 }
    
    console.log(obj.getC());
    //returns 5
    

    【讨论】:

    • 我投了这个答案,但后来我意识到这是错误的;每次调用“fooFactory”时,都会从头开始重新创建原型的所有方法。因此,与直接向对象添加方法相比,它没有任何优势,只会导致混乱。
    【解决方案5】:

    如果你愿意使用 ES2015 类,

    使用 ESNext,您可以像这样使用Javascript private variables

    class Foo {
      #bar = '';
      constructor(val){
          this.#bar = val;
      }
      otherFn(){
          console.log(this.#bar);
      }
    }
    

    在 Foo 类之外无法访问私有字段 #bar。

    【讨论】:

      【解决方案6】:

      在 ES6+ 术语中,执行此操作的正确方法如 @Nitin Jadhav's answer 所示。但是,如果出于某种原因您想坚持使用良好的旧构造函数,则可以像这样实现;

      function Foo(val){
        function Construct(){};
        Construct.prototype = { set bar(_){} // you may throw an error here
                              , get bar(){return val;}
                              };
        return new Construct();
      };
      

      所以这里发生了两件事。

      1. 您不会使用已接受答案中的属性填充 Foo 的实例。
      2. Private Class Fields 抽象不同,当有人尝试通过setter 访问私有变量时,您可以随意抛出错误。

      也许您想要实例本身的私有字段,而不是通过原型访问它。那么你可能会喜欢;

      function Foo(val){
        Object.defineProperty(this,"bar",{ set: function(){}
                                         , get: function(){return val;}
                                         });
      };
      

      【讨论】:

        【解决方案7】:

        我最近遇到了类似的问题,但也想使用访问器属性。下面是一个基于我提出的解决方案的 Foo(Bar) 示例。这个例子很简单,但可以在使用更复杂的 get/set 函数时轻松扩展。

        function Foo(Bar){
            Object.defineProperty(this,"bar",{get:function(){return Bar},set:function(val){Bar=val}});
        
        }
        x=new Foo(3);
        y=x.bar; //3
        x.bar++; //x.bar==4
        

        【讨论】:

        • @Gary Javascript 区分大小写。 Bar 和 bar 是两个独立的属性。 Bar 是 Foo 私有的,而 bar 是提供对 Bar 的访问的公共属性。这与接受的答案完成相同的事情。
        猜你喜欢
        • 2010-10-09
        • 2021-04-08
        • 2019-02-22
        • 1970-01-01
        • 1970-01-01
        • 2014-11-10
        • 1970-01-01
        • 1970-01-01
        • 2011-03-18
        相关资源
        最近更新 更多