Ответ 1
Чтобы ответить на ваш вопрос; единственный способ иметь конкретных частных членов intace - иметь как члены, так и привилегированные функции (функции, которые могут обращаться к ним) в той же области. Это означает, что все они должны находиться в теле конструктора (var my private... this.myPrivileged = function() {console.log(myPrivate...) или в IIFE с закрывающим объектом, отслеживающим экземпляры и их рядовые.
При возврате частного объекта вы уже потеряли конфиденциальность, потому что вызывающий код может изменить ваше личное значение. Чтобы предотвратить это, вам нужно глубоко скопировать значение и вернуть его.
Чтобы иметь приват на прототипе, рядовые будут разделены. Это потому, что экземпляр не известен при объявлении вашего прототипа и частных членов.
Это связано с тем, что JavaScript не имеет частного модификатора и только имитирует их путем закрытия.
Один шаблон, который может использовать прототип, например, для конкретных переменных protected, используется пример окна Crockford.
Все защищенные объекты помещаются в поле, которое может быть открыто только с помощью ключа, ключ доступен через закрытие всех элементов-прототипов, определенных в IIFE.
Поскольку экземпляр неизвестен при создании прототипа, вы должны вызывать initProtecteds из экземпляра, чтобы создавать особые защищенные члены экземпляра.
Минимальный код с примером защищенного члена экземпляра, называемого medicalHistory, используется в Animal.
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 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 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 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());