【问题标题】:Possible to access private constructor-scoped variables from a functions prototype?可以从函数原型访问私有构造函数范围的变量吗?
【发布时间】:2011-12-08 22:41:40
【问题描述】:

根据我对javascript的理解,原型方法不能访问构造函数作用域私有的变量,

 var Foo = function() {
      var myprivate = 'I am private';    
      this.mypublic = 'I am public';
 }

 Foo.prototype = {
     alertPublic: function() { alert(this.mypublic); } // will work
     alertPrivate: function() { alert(myprivate); } // won't work
 }

这很有意义,但是有什么安全且良好的做法可以解决这个问题吗?由于使用原型提供了性能优势,因为成员函数只分配一次,我希望实现类似的功能,同时仍然能够访问我的私有变量。我认为它不会通过使用原型来工作,但是是否有另一种模式,例如工厂方法或闭包方法?有点像,

var fooFactory = function() {
    var _alertPrivate = function(p) { alert(p); } // bulk of the logic goes here
    return function(args) {
         var foo = {}; 
         var myprivate = args.someVar; 
         foo.mypublic = args.someOtherVar; 
         foo.alertPrivate = function() { _alertPrivate(myprivate); };
         return foo; 
    }; 
}

var makeFoo = new fooFactory();
var foo = makeFoo(args); 

我不确定每次创建新 Foo 时是否会创建 _alertPrivate 的新副本,或者是否有任何潜在的性能优势。目的是获得类似于原型设计的功能(因为它可以节省内存),同时仍然能够访问私有变量。

谢谢。

【问题讨论】:

  • 你确定性能差异真的有问题吗?
  • @Qerub,可以。通常规则是,如果您需要创建大量实例,最好使用原型,因为每个实例都引用原型中仅创建一次的函数。我已经对其进行了基准测试,使用原型有显着的性能优势。想要启用继承还有一个明显的原因。
  • 在以下帖子的末尾是一个链接,用于创建受保护的成员,而无需在每次创建实例时创建大量闭包:stackoverflow.com/questions/16063394/…

标签: javascript constructor closures private prototype-programming


【解决方案1】:

我想出了以下模式来解决这个问题,至少现在是这样。我需要的是一个特权设置器,以便可以从某些原型函数内部更改私有变量,但不能从其他任何地方更改:

 var Foo = (function() {

    // the bulk of the objects behavior goes here and is created once 
    var functions = {
        update: function(a) {
             a['privateVar'] = "Private variable set from the prototype";
        }
    }; 

    // the objects prototype, also created once
    var proto = {
        Update: function() {
             this.caller('update'); 
        }
    };

    // special function to get private vars into scope
    var hoist = function(accessor) {
        return function(key) {
             return functions[key](accessor()); 
        }
    }

    // the constructor itself
    var foo = function foo() {
        var state = {
            privateVar: "Private variable set in constructor",
            // put more private vars here
        }
        this.caller = hoist(function(){
            return state;
        }); 
    }

    // assign the prototype
    foo.prototype = proto;

    // return the constructor
    return foo; 

 })(); 

基本上,一个指向对象内部状态的指针通过一个简单的访问器 function() { return state; 上的闭包被提升到它的原型。 }。在任何给定实例上使用“调用者”函数允许您调用仅创建一次但仍可以引用该实例中保存的私有状态的函数。同样重要的是要注意原型之外的任何函数都不能访问特权访问器,因为“调用者”只接受一个引用回范围内预定义函数的键。

以下是此方法的一些基准,以了解它与纯原型制作的比较。这些数字代表在一个循环中创建了 80,000 个对象实例(请注意,用于基准测试的对象比上面的对象更复杂,这只是为了简化目的):

铬:

仅关闭 - 2172ms

原型制作(以上方式) - 822ms

原型制作(标准方式) - 751ms

火狐:

仅关闭 - 1528 毫秒

原型制作(以上方式) - 971 毫秒

原型制作(标准方式) - 752ms

如您所见,该方法几乎与普通原型设计一样快,而且绝对比仅使用普通闭包(将函数与实例一起复制)要快。

【讨论】:

    【解决方案2】:

    我发现 Sean Thoman 的回答很有帮助(虽然一开始很难理解)。

    看起来公共 setter 不能接受 privateVar 的值,所以我做了一些调整:

    update 更改为functions

    update: function(st, newVal) {
         st['privateVar'] = newVal;
    }
    

    Update改成proto

    Update: function(newVal) {
         this.caller('update', newVal); 
    }
    

    更改hoist

    var hoist = function(accessor) {
        return function(key) {
            // we can't slice arguments directly because it's not a real array
            var args_tail = Array.prototype.slice.call(arguments, 1);
            return functions[key].apply(functions[key], [accessor()].concat(args_tail)); 
        }
    }
    

    【讨论】:

      【解决方案3】:

      您所要求的都是可能的,尽管在性能(速度或内存)和功能之间总是需要权衡。

      在 JavaScript 中,可以使用普通的原型方法(并且没有集中的、泄漏的、字段存储)来实现私有的每个实例状态。

      查看我写的关于该技术的文章:http://www.codeproject.com/KB/ajax/SafeFactoryPattern.aspx

      或者直接去源码中:https://github.com/dcleao/private-state

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-17
        • 2014-12-10
        • 2010-09-30
        • 1970-01-01
        • 2011-05-06
        • 2013-08-03
        • 1970-01-01
        相关资源
        最近更新 更多