隐私的伪经典继承?

时间:2014-02-15 15:11:02

标签: javascript inheritance

JavaScript:好的部分中,Crockford认为使用伪经典继承模式的一个缺点是它公开暴露了实例变量。

例如:

var Ball = function(width, color) {
  this.width = width;
  this.color = color;
}

var redBall = new Ball(5, "red");
redBall.width = 12; // Changes width to 12

现在,如果我希望球的宽度是私密的呢?

这是我尝试过的:

var Ball = function(width, color) {
  this.getWidth = function() { return width; }
  this.color = color;
}

var redBall = new Ball(5, "red");

问题是我们仍然可以改变this.getWidth并且可能存在依赖它的原型方法。

怎么样......

var Ball = function(width, color) {
  return {
    getWidth: function() { return width; },
    color: color
  }
}

var redBall = new Ball(5, "red");

问题在于原型方法不再能够访问实例变量。它也更接近继承的功能模式,但使用new运算符的间接更多。

那么如何使用伪经典继承模式实现隐私?这甚至可能吗?

2 个答案:

答案 0 :(得分:4)

回答你的问题;嵌入特定私有成员的唯一方法是在同一范围内同时拥有成员和特权函数(可以访问它们的函数)。这意味着它们都必须位于构造函数体中(var我的私有... this.myPrivileged = function(){console.log(myPrivate ...)或带有闭包对象的IIFE跟踪{{3} }。

当私有对象返回时,您已经失去了隐私,因为调用代码可以改变您的私有值。为了防止这种情况,您必须深度复制该值并返回该值。

要在原型上拥有私有部分,将共享私有部分。这是因为在声明原型和私有成员时不知道实例。

这是因为JavaScript没有私有修饰符,只能通过闭包来模拟它们。

可以使用原型作为特定受保护变量的一种模式正在使用Crockford的框示例。

所有受保护的物品都放在一个只能用钥匙打开的盒子里,钥匙可以通过闭锁到IIFE中定义的所有原型成员。

因为在创建原型时不知道实例,所以必须从实例调用initProtecteds才能创建特定于实例的受保护成员。

在Animal中使用了名为medicalHistory的示例受保护实例成员的最小代码。

function makeBox(key){
  var ret = {};
  return {
    get : function(pKey){
      if(pKey===key){
        return ret;
      }
      return false;
    }
  }
};

var Person = function(args){
  args = args || {};
  this.name = args.name || "Nameless Person";
  this.initProtecteds();
};

//using IIFE to define some members on Person.prototype
//  these members and only these members have access to 
//  the passed object key (through closures)
//  later the key is used to create a box for each instance
//  all boxes use the same key so instances of same type
//  can access each other's protected members and instances
//  inheriting from Person can do so too, extending parent methods
//  will be trickier, no example for that is given in this code
(function(key){
  //private shared member
  var privateBehavior =  function(instance,args){
    //when you invoke this from public members you can pass
    //  the instance or use call/apply, when using call/apply
    //  you can refer to this as the current instance, when
    //  passing it as an argument then instance will 
    //  be the current instance
    console.log("private shared invoked");
  };
  //set default _protecteds to false so init knows
  //  it has not been initialised and needs to be shadowed
  //  with a box
  Person.prototype._protecteds=false;
  Person.prototype.getMedicalHistory = function(){
    //Maybe run some code that will check if you can access
    //  medical history, invoking a private method
    privateBehavior(this,{});
    var protectedObject  = this._protecteds.get(key);
    //if medicalHistory is an object the calling code
    //  can now mutate it
    return protectedObject.medicalHistory;
  };
  Person.prototype.hasSameDesease = function(person){
    //this Person instance should be able to see
    //  medical history of another Person instance
    return person._protecteds.get(key);
  };
  Person.prototype.getArr = function(){
    //Returns protecteds.get(key).arr so we can
    //  mutate it and see if protecteds are instance
    //  specific
    return this._protecteds.get(key).arr;
  };
  Person.prototype.initProtecteds =  function(){
    //only create box if it hasn't been created yet
    if(this._protecteds!==false)
      return;
    //use the same key for all instance boxes, one instance
    //  can now open another instance's box
    this._protecteds=makeBox(key);
    //retreive the object held by the box
    var protectedObject  = this._protecteds.get(key);
    //add protected members by mutating the object held
    //   by the box
    protectedObject.medicalHistory = "something";    
    protectedObject.arr = [];
    //protectedObject is no longer needed
    protectedObject=null;
  };
}({}));
var Animal = function(){
  this.initProtecteds();
};
(function(key){
  Animal.prototype._protecteds=false;
  Animal.prototype.initProtecteds =  function(){
    if(this._protecteds!==false)
      return;
    this._protecteds=makeBox(key);
    var protectedObject  = this._protecteds.get(key);
    protectedObject.medicalHistory = "something";    
  };
}({}));
var Employee = function(args){
  //re use Person constructor
  Person.call(this,args);
};
//set up prototype part of inheritance
Employee.prototype = Object.create(Person.prototype);
//repair prototype.constructor to point to the right function
Employee.prototype.constructor = Employee;

var ben = new Person({name:"Ben"});
var nameless = new Person();
console.log(ben.getMedicalHistory());//=something
//key is in closure and all privileged methods are in that closure
//  since {} !== {} you can't open the box unless you're in the closure
//  or modify the code/set a breakpoint and set window.key=key in the closure
console.log(ben._protecteds.get({}));//=false
//One Person instance can access another instance's protecteds
//  Objects that inherit from Person are same
console.log(ben.hasSameDesease(nameless));//=Object { medicalHistory="something"}
var lady = new Animal();
//An Animal type object cannot access a Person protected members
console.log(ben.hasSameDesease(lady));//=false
var jon = new Employee({name:"Jon"});
console.log(ben.hasSameDesease(jon));//=Object { medicalHistory="something"}
//making sure that protecteds are instance specific
ben.getArr().push("pushed in ben");
console.log(jon.getArr());
console.log(nameless.getArr());
console.log(ben.getArr());

答案 1 :(得分:2)

这很有趣。

对我来说(我认为自己是js的学生),看起来只有私有成员函数才能访问对象的私有变量。这是因为它在var:

周围创建了一个闭包
var Ball = function(width, color) {
   var width = width;
   this.color = color;
   this.getWidth=function(){return width}
   this.specialWidthCalc=function(x){ width = width + x;}
}

所以程序员可以这样做:

  var redBall = new Ball(5, "red");
  consoloe.log( redBall.getWidth() );

  redBall.specialWidthCalc(3);
  consoloe.log( redBall.getWidth() );

我无法创建可访问宽度的原型。