从原型定义的函数访问私有成员变量

199

有没有办法让“private”变量(在构造函数中定义)对使用原型定义的方法可用?

TestClass = function(){
    var privateField = "hello";
    this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};

这个有效:

t.nonProtoHello()

但这个不行:

t.prototypeHello()

我习惯在构造函数中定义我的方法,但出于几个原因而放弃了这种做法。


可能是如何创建可访问原型函数的私有变量?的重复问题。 - educampver
14
@ecampver,除了这个问题是 2 年提出的…… - Pacerier
25个回答

0
今天我遇到了完全相同的问题,在详细阐述了Scott Rippey的一流回答之后,我想出了一个非常简单的解决方案(在我看来),它既与ES5兼容又高效,而且还是名称冲突安全的(使用_private似乎不安全)。
/*jslint white: true, plusplus: true */

 /*global console */

var a, TestClass = (function(){
    "use strict";
    function PrefixedCounter (prefix) {
        var counter = 0;
        this.count = function () {
            return prefix + (++counter);
        };
    }
    var TestClass = (function(){
        var cls, pc = new PrefixedCounter("_TestClass_priv_")
        , privateField = pc.count()
        ;
        cls = function(){
            this[privateField] = "hello";
            this.nonProtoHello = function(){
                console.log(this[privateField]);
            };
        };
        cls.prototype.prototypeHello = function(){
            console.log(this[privateField]);
        };
        return cls;
    }());
    return TestClass;
}());

a = new TestClass();
a.nonProtoHello();
a.prototypeHello();

已经在 ringojs 和 nodejs 上进行了测试。我迫不及待地想读取您的意见。


这里有一个参考链接:请查看“更进一步”部分。https://philipwalton.com/articles/implementing-private-and-protected-members-in-javascript/ - jimasun

0

ES6 WeakMaps

通过使用基于ES6 WeakMaps的简单模式,可以获得从原型函数访问的私有成员变量

注意:使用WeakMaps可以保证防止内存泄漏,因为它允许垃圾回收器识别和丢弃未使用的实例。

// Create a private scope using an Immediately 
// Invoked Function Expression...
let Person = (function() {

    // Create the WeakMap that will hold each  
    // Instance collection's of private data
    let privateData = new WeakMap();
    
    // Declare the Constructor :
    function Person(name) {
        // Insert the private data in the WeakMap,
        // using 'this' as a unique acces Key
        privateData.set(this, { name: name });
    }
    
    // Declare a prototype method 
    Person.prototype.getName = function() {
        // Because 'privateData' is in the same 
        // scope, it's contents can be retrieved...
        // by using  again 'this' , as  the acces key 
        return privateData.get(this).name;
    };

    // return the Constructor
    return Person;
}());

这个模式的更详细解释可以在这里找到。


0

我知道这个问题已经被问了十多年,但是作为一名程序员,我又在第n次思考这个问题,并找到了一个可能的解决方案,但我还不确定是否完全喜欢它。我之前没有看到过这种方法的文档,所以我将其命名为“私有/公共美元模式”或_$ / $ 模式

var ownFunctionResult = this.$("functionName"[, arg1[, arg2 ...]]);
var ownFieldValue = this._$("fieldName"[, newValue]);

var objectFunctionResult = objectX.$("functionName"[, arg1[, arg2 ...]]);

//Throws an exception. objectX._$ is not defined
var objectFieldValue = objectX._$("fieldName"[, newValue]);

该概念使用一个ClassDefinition函数,返回一个Constructor函数,该函数返回一个Interface对象。接口的唯一方法是$,它接收一个name参数来调用构造函数对象中对应的函数,传递在name之后的任何其他参数都将在调用中传递。
全局定义的辅助函数ClassValues将所有字段存储在an对象中,根据需要定义_$函数以通过name访问它们。这遵循一个简短的get/set模式,因此如果传递了value,它将被用作新变量值。
var ClassValues = function (values) {
  return {
    _$: function _$(name, value) {
      if (arguments.length > 1) {
        values[name] = value;
      }

      return values[name];
    }
  };
};

全局定义的函数Interface接受一个对象和一个Values对象,返回一个带有一个单一函数$_interface,该函数检查obj以查找以参数name命名的函数,并使用values作为scoped对象调用它。传递给$的附加参数将传递给函数调用。

var Interface = function (obj, values, className) {
  var _interface = {
    $: function $(name) {
      if (typeof(obj[name]) === "function") {
        return obj[name].apply(values, Array.prototype.splice.call(arguments, 1));
      }

      throw className + "." + name + " is not a function.";
    }
  };

  //Give values access to the interface.
  values.$ = _interface.$;

  return _interface;
};

在下面的示例中,ClassX 被赋值为 ClassDefinition 的结果,而 ClassDefinitionConstructor 函数。 Constructor 可以接收任意数量的参数。 Interface 是在调用构造函数后外部代码获得的内容。
var ClassX = (function ClassDefinition () {
  var Constructor = function Constructor (valA) {
    return Interface(this, ClassValues({ valA: valA }), "ClassX");
  };

  Constructor.prototype.getValA = function getValA() {
    //private value access pattern to get current value.
    return this._$("valA");
  };

  Constructor.prototype.setValA = function setValA(valA) {
    //private value access pattern to set new value.
    this._$("valA", valA);
  };

  Constructor.prototype.isValAValid = function isValAValid(validMessage, invalidMessage) {
    //interface access pattern to call object function.
    var valA = this.$("getValA");

    //timesAccessed was not defined in constructor but can be added later...
    var timesAccessed = this._$("timesAccessed");

    if (timesAccessed) {
      timesAccessed = timesAccessed + 1;
    } else {
      timesAccessed = 1;
    }

    this._$("timesAccessed", timesAccessed);

    if (valA) {
      return "valA is " + validMessage + ".";
    }

    return "valA is " + invalidMessage + ".";
  };

  return Constructor;
}());

Constructor中没有非原型函数的意义,尽管您可以在构造函数体中定义它们。所有函数都使用公共美元模式this.$("functionName"[, param1[, param2 ...]])调用。私有值使用私有美元模式this._$("valueName"[, replacingValue]);访问。由于Interface没有_$的定义,因此外部对象无法访问这些值。由于每个原型函数体的this都设置为函数$中的values对象,因此如果直接调用Constructor兄弟函数,则会出现异常;在原型函数体中也需要遵循_$ / $模式。以下是示例用法。

var classX1 = new ClassX();
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
console.log("classX1.valA: " + classX1.$("getValA"));
classX1.$("setValA", "v1");
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
var classX2 = new ClassX("v2");
console.log("classX1.valA: " + classX1.$("getValA"));
console.log("classX2.valA: " + classX2.$("getValA"));
//This will throw an exception
//classX1._$("valA");

以及控制台输出。

classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2

_$/$模式允许在完全原型化的类中对值进行完全私有化。我不知道我是否会使用它,也不知道它是否存在缺陷,但是嘿,这是一个很好的难题!


-1

您需要更改代码中的三个地方:

  1. var privateField = "hello"替换为this.privateField = "hello"
  2. 在原型中用this.privateField替换privateField
  3. 在非原型中也用this.privateField替换privateField

最终代码应如下所示:

TestClass = function(){
    this.privateField = "hello";
    this.nonProtoHello = function(){alert(this.privateField)};
}

TestClass.prototype.prototypeHello = function(){alert(this.privateField)};

var t = new TestClass();

t.prototypeHello()

this.privateField would not be a private field. it is accessible from the outside: t.privateField - V. Rubinetti

-2

你可以在构造函数定义中使用原型赋值。

该变量将对原型添加的方法可见,但所有函数实例将访问相同的共享变量。

function A()
{
  var sharedVar = 0;
  this.local = "";

  A.prototype.increment = function(lval)
  {    
    if (lval) this.local = lval;    
    alert((++sharedVar) + " while this.p is still " + this.local);
  }
}

var a = new A();
var b = new A();    
a.increment("I belong to a");
b.increment("I belong to b");
a.increment();
b.increment();

希望这对你有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接