当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《从原型定义的函数访问私有成员变量》经验,为你挑选了8个好方法。

有没有办法制作"私有"变量(在构造函数中定义的变量),可用于原型定义的方法?

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

这有效:

t.nonProtoHello()

但这不是:

t.prototypeHello()

我习惯在构造函数中定义我的方法,但由于一些原因,我正在远离它.



1> Triptych..:

不,没有办法做到这一点.这基本上是反过来的范围.

构造函数中定义的方法可以访问私有变量,因为所有函数都可以访问定义它们的作用域.

在原型上定义的方法未在构造函数的范围内定义,并且无法访问构造函数的局部变量.

您仍然可以拥有私有变量,但是如果您希望在原型上定义的方法可以访问它们,则应该在this对象上定义getter和setter ,原型方法(以及其他所有内容)可以访问它们.例如:

function Person(name, secret) {
    // public
    this.name = name;

    // private
    var secret = secret;

    // public methods have access to private members
    this.setSecret = function(s) {
        secret = s;
    }

    this.getSecret = function() {
        return secret;
    }
}

// Must use getters/setters 
Person.prototype.spillSecret = function() { alert(this.getSecret()); };


"反向搜索范围"是带有"friend"关键字的C++功能.实际上任何函数都应该定义它的原型,因为它是它的朋友.可悲的是,这个概念是C++而不是JS :(
我没有看到这一点 - 你只是添加了一层没有做任何事情的抽象.你也可以把`secret`作为'this`的属性.JavaScript只是不支持带有原型的私有变量,因为原型绑定到调用站点上下文,而不是"创建站点"上下文.

2> Scott Rippey..:
更新:使用ES6,有一个更好的方法:

长话短说,你可以用new Symbol来创建私有字段.
这是一个很好的描述:https://curiosity-driven.org/private-properties-in-javascript

例:

var Person = (function() {
    // Only Person can access nameSymbol
    var nameSymbol = Symbol('name');

    function Person(name) {
        this[nameSymbol] = name;
    }

    Person.prototype.getName = function() {
        return this[nameSymbol];
    };

    return Person;
}());
对于所有使用ES5的现代浏览器:

您可以只使用Closures

构造对象的最简单方法是完全避免原型继承.只需在闭包中定义私有变量和公共函数,所有公共方法都可以私有访问变量.

或者你可以只使用Prototypes

在JavaScript中,原型继承主要是一种优化.它允许多个实例共享原型方法,而不是每个实例都有自己的方法.
缺点是,每次调用原型函数时this,唯一不同的是它.
因此,任何私人领域都必须是可以访问的this,这意味着它们将是公开的.所以我们坚持使用_private字段的命名约定.

不要把Closures和Prototypes混在一起

我认为你不应该把闭包变量和原型方法混在一起.你应该使用其中一个.

使用闭包访问私有变量时,原型方法无法访问变量.因此,您必须将闭包暴露在外this,这意味着您以某种方式公开它.这种方法几乎没有什么好处.

我该选哪个?

对于非常简单的对象,只需使用带闭包的普通对象即可.

如果你需要原型继承 - 继承,性能等 - 那么坚持使用"_private"命名约定,而不用担心闭包.

我不明白为什么JS开发人员努力使字段真正私有化.


遗憾的是,如果你想利用原型继承,`_private`命名约定仍然是最好的解决方案.
您链接的文章说:“ *符号类似于私人名称,但-与私人名称不同-它们**不能提供真正的隐私**。*”。实际上,如果您有实例,则可以使用Object.getOwnPropertySymbols来获取其符号。因此,这仅仅是默默无闻的隐私。
@Oriol是的,隐私是严重默默无闻的.仍然可以迭代符号,并通过`toString`推断符号的目的.这与Java或C#没有什么不同......私有成员仍可通过反射访问,但通常会被强烈模糊.这一切都强化了我的最后一点,"我不明白为什么JS开发人员努力使字段真正私密化."

3> Mims H. Wrig..:

当我读到这篇文章时,这听起来像是一个艰难的挑战,所以我决定想办法.我想出的是CRAAAAZY,但它完全有效.

首先,我尝试在立即函数中定义类,以便您可以访问该函数的某些私有属性.这有效并且允许您获取一些私有数据,但是,如果您尝试设置私有数据,您很快就会发现所有对象将共享相同的值.

var SharedPrivateClass = (function() { // use immediate function
    // our private data
    var private = "Default";

    // create the constructor
    function SharedPrivateClass() {}

    // add to the prototype
    SharedPrivateClass.prototype.getPrivate = function() {
        // It has access to private vars from the immediate function!
        return private;
    };

    SharedPrivateClass.prototype.setPrivate = function(value) {
        private = value;
    };

    return SharedPrivateClass;
})();

var a = new SharedPrivateClass();
console.log("a:", a.getPrivate()); // "a: Default"

var b = new SharedPrivateClass();
console.log("b:", b.getPrivate()); // "b: Default"

a.setPrivate("foo"); // a Sets private to "foo"
console.log("a:", a.getPrivate()); // "a: foo"
console.log("b:", b.getPrivate()); // oh no, b.getPrivate() is "foo"!

console.log(a.hasOwnProperty("getPrivate")); // false. belongs to the prototype
console.log(a.private); // undefined

// getPrivate() is only created once and instanceof still works
console.log(a.getPrivate === b.getPrivate);
console.log(a instanceof SharedPrivateClass);
console.log(b instanceof SharedPrivateClass);


4> Jason S..:

请参阅Doug Crockford的页面.您必须间接地使用可以访问私有变量范围的内容.

另一个例子:

Incrementer = function(init) {
  var counter = init || 0;  // "counter" is a private variable
  this._increment = function() { return counter++; }
  this._set = function(x) { counter = x; }
}
Incrementer.prototype.increment = function() { return this._increment(); }
Incrementer.prototype.set = function(x) { return this._set(x); }

用例:

js>i = new Incrementer(100);
[object Object]
js>i.increment()
100
js>i.increment()
101
js>i.increment()
102
js>i.increment()
103
js>i.set(-44)
js>i.increment()
-44
js>i.increment()
-43
js>i.increment()
-42


这个例子似乎是一种可怕的做法.使用原型方法的目的是让您不必为每个实例创建一个新方法.无论如何,你正在这样做.对于每种方法,您都要创建另一种方法.
为什么甚至打扰通过`set`暴露`_set`?为什么不直接将它命名为"set"?
@ArmedMonkey这个概念看起来很合理,但同意这是一个不好的例子,因为显示的原型函数很简单.如果原型函数需要更长的函数,需要对"私有"变量进行简单的获取/设置访问,那么这将是有意义的.

5> 小智..:

我建议将"在构造函数中进行原型赋值"描述为Javascript反模式可能是个好主意.想一想.风险太大了.

你在创建第二个对象(即b)时实际做的是为所有使用该原型的对象重新定义原型函数.这将有效地重置示例中对象a的值.如果你想要一个共享变量并且你碰巧事先创建了所有的对象实例,那么它会起作用,但它感觉风险太大了.

我发现最近我正在处理的一些Javascript中的一个错误是由于这种确切的反模式.它试图在正在创建的特定对象上设置拖放处理程序,而是为所有实例执行此操作.不好.

Doug Crockford的解决方案是最好的.



6> Tim..:

@Kai

那不行.如果你这样做

var t2 = new TestClass();

然后t2.prototypeHello将访问t的私人部分.

@AnglesCrimes

示例代码工作正常,但它实际上创建了一个由所有实例共享的"静态"私有成员.它可能不是摩根代码寻找的解决方案.

到目前为止,我还没有找到一种简单而干净的方法来实现这一点,而无需引入私有哈希和额外的清理功能.可以在一定程度上模拟私有成员函数:

(function() {
    function Foo() { ... }
    Foo.prototype.bar = function() {
       privateFoo.call(this, blah);
    };
    function privateFoo(blah) { 
        // scoped to the instance by passing this to call 
    }

    window.Foo = Foo;
}());



7> Edward..:

是的,这是可能的.PPF设计模式正好解决了这个问题.

PPF代表私有原型功能.基本PPF解决了这些问题:

    原型函数可以访问私有实例数据.

    原型功能可以是私有的.

首先,只需:

    将您想要从原型函数访问的所有私有实例变量放在一个单独的数据容器中,并且

    将对数据容器的引用作为参数传递给所有原型函数.

就这么简单.例如:

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y)
{
  // container for private vars: all private vars go here
  // we want x, y be changeable via methods only
  var data = new Data;
  data.x = x;
  data.y = y;

  ...
}

// Prototype functions now have access to private instance data
Point.prototype.getX = function(data)
{
  return data.x;
}

Point.prototype.getY = function(data)
{
  return data.y;
}

...

阅读完整的故事:

PPF设计模式


但是,如果在该网站之后的某个时间点发生故障,会发生什么?那么有人应该如何看待一个例子呢?该政策已经制定,以便链接中的任何有价值的东西都可以保留在这里,而不必依赖于我们无法控制的网站.
只有链接的答案通常不赞成SO.请举例说明.
@Edward,你的链接很有意思!但是,在我看来,使用原型函数访问私有数据的主要原因是防止每个对象浪费具有相同公共函数的内存.您描述的方法无法解决此问题,因为对于公共使用,原型函数需要包含在常规公共函数中.我想如果你有很多ppf组合在一个公共函数中,那么这个模式对于节省内存很有用.你用它们做别的吗?

8> 小智..:

您实际上可以通过使用访问者验证来实现:

(function(key, global) {
  // Creates a private data accessor function.
  function _(pData) {
    return function(aKey) {
      return aKey === key && pData;
    };
  }

  // Private data accessor verifier.  Verifies by making sure that the string
  // version of the function looks normal and that the toString function hasn't
  // been modified.  NOTE:  Verification can be duped if the rogue code replaces
  // Function.prototype.toString before this closure executes.
  function $(me) {
    if(me._ + '' == _asString && me._.toString === _toString) {
      return me._(key);
    }
  }
  var _asString = _({}) + '', _toString = _.toString;

  // Creates a Person class.
  var PersonPrototype = (global.Person = function(firstName, lastName) {
    this._ = _({
      firstName : firstName,
      lastName : lastName
    });
  }).prototype;
  PersonPrototype.getName = function() {
    var pData = $(this);
    return pData.firstName + ' ' + pData.lastName;
  };
  PersonPrototype.setFirstName = function(firstName) {
    var pData = $(this);
    pData.firstName = firstName;
    return this;
  };
  PersonPrototype.setLastName = function(lastName) {
    var pData = $(this);
    pData.lastName = lastName;
    return this;
  };
})({}, this);

var chris = new Person('Chris', 'West');
alert(chris.setFirstName('Christopher').setLastName('Webber').getName());

这个例子来自我关于原型函数和私有数据的帖子,并在此有更详细的解释。

推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有