Как достать инкапсулированное значение в прототип?
Как это выглядит:
function A(a,b){
this.a = a;
this.b = b;
var arr = [a,b];
}
A.prototype.A1 = function(){
console.log(this.a);
}
A.prototype.A2 = function(){
console.log(this.arr);
}
var a1 = new A(1,2);
a1.A1();//1
a1.A2();//undefined
Как использовать arr в прототипе?
Ответы
Ответ 1
arr в данном коде является локальной переменной.
Доступ к локальной переменной может быть осуществлен только внутри функции, в которой объявлена переменная.
Либо во внутренних функциях.
function A(a,b){
this.a = a;
this.b = b;
var arr = [a,b];
function B(){
console.log('B', arr); // доступно
}
console.log('A', arr); // доступно
}
A.prototype.C = function (){ /* arr недоступен */ }
Таким образом, использовать локальную переменную arr где-то еще кроме функции A - невозможно.
Ответ 2
Рекомендую использовать Symbol для реализации более-менее приватных членов "класса".
var A = (function() {
var arrKey = Symbol();
A.prototype.getArr = function() {
return this[arrKey];
};
return A;
function A(a, b) {
this.a = a;
this.b = b;
this[arrKey] = [a, b];
};
})();
var a1 = new A(1, 2);
console.log(a1.getArr()); // [1, 2]
Ответ 3
Ну если очень хочется, а условная приватность символов не устраивает, то можно так (при наличии WeakMap из ES6):
var Smth = (function () {
var privates = new WeakMap;
function Smth(x, y) {
this.x = x;
privates.set(this, {y: y});
}
Smth.prototype.doSmth = function () {
console.log(this.x + ' ' + privates.get(this).y);
};
return Smth;
})();
var a = new Smth(10, 13);
var b = new Smth(123, 167);
a.doSmth();
b.doSmth();
Ответ 4
Как один из вариантов, так:
function A(a,b){
this.a = a;
this.b = b;
var arr = [a,b];
A.prototype.getArr = function () {
return arr;
}
}
A.prototype.A1 = function(){
console.log(this.a);
};
A.prototype.A2 = function(){
console.log(A.prototype.getArr());
};
var a1 = new A(1,2);
a1.A1();//1
a1.A2();//[1, 2]
Дополнено:
Если с несколькими экземплярами, тогда можно так попробовать:
function A(a, b) {
this.a = a;
this.b = b;
var arr = [a, b];
this.xxx = arr;
}
A.prototype.A1 = function(){
console.log(this.a);
};
A.prototype.A2 = function(){
console.log(this.xxx);
};
var a1 = new A(1,2);
var a2 = new A(2,3);
var a3 = new A(3,4);
a1.A1();//1
a1.A2();//[1, 2]
a2.A2();//[2, 3]
a3.A2();//[3, 4]
Вообще, если предположить, что функцию A менять нельзя, то, видимо, никак не получить значение arr.
Ответ 5
Можно использовать "геттер" и достать значение так:
function A(a,b){
this.a = a;
this.b = b;
var arr = [a,b];
this.getArray = function () {
return arr;
}
}
A.prototype.A1 = function(){
console.log(this.a);
}
A.prototype.A2 = function(){
console.log(this.getArray());
}
var a1 = new A(1,2);
a1.A1();
a1.A2();
Ответ 6
Все переменные, объявленные внутри функции, являются приватными для неё. Доступ
ним может быть осуществлён только из самой функции и через замыкание из вложенных в неё.
Поскольку в нормальной реализации класса фукнции помещаются в прототип и существую
в единственном экземпляре, они не могут через замыкание получить доступ к инстансным переменным (функция одна, а инстансов много - функция не может "выбирать" замыкание в момент вызова).
Возможны следующие решения:
Классическое соглашение о подчёркивании в имени
Если имя начинается с подчёркивания, то по устному соглашению считается, что ег
не надо трогать. Естественно, на уровне кода это никакой приватности не обеспечивает.
Отказаться от методов в прототипе и помещать методы в экземпляры
Приватность таким образом обеспечивается, однако происходит создание большого числа однотипных функций, отличающихся только замыканиями.
Этот подход считается нерациональным, однако довольно распространён в реализаци
различных сервисов, которые по сути являются синглтонами.
Использовать Symbol в качестве ключа
Тип Symbol является особым типом ключа. Получить доступ к значению поля можно только имея сам символ, через который это значение было записано.
Это достаточно похоже на приватность, однако не даёт полную защиту от доступа, поскольку все символы-ключи для объекта можно получить через Object.getOwnPropertySymbols.
Symbol - это ES6. Полифилы для него генерируют обычное поле с достаточно рандомным ключом, чтобы исклчить пересечения с другими полями.
Использовать некий словарь в замыкании
Одно замыкание может быть общим для конструктора и всех прототипных методов. Но инстанс
надо как-то различать. Если поместить в это замыкание нечто, где инстанс является ключом, а значением является весь набор приватных полей, то мы получим полную настоящую приватность.
Однако, тут есть проблема. Если мы будем держать что-то типа массива, то оно буде
удерживать все наши объекты от сборки мусора, т. е. получается утечка памяти. Эту проблему решает введённый в ES6 WeakMap, который позволяет сборщику мусора уничтожать объекты, являющиеся его ключами.
Полифилом для WeakMap является помещение некого поля в сам объект и сохранение имени этого поля в карте. По сути, скатываемся к предыдущему способу.
Ответ 7
Вот написал я это всё и понял, что приватность-то всё ещё не совсем настоящая. Можн
подменить функцию private (можно было бы решить через readonly в defineProperty, но всё равно можно Object.create поверх и защита не сработает) и получить таким образом ключ, который ей дают, а дальше уже по этому ключу извлечь данные.
Всё-таки придумал способ получить полноценную приватность в ES5:
Но всё равно интересно. Может быть кто-то додумает эту идею до конца.
var Smth = (function () {
var PKEY = {}; // Объект-ключ для доступа к приватным данным, который
// доступен только в замыкании, т. к. нигде не отдаётся наружу
function Smth(x, y) {
this.x = x;
var privates = {y:y}; // Объект с приватными значениями, который
// отдаётся только через функцию this.private
// ... которая его не отдаст, если ей не дадут ключ
var me = this;
this.private = function (pkey) {
if (this && this !== window && this !== me) {
throw new Error("Do not call `private` on foreign object");
}
if (pkey === PKEY) {
return privates; // PKEY снаружи не знают, значит обращение идёт изнутри
}
throw new Error("Access to privates is denied");
}
}
Smth.prototype.doSmth = function () {
var privates = this.private(PKEY);
console.log(this.x + ' ' + privates.y);
};
return Smth;
})();
var a = new Smth(10, 13);
var b = new Smth(123, 167);
a.doSmth();
b.doSmth();
Но этот способ ещё нужно адаптировать для наследования, поскольку каждый наследни
затирает родительскую реализацию this.private, получается, что родительский класс н
теряет свой приватный объект. Чтобы этого не происходило, надо немного изменить реализацию метода, чтобы он прокидывал вызов к родительскому методу (ведь там тоже будет проверка собственного ключа). А исключение оставим последнему уровню, когда родительского метода уже нет.
В следующем коде в Smth заменена только функция
var Smth = (function () {
var PKEY = {}; // Объект-ключ для доступа к приватным данным, который
// доступен только в замыкании, т. к. нигде не отдаётся наружу
function Smth(x, y) {
var basePrivate = this.private; // Тут получится undefined
this.x = x;
var privates = {y:y}; // Объект с приватными значениями, который
// отдаётся только через функцию this.private
// ... которая его не отдаст, если ей не дадут ключ
var me = this;
this.private = function (pkey) {
if (this && this !== window && this !== me) {
throw new Error("Do not call `private` to foreign object");
}
if (pkey === PKEY) {
return privates; // PKEY снаружи не знают, значит обращение идёт изнутри
}
if (basePrivate) {
return basePrivate(pkey);
}
throw new Error("Access to privates is denied");
}
}
Smth.prototype.doSmth = function () {
var privates = this.private(PKEY);
console.log(this.x + ' ' + privates.y);
};
return Smth;
})();
var Derived = (function () {
var PKEY = {}; // Объект-ключ для доступа к приватным данным, который
// доступен только в замыкании, т. к. нигде не отдаётся наружу
function Derived(x, y, a, b) {
Smth.call(this, x, y);
var basePrivate = this.private; // Это функция - она не вызывается тут
this.a = a;
var privates = {b:b}; // Объект с приватными значениями, который
// отдаётся только через функцию this.private
// ... которая его не отдаст, если ей не дадут ключ
var me = this;
this.private = function (pkey) {
if (this && this !== window && this !== me) {
throw new Error("Do not call `private` to foreign object");
}
if (pkey === PKEY) {
return privates; // PKEY снаружи не знают, значит обращение идёт изнутри
}
if (basePrivate) {
return basePrivate(pkey);
}
throw new Error("Access to privates is denied");
}
}
Derived.prototype = Object.create(Smth.prototype);
Derived.prototype.doOtherThing = function () {
var privates = this.private(PKEY);
console.log(this.x + ' ' + this.a + ' ' + privates.b);
};
return Derived;
})();
var a = new Derived(10, 13, -8, -67);
var b = new Derived(123, 167, -411, -77);
a.doSmth();
a.doOtherThing();
b.doSmth();
b.doOtherThing();
Ответ 8
arr объявляется только в function A. Чтобы "внедрить" arr в прототип, вам надо написать:
function A(a,b){
this.a = a;
this.b = b;
this.arr = [a,b];
}
Комментариев нет:
Отправить комментарий