Страницы

Поиск по вопросам

вторник, 26 ноября 2019 г.

Инкапсуляция JavaScript


Как достать инкапсулированное значение в прототип?

Как это выглядит:

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]; }

Комментариев нет:

Отправить комментарий