带有隐私的伪经典继承?

IT技术 javascript inheritance
2021-02-15 21:47:40

JavaScript: The Good Parts 中,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个回答

回答你的问题;拥有特定私有成员的唯一方法是让成员和特权函数(可以访问它们的函数)在同一范围内。这意味着它们都必须在构造函数主体中(var my private...this.myPrivileged=function(){ console.log (myPrivate...) 或在具有闭包对象的 IIFE 中,跟踪实例及其私有.

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

要在原型上拥有私有数据,私有数据将被共享。这是因为在声明您的原型和私有成员时不知道该实例。

这是因为 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());

考虑到这一点很有趣。

对我来说(我认为自己是 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() );

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

只是为了踢球,我试图再次为私人成员提出一种模式,但只能弄清楚如何实现受保护。用模式更新了我的答案。
2021-04-18 21:47:40
似乎有很多不必要的代码,但收益甚微。
2021-04-21 21:47:40
布莱恩,你说得对。也许您会发现以下有关构造函数和原型的有用信息stackoverflow.com/a/16063711/1641941没有私有模式
2021-04-26 21:47:40
我完全同意你的看法。还没有看到在 JavaScript 中使用私有或受保护的令人信服的理由。除了您的代码看起来更引人注目以及另一个module意外访问或使用您的module实现成员之外。
2021-04-30 21:47:40