【问题标题】:Create a class with IIFE that isn't a reference?使用不是参考的 IIFE 创建一个类?
【发布时间】:2013-11-21 14:26:12
【问题描述】:

我是 JavaScript 新手,我正试图用私有数据和公共函数创建“类”。有人告诉我立即调用函数表达式 (IIFE) 可以实现这一点,但是当我从类中“实例化”新对象时,它们会引用私有数据而不是拥有自己的数据。

其中一部分是从Create a JS class: IIFE vs return prototype借来的

例如,一个简单的汽车“类”:

var Car = (function() {

    var body = { color: 'red' };
    Car.prototype.newColor = function(color) {
            body.color = color;
        };
    Car.prototype.getColor = function() {
            return body.color;
        };

    return Car;
})();

var car1 = new Car();
var car2 = new Car();

car2 的颜色也变为紫色。

car1.newColor('purple');
car2.getColor(); // 'purple'

我希望 Car 类的每个对象都保存自己的私有数据。 IFFE如何做到这一点,或者有其他方法吗?

【问题讨论】:

标签: javascript class iife


【解决方案1】:

模拟私有实例变量的唯一方法是在构造函数中将它们声明为var myprivate

任何特权方法(=可以访问私有成员的方法)也必须在构造函数的主体中声明,因此不能在原型上(将花费您额外的 cpu 和内存,并且可能不会优化为在一些 JS 引擎中很好)。

我从来没有遇到过需要这样做的情况,因为在我看来,付出的代价不值得。通常向我未来的自己和其他程序员表明一个成员是私有的,通过广泛使用的命名约定(名称以下划线开头)_myPrivate

“Public override”的回答启发了我创建以下代码。私有实例成员可以通过 ben._data.set 公开访问,或者您可以重新实施规则和/或 getter/setter,以便有人仍然可以滥用它。它仍然可以清理您是对象的可公开访问的成员,并使其更易于使用 getter 和 setter。

//Namespacing DataStore to limit scope of the closures
var tools = {
  DataStore : function(){
    var store = [];
    this.get = function(key){
      return store[key];
    };
    this.set = function(key,value){
      store[key] = value;
      return value;
    };
  }
};
//Person constructor
var Person = function(name){
  //you can access this member directly
  // bob.name = "Lucy";
  this.name=name;
  //if having _data as not accesable by defining
  //  with var _data we whould have to define
  //  get and set here as this.get and this.set
  this._data=new tools.DataStore();
};
//constant value used to get or set, for example:
//ben.get(ben.AGE);
//Could add this and rules to Person instead of Person.prototype
//then you'll need a helper function to set up inheritance
//to make sure the static's on Person are copied to it's children
Person.prototype.AGE=0;
//rules for getters and setters
//Will be a problem with inheritance if on prototype 
//function Employee(name){Person.call(this,name);};
//Employee.prototype=Object.create(Person.prototype);
//Employee.prototype.rules["0set"]=..overwrites Person.prototype.rules["0set"]
//When inheriting you need to have a helper function set the rules for a child
//object
Person.rules = {}
//rule for AGE set
Person.rules[Person.prototype.AGE+"set"] = function(val){
  var tmp;
  tmp = parseInt(val);
  if(isNaN(tmp)){
    throw new Error("Cannot set the age of the person "+
      "to non number value, value of age:"+val);
  }
  if(tmp>150){
    throw new Error("Are you sure this is a person and "+
      "not a turtule? Trying to set age to:"+val);
  }
  return this._data.set(this.AGE,tmp);
};
//rule for age get
Person.rules[Person.prototype.AGE+"get"] = function(){
  return this._data.get(this.AGE);
};
Person.prototype.get = function(key){
  return Person.rules[key+"get"].call(this);
};
Person.prototype.set  = function(key,value){
  return Person.rules[key+"set"].call(this,value);
};

var ben = new Person("Ben");
ben.set(ben.AGE,22);
console.log(ben.get(ben.AGE));
try{
  ben.set(ben.AGE,151);
}catch(e){
  console.log("error",e);
}
try{
  ben.set(ben.AGE,"HELLO WORLD!");
}catch(e){
  console.log("error",e);
}

注意事项:Person.rules 需要从 Person 继承时复制到 Child 实例。

更多关于原型、继承、覆盖、调用超级、多重继承(混合)和this的值在这里:https://stackoverflow.com/a/16063711/1641941

【讨论】:

  • 您对stackoverflow.com/a/16063711/1641941 的回答确实有助于巩固类和私有变量。并动摇了我做出这个答案的决定。
  • @HMR 你应该得到疯狂的赞誉。我读过你的第三个答案,他们都启发了我。
【解决方案2】:

但是这样你每次创建对象时都定义.privilegedMethod(),并且每个对象都将持有不同版本的(相同目的)方法......

我想出的解决方案是使用对象到对象(私有)hashmap,并将新创建的对象映射到它在ctor函数中的相应数据,并使用hasmap作为“管理器”来确定哪些数据对应于哪个对象,在原型方法中,是这样的:

var Car = 
( function ( hashmap ) {

  function PrivateClassCarData ( c, t ) {
    this.color = c;
    this.type  = t;
  }

  function Car ( color, type ) {
    hashmap.place( this, new PrivateClassCarData( color, type ) );
  }

  // read
  Car.prototype.getColor =
  function () {
    return hashmap.read( this ).color;
  };

  // write
  Car.prototype.setColor =
  function (c) {
    hashmap.read( this ).color = c;
    return this;
  };

  // weak point, memory leak source
  // dereference object from hash-map before updating variable that points to it
  // another reference is kept in hashmap
  // @TODO, automatic dereferencing execution, anybody?
  Car.prototype.mfree =
  function () {
    hashmap.drop( this );
    return this;
  };

  return Car;

} )(

  // basic hash-map implementation
  // maps objects to objects
  ( function ( hk, hv ) {

    return {

      place : function ( objKey, objVal ) {
        hk.push( objKey );
        hv.push( objVal );
        return this;
      },

      read  : function ( objKey ) {
        return hv[ hk.indexOf( objKey ) ];
      },

      drop  : function ( objKey ) {
        var pos;
        ( ( pos = hk.indexOf( objKey ) ) != -1 )
        && (
          hk.splice( pos, 1 ),
          hv.splice( pos, 1 )
        );
        return this;
      }

    };

  } )( [], [] )

);

var c1 = new Car("red","ferrary");
var c2 = new Car("white","porche");

c1.getColor();
// red

c2.setColor("silver");

c1.getColor();
// red

c2.getColor();
// silver
//

【讨论】:

  • 关于弱点的评论很好。在创建跟踪已创建实例的静态方法时,我遇到了同样的问题。当您完成创建的实例或哈希图防止实例被垃圾收集时,您必须显式调用destroy 或在您的情况下为mfree。除了必须创建和销毁实例之外,我也找不到解决方案。 stackoverflow.com/a/19827617/1641941 你肯定会得到我的 +1,因为如果你有很多特权方法,这可能会起作用。
  • 是的,据我所知,没有办法在 JavaScript 中访问垃圾收集器的内部引用计数系统。
  • 第二个想法,如果你使用不同的方法,比如.data() 排序方法,使用它来操作私有数据存储,并且通过取消引用对象,JavaScript 能够识别由object 和 .data() api 并且当不再存在外部引用时它们会 gc 吗?我一得到想法就会发布。
【解决方案3】:
var Car = 
( function ( cardb ) {

  function Car ( color ) {

    // facing the same problem here
    // reinstaling .data() method for each created object
    // but this way each has its own data store object
    // and inner 1 to 1 circular reference js is able to deal with
    cardb( this );

    // set provided color parameter
    this.data("color", color);

  }

  return Car;

} )(

  // function to install .data() method to given object
  // it gets attached to object directly, instead of
  // attaching it to .prototype, in which case all
  // object will access same data store
  function ( obj ) {

    var _data = {};

    obj.data =
    function ( name, value ) {
      return arguments.length
       ? (
        ( value == null )
         ? _data[name]
         : (
           _data[name] = value,
           this
         )
       )
       : _data;
    };

    return obj;

  }
);

var c1 = new Car("red");
var c2 = new Car("blue");

c1.data("color");
// red

c2.data("color");
// blue

c1.data("color","white");

c2.data("color");
// blue
c1.data("color");
// white
//

【讨论】:

  • 很好,你可以在数据方法中实现getter和setter规则。我仍然会为浏览器运行代码使用_private,因为安全方面不是一个大问题。在我的 Web 应用程序中注入脚本的人不太可能滥用设置私有变量。其他程序员应该更清楚直接访问_privates。 JavaScript 并不总是在网页上使用,所以它会有用处。
猜你喜欢
  • 2012-10-24
  • 1970-01-01
  • 2015-08-19
  • 2012-10-21
  • 1970-01-01
  • 2020-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多