Чтобы создать класс JavaScript с общедоступным методом, я бы сделал что-то вроде:
function Restaurant() {}
Restaurant.prototype.buy_food = function(){
// something here
}
Restaurant.prototype.use_restroom = function(){
// something here
}
Таким образом пользователи моего класса могут:
var restaurant = new Restaurant();
restaurant.buy_food();
restaurant.use_restroom();
Как создать частный метод, который может вызываться методами buy_food и use_restroom, но не извне пользователями класса?
Другими словами, я хочу, чтобы моя реализация метода могла:
Restaurant.prototype.use_restroom = function() {
this.private_stuff();
}
Но это не должно работать:
var r = new Restaurant();
r.private_stuff();
Как определить private_stuff как частный метод, чтобы выполнялись оба условия?
Я читал Запись Дуга Крокфорда несколько раз, но не похоже, что «частные» методы могут быть вызваны общедоступными методами, а «привилегированные» методы могут быть вызваны извне.
См. этот ответ для чистого и простого решения «класса» с частным и общедоступным интерфейсом и поддержкой композиции.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы можете это сделать, но недостатком является то, что он не может быть частью прототипа:
function Restaurant() {
var myPrivateVar;
var private_stuff = function() { // Only visible inside Restaurant()
myPrivateVar = "I can set this here!";
}
this.use_restroom = function() { // use_restroom is visible to all
private_stuff();
}
this.buy_food = function() { // buy_food is visible to all
private_stuff();
}
}
Скрытие тонких материалов внутри крышки не гарантирует конфиденциальность для всех переводчиков. См. code.google.com/p/google-caja/wiki/…
@mikesamuel - правда, но только когда в этих интерпретаторах есть ошибки :)
Это, конечно, частный метод, но он, как правило, использует намного больше памяти, чем обычный метод-прототип, особенно если вы создаете много таких объектов. Для каждого экземпляра объекта создается отдельная функция, привязанная к объекту, а не к классу. Кроме того, сборщик мусора не производится до тех пор, пока сам объект не будет уничтожен.
Если вы сделаете объект McDonalds () унаследованным от Restaurant (), McDonalds не сможет переопределить частные методы, если вы объявите их таким образом. Что ж, на самом деле вы можете, но это не сработает, если какой-то другой метод вызывает частный, он вызовет исходную версию метода, и вы также не сможете вызвать родительский метод. Пока я не нашел хорошего способа объявить частные методы, которые хорошо работают с наследованием. Это, а также влияние на производительность делают этот шаблон проектирования не очень хорошим. Я бы порекомендовал сделать какой-то префикс для обозначения частных методов, например, начиная с подчеркивания.
Частные методы не должны переопределяться - они частные.
Если вам нужны частные методы (не частные данные), просто объявите их вне прототипа и назовите их следующим образом: myFoo.apply (this, args). Тогда вы можете оставить общедоступный api маленьким, сохранив при этом частные методы, работающие с вашим локальным объектом.
А что, если у меня есть частная функция внутри другой функции, которая была добавлена в прототип, например. Thing.prototype.public = function() { var private = function() {...}; private(); }; Это все еще нерациональное использование памяти?
Пожалуйста, подумайте об использовании самозапускающаяся функция + вызов, как описано здесь: stackoverflow.com/a/25172901/1480391
eval("Restaurant = " + Restaurant.toString().replace( /{/, "{ this.eval = function(code) { return eval(code); }; " )); var r = new Restaurant(); r.eval("myPrivateVar = \"I can set this too!\"");
Если вы приедете сюда в 2019 году: это действительно плохой путь. Эти функции не только частные, но и СТАТИЧЕСКИЕ. См. Способ самостоятельного вызова функции
@Arindam, если вы используете только один объект, я думаю, он не использует больше памяти, чем другие решения? Я думаю, это самое простое и удобочитаемое решение, не нужно использовать "странные" JavaScript-штуки вроде prototype или call.
@MikeSamuel, тогда просто сделай delete window.eval;, и тогда все в порядке.
@Sapphire_Brick Это только запрещает доступ к eval коду, который еще не запущен, и нарушает работу кода, который требует eval для работы. Загруженные модули могут захватывать eval и удерживать его. Я написал это в 2008 году, и современные интерпретаторы этого уже не делают.
@MikeSamuel Если предположить, что никто не зависит от eval, в чем проблема?
@Sapphire_Brick. Я не принимаю это предположение. Есть очень широко используемые модули, которые используют eval для метапрограммирования.
ну, они не должны использовать eval, и хорошо, что мы не говорим о метапрограммировании. Кроме того, мы можем поместить оператор delete window.eval в нижнюю часть кода, чтобы вы по-прежнему могли выполнять свое злое метапрограммирование в верхней части кода и иметь инкапсуляцию в нижней части.
Почти ... 1) У вас по-прежнему не может быть двух экземпляров с собственными копиями myPrivateVar. 2) Общедоступным методам должен быть разрешен доступ к myPrivateVar. В этой реализации вы можете получить доступ к частным данным только из частных методов.
Вы можете смоделировать частные методы следующим образом:
function Restaurant() {
}
Restaurant.prototype = (function() {
var private_stuff = function() {
// Private code here
};
return {
constructor:Restaurant,
use_restroom:function() {
private_stuff();
}
};
})();
var r = new Restaurant();
// This will work:
r.use_restroom();
// This will cause an error:
r.private_stuff();
Подробнее об этой технике здесь: http://webreflection.blogspot.com/2008/04/natural-javascript-private-methods.html
Я бы также предложил сайт Дугласа Крокфорда в качестве ресурса по частным / общедоступным методам и членам javascript.crockford.com/private.html
Он упомянул эту ссылку в вопросе.
Обратной стороной этого метода является то, что у вас не может быть доступа private_stuff () к другим частным данным в ресторане, а другие методы ресторана не могут вызывать private_stuff (). Положительным моментом является то, что если вам не нужно ни одно из условий, о которых я только что упомянул, вы можете сохранить use_restroom () в прототипе.
Мне также не нравится многословие этого подхода, поэтому, вероятно, я не буду использовать его в общем случае. Это изящно, как он добавляется в прототип, и в JS обычно производительность побеждает все остальное.
Это должно быть решение и принятый ответ, потому что автор явно использует свойство prototype.
Что насчет использования call, тогда частные методы смогут получить доступ к закрытым членам. private_stuff.call (это)
В соответствии с шаблоном, предложенным @georgebrock, частные данные все будут совместно использоваться среди объектов ресторана все. Это похоже на статические частные переменные и функции в ООП на основе классов. Я предполагаю, что OP этого хочет. Чтобы проиллюстрировать, что я имею в виду, я создал jsFiddle.
@feklee: общедоступные методы определяются как свойства объекта-прототипа, который используется многими экземплярами. Здесь мы делаем то же самое с частными методами: единый общий объект function, доступный для всех экземпляров. Вы правы, указывая, что этот метод не работает для частных данных, но вопрос не об этом.
@georgebrock: Судя по ответу, принятому OP, он действительно хочет иметь закрытых членов за объект, а не в объекте-прототипе. Публикуемый им вариант использования также предполагает это. Состояние туалета, например, обычно соответствует ресторану.
Вам нужна деталь constructor:Restaurant? Когда я устанавливаю прототип конструктора, я обычно делаю, например, Restaurant.prototype = {};, и вроде работает. Я не понимаю, чем будет отличаться Restaurant.prototype = (function () { return {}; })();.
Как это будет работать с наследованием? Например, вы не можете установить Restaurant.prototype на Object.create(House.prototype), поскольку он уже установлен.
@ PaulD.Waite: хороший вопрос. В этом примере конструктор - пустая функция, но что, если это не так? Кажется, что отсутствие части constructor: Restaurant не мешает правильно связать свойство конструктора с функцией Restaurant. Разве это не должно быть undefined?
При таком подходе «частные» методы не могут быть вызваны из конструктора. Мне это кажется серьезным ограничением.
@feklee i обновил свою скрипку, чтобы показать, как использовать свойство члена вместо внутри прототипа.
@ NicolasLeThierryd'Ennequin Чтобы получить доступ к закрытым методам в конструкторе, вы можете явно определить открытый конструктор прототипа и вызвать его из реального конструктора: function Restaurant() { this.constructor(); } и constructor: function(){ private_stuff(); }
В этих ситуациях, когда у вас есть общедоступный API, и вам нужны частные и общедоступные методы / свойства, я всегда использую шаблон модуля. Этот шаблон стал популярным в библиотеке YUI, подробности можно найти здесь:
http://yuiblog.com/blog/2007/06/12/module-pattern/
Это действительно просто и легко понять другим разработчикам. Для простого примера:
var MYLIB = function() {
var aPrivateProperty = true;
var aPrivateMethod = function() {
// some code here...
};
return {
aPublicMethod : function() {
aPrivateMethod(); // okay
// some code here...
},
aPublicProperty : true
};
}();
MYLIB.aPrivateMethod() // not okay
MYLIB.aPublicMethod() // okay
такие вещи не будут обнаружены автозаполнением IDE :(
Но это не класс, поэтому у вас не может быть 2 «экземпляров» этого класса с разными состояниями.
удалите часть (), и у вас будет «класс». по крайней мере, где вы можете создавать экземпляры разных isntances с разными состояниями. хотя шаблон модуля довольно интенсивно использует память ...
@DevAntoine Посмотрите комментарии к ответу 17 из 26. В JavaScript расширяемые классы и частные методы нелегко идут рука об руку. В этом случае я предлагаю отдать предпочтение композиции, а не наследованию. Создайте расширяемый прототип с помощью тех же методов, что и закрытый конкретный объект. Затем внутри вашего прототипа вы можете решить, когда вызывать методы вашего конкретного объекта.
Есть ли недостатки в вызове общедоступных переменных из частных функций, например aPrivateMethod = function() { MYLIB.aPublicProperty}?
Это не позволяет частным методам получать доступ к общедоступным методам.
Апофеоз паттерна модуля: Выявление паттерна модуля
Небольшое аккуратное продолжение очень прочного узора.
Все это закрытие будет стоить вам. Убедитесь, что вы проверили влияние скорости, особенно в IE. Вы обнаружите, что лучше использовать соглашение об именах. Есть еще много корпоративных веб-пользователей, которые вынуждены использовать IE6 ...
Те 9%, которые все еще используют IE6, не заботятся о скорости, оптимизации и всех современных функциях HTML5. Так что закрытие ничего не будет стоить.
Сейчас 0,5% (август 2012 г.) w3schools.com/browsers/browsers_explorer.asp
@LorenzoPolidori пользователи w3schools! == корпоративные веб-пользователи;]
Соглашение об именах (например, добавление символа подчеркивания) - это правильный путь. Код легче поддерживать, а методы все еще определены в прототипе. Однако в настоящее время ... я просто помечаю этот метод как частный в машинописном тексте.
Если вам нужен полный набор общедоступных и частных функций с возможностью для общедоступных функций доступ к частным функциям, разметьте код для объекта, подобного этому:
function MyObject(arg1, arg2, ...) {
//constructor code using constructor arguments...
//create/access public variables as
// this.var1 = foo;
//private variables
var v1;
var v2;
//private functions
function privateOne() {
}
function privateTwon() {
}
//public functions
MyObject.prototype.publicOne = function () {
};
MyObject.prototype.publicTwo = function () {
};
}
Может кто-нибудь сказать мне, почему это было отклонено? Мне нравится.
Каждый раз, когда вы делаете new MyObject, прототип MyObject заменяется теми же значениями.
-1. Никогда не назначайте .prototype внутри конструктора.
Я придумал это: Обновлено: На самом деле кто-то связался с идентичным решением. Ага!
var Car = function() {
}
Car.prototype = (function() {
var hotWire = function() {
// Private code *with* access to public properties through 'this'
alert( this.drive() ); // Alerts 'Vroom!'
}
return {
steal: function() {
hotWire.call( this ); // Call a private method
},
drive: function() {
return 'Vroom!';
}
};
})();
var getAwayVechile = new Car();
hotWire(); // Not allowed
getAwayVechile.hotWire(); // Not allowed
getAwayVechile.steal(); // Alerts 'Vroom!'
Это хороший метод, но как бы вы разрешили параметры в своем конструкторе? Например, var getAwayVehicle = new Car(100);, где 100 - это скорость, и вы хотите предупреждать о скорости. Спасибо!
Разобрался, может var Car = function(speed) { this.speed = speed; } и `return {constructor: Car, ...`
var TestClass = function( ) {
var privateProperty = 42;
function privateMethod( ) {
alert( "privateMethod, " + privateProperty );
}
this.public = {
constructor: TestClass,
publicProperty: 88,
publicMethod: function( ) {
alert( "publicMethod" );
privateMethod( );
}
};
};
TestClass.prototype = new TestClass( ).public;
var myTestClass = new TestClass( );
alert( myTestClass.publicProperty );
myTestClass.publicMethod( );
alert( myTestClass.privateMethod || "no privateMethod" );
Подобно georgebrock, но немного менее многословно (IMHO) Есть проблемы с тем, чтобы сделать это таким образом? (Нигде не видел)
edit: Я понял, что это бесполезно, поскольку каждый независимый экземпляр имеет свою собственную копию общедоступных методов, что подрывает использование прототипа.
Частные функции не могут получить доступ к общедоступным переменным с использованием шаблона модуля
Вот класс, который я создал, чтобы понять, что предложил Дуглас Крокфорд на своем сайте Частные члены в JavaScript
function Employee(id, name) { //Constructor
//Public member variables
this.id = id;
this.name = name;
//Private member variables
var fName;
var lName;
var that = this;
//By convention, we create a private variable 'that'. This is used to
//make the object available to the private methods.
//Private function
function setFName(pfname) {
fName = pfname;
alert('setFName called');
}
//Privileged function
this.setLName = function (plName, pfname) {
lName = plName; //Has access to private variables
setFName(pfname); //Has access to private function
alert('setLName called ' + this.id); //Has access to member variables
}
//Another privileged member has access to both member variables and private variables
//Note access of this.dataOfBirth created by public member setDateOfBirth
this.toString = function () {
return 'toString called ' + this.id + ' ' + this.name + ' ' + fName + ' ' + lName + ' ' + this.dataOfBirth;
}
}
//Public function has access to member variable and can create on too but does not have access to private variable
Employee.prototype.setDateOfBirth = function (dob) {
alert('setDateOfBirth called ' + this.id);
this.dataOfBirth = dob; //Creates new public member note this is accessed by toString
//alert(fName); //Does not have access to private member
}
$(document).ready()
{
var employee = new Employee(5, 'Shyam'); //Create a new object and initialize it with constructor
employee.setLName('Bhaskar', 'Ram'); //Call privileged function
employee.setDateOfBirth('1/1/2000'); //Call public function
employee.id = 9; //Set up member value
//employee.setFName('Ram'); //can not call Private Privileged method
alert(employee.toString()); //See the changed object
}
The that = this - довольно распространенный шаблон, популяризированный вышеупомянутым Крокфордом в его книге "Javascript: хорошие части"
that используется вместо this, чтобы избежать проблем с областью видимости, когда функции привязаны к другому объекту. Здесь вы сохраняете this в that и никогда не используете его снова, что равносильно тому, что вы не делаете этого вообще. Вы должны заменить this на that во всех внутренних функциях Constructor (а не в объявлении методов). Если employee каким-то образом связан с apply или call, эти методы могут вызвать ошибку, поскольку this будет неправильно привязан.
Кроме того, у каждого экземпляра будет полная копия частных функций, что неэффективно. Это вдобавок к тому факту, что общедоступные методы не могут получить доступ к частным варам классов, заставляет меня переключиться на дротик. К сожалению, angulardart - супер бета.
Поскольку каждый размещал здесь свой код, я тоже сделаю это ...
Мне нравится Крокфорд, потому что он ввел в Javascript настоящие объектно-ориентированные шаблоны. Но он также придумал новое недоразумение, «то».
Так почему он использует «это = это»? Это вообще не имеет ничего общего с частными функциями. Это связано с внутренними функциями!
Потому что, по словам Крокфорда, это ошибочный код:
Function Foo( ) {
this.bar = 0;
var foobar=function( ) {
alert(this.bar);
}
}
Поэтому он предложил сделать следующее:
Function Foo( ) {
this.bar = 0;
that = this;
var foobar=function( ) {
alert(that.bar);
}
}
Итак, как я уже сказал, я совершенно уверен, что Крокфорд ошибался в своих объяснениях по поводу того и этого (но его код определенно верен). Или он просто обманул мир Javascript, чтобы узнать, кто копирует его код? Я не знаю ... Я не фанат браузеров; D
РЕДАКТИРОВАТЬ
Ах, вот в чем все суть: Что означает 'var that = this;' имеете в виду в JavaScript?
Итак, Кроки действительно ошибся в своем объяснении ... но правильно в своем коде, так что он все еще отличный парень. :))
В общем, я временно добавил к объекту частный объект _. Вы должны открыть приватность только в «Power-конструкторе» для метода. Если вы вызовете метод из прототипа, вы иметь возможность перезаписать метод-прототип
Сделать общедоступный метод доступным в «Power-конструкторе»: (ctx - контекст объекта)
ctx.test = GD.Fabric.open('test', GD.Test.prototype, ctx, _); // is a private object
Теперь у меня есть openPrivacy:
GD.Fabric.openPrivacy = function(func, clss, ctx, _) {
return function() {
ctx._ = _;
var res = clss[func].apply(ctx, arguments);
ctx._ = null;
return res;
};
};
Вы должны закрыть свою фактическую функцию-конструктор, где вы можете определить свои частные методы. Чтобы изменить данные экземпляров с помощью этих частных методов, вы должны передать им «this» вместе с ними либо в качестве аргумента функции, либо путем вызова этой функции с помощью .apply (this):
var Restaurant = (function(){
var private_buy_food = function(that){
that.data.soldFood = true;
}
var private_take_a_shit = function(){
this.data.isdirty = true;
}
// New Closure
function restaurant()
{
this.data = {
isdirty : false,
soldFood: false,
};
}
restaurant.prototype.buy_food = function()
{
private_buy_food(this);
}
restaurant.prototype.use_restroom = function()
{
private_take_a_shit.call(this);
}
return restaurant;
})()
// TEST:
var McDonalds = new Restaurant();
McDonalds.buy_food();
McDonalds.use_restroom();
console.info(McDonalds);
console.info(McDonalds.__proto__);
На самом деле это не работает. Каждый new Restaurant будет иметь свой собственный конструктор restaurant, и «прототипом» полностью злоупотребляют.
@Bergi. Собственно, ты прав. Это сработало бы, но также потребовало бы ресурсов (это так называется?). Я отредактировал свой ответ по этому поводу.
Спасибо за обновления. Не знаю, как назвать предыдущую версию (но "баг" :-)
Я думаю, что такие вопросы возникают снова и снова из-за непонимания закрытий. Скрытность - это самое главное в JS. Каждый JS-программист должен почувствовать его суть.
1. Прежде всего нам нужно сделать отдельную область видимости (замыкание).
function () {
}
2. В этой области мы можем делать все, что захотим. И никто об этом не узнает.
function () {
var name,
secretSkills = {
pizza: function () { return new Pizza() },
sushi: function () { return new Sushi() }
}
function Restaurant(_name) {
name = _name
}
Restaurant.prototype.getFood = function (name) {
return name in secretSkills ? secretSkills[name]() : null
}
}
3. Чтобы мир узнал о нашем ресторанном классе, мы должны вернуть его из укупорки.
var Restaurant = (function () {
// Restaurant definition
return Restaurant
})()
4. В итоге имеем:
var Restaurant = (function () {
var name,
secretSkills = {
pizza: function () { return new Pizza() },
sushi: function () { return new Sushi() }
}
function Restaurant(_name) {
name = _name
}
Restaurant.prototype.getFood = function (name) {
return name in secretSkills ? secretSkills[name]() : null
}
return Restaurant
})()
5. Кроме того, у этого подхода есть потенциал для наследования и создания шаблонов.
// Abstract class
function AbstractRestaurant(skills) {
var name
function Restaurant(_name) {
name = _name
}
Restaurant.prototype.getFood = function (name) {
return skills && name in skills ? skills[name]() : null
}
return Restaurant
}
// Concrete classes
SushiRestaurant = AbstractRestaurant({
sushi: function() { return new Sushi() }
})
PizzaRestaurant = AbstractRestaurant({
pizza: function() { return new Pizza() }
})
var r1 = new SushiRestaurant('Yo! Sushi'),
r2 = new PizzaRestaurant('Dominos Pizza')
r1.getFood('sushi')
r2.getFood('pizza')
Надеюсь, это поможет кому-то лучше понять эту тему
То, что вы имеете в пункте 4., просто великолепно! Я думаю, что это единственный ответ из всех здесь, где вы получаете прирост производительности / памяти при использовании методов в прототипе, И эти общедоступные методы имеют полный доступ к частным членам. +1
Не работает. Переменная name здесь действует как статическая переменная и используется всеми экземплярами Restaurant. Вот jsbin: jsbin.com/oqewUWa/2/edit?js,output
это хорошая попытка, но, как заметил Шитал, переменная имени содержит ошибки.
добавление сюда моего 2c, чтобы подтвердить, что это не работает, выглядит красиво, но, как указано выше, "name" будет служить статической переменной, то есть совместно используемой для всех экземпляров
Вот что я разработал:
Требуется один класс кода сахара, который вы можете найти здесь. Также поддерживает защищенный, наследование, виртуальный, статический материал ...
;( function class_Restaurant( namespace )
{
'use strict';
if ( namespace[ "Restaurant" ] ) return // protect against double inclusions
namespace.Restaurant = Restaurant
var Static = TidBits.OoJs.setupClass( namespace, "Restaurant" )
// constructor
//
function Restaurant()
{
this.toilets = 3
this.Private( private_stuff )
return this.Public( buy_food, use_restroom )
}
function private_stuff(){ console.info( "There are", this.toilets, "toilets available") }
function buy_food (){ return "food" }
function use_restroom (){ this.private_stuff() }
})( window )
var chinese = new Restaurant
console.info( chinese.buy_food() ); // output: food
console.info( chinese.use_restroom() ); // output: There are 3 toilets available
console.info( chinese.toilets ); // output: undefined
console.info( chinese.private_stuff() ); // output: undefined
// and throws: TypeError: Object #<Restaurant> has no method 'private_stuff'
Как насчет этого?
var Restaurant = (function() {
var _id = 0;
var privateVars = [];
function Restaurant(name) {
this.id = ++_id;
this.name = name;
privateVars[this.id] = {
cooked: []
};
}
Restaurant.prototype.cook = function (food) {
privateVars[this.id].cooked.push(food);
}
return Restaurant;
})();
Поиск частных переменных невозможен за пределами непосредственной функции. Нет дублирования функций, экономия памяти.
Обратной стороной является то, что поиск частных переменных неудобен. privateVars[this.id].cooked нелепо набирать. Также есть дополнительная переменная id.
Это оставит Restaurant как undefined, потому что вы ничего не возвращаете из анонимной функции.
Где и как? Предполагая, что ссылка на созданный Ресторан утеряна, privateVars не будет иметь ссылки на своего владельца. Контрольный граф ациклический. Что мне не хватает?
На самом деле это единственный ответ, который поддерживает частные свойства помимо методов. Только две проблемы уже отмечены в ответе.
Я вижу утечку памяти: когда экземпляр Restaurant был обработан сборщиком мусора, его значения остаются в пределах privateVars. В этом случае WeakMap может стать хорошей заменой Array.
Class({
Namespace:ABC,
Name:"ClassL2",
Bases:[ABC.ClassTop],
Private:{
m_var:2
},
Protected:{
proval:2,
fight:Property(function(){
this.m_var--;
console.info("ClassL2::fight (m_var)" +this.m_var);
},[Property.Type.Virtual])
},
Public:{
Fight:function(){
console.info("ClassL2::Fight (m_var)"+this.m_var);
this.fight();
}
}
});
Я создал новый инструмент, который позволит вам иметь настоящие частные методы на прототипе. https://github.com/TremayneChrist/ProtectJS
Пример:
var MyObject = (function () {
// Create the object
function MyObject() {}
// Add methods to the prototype
MyObject.prototype = {
// This is our public method
public: function () {
console.info('PUBLIC method has been called');
},
// This is our private method, using (_)
_private: function () {
console.info('PRIVATE method has been called');
}
}
return protect(MyObject);
})();
// Create an instance of the object
var mo = new MyObject();
// Call its methods
mo.public(); // Pass
mo._private(); // Fail
Объясните, пожалуйста, как это работает? Как / где может вы вызываете метод _private?
JavaScript использует прототипы и не имеет классов (или методов в этом отношении), таких как объектно-ориентированные языки. Разработчику JavaScript нужно думать на JavaScript.
Цитата из Википедии:
Unlike many object-oriented languages, there is no distinction between a function definition and a method definition. Rather, the distinction occurs during function calling; when a function is called as a method of an object, the function's local this keyword is bound to that object for that invocation.
Решение с использованием самозапускающаяся функция и функция вызова для вызова частного «метода»:
var MyObject = (function () {
// Constructor
function MyObject(foo) {
this._foo = foo;
}
function privateFun(prefix) {
return prefix + this._foo;
}
MyObject.prototype.publicFun = function () {
return privateFun.call(this, ">>");
}
return MyObject;
}());
var myObject = new MyObject("bar");
myObject.publicFun(); // Returns ">>bar"
myObject.privateFun(">>"); // ReferenceError: private is not defined
функция вызова позволяет нам вызывать частную функцию с соответствующим контекстом (this).
Если вы используете Node.js, вам не нужен IIFE, потому что вы можете воспользоваться преимуществами система загрузки модулей:
function MyObject(foo) {
this._foo = foo;
}
function privateFun(prefix) {
return prefix + this._foo;
}
MyObject.prototype.publicFun = function () {
return privateFun.call(this, ">>");
}
module.exports= MyObject;
Загрузите файл:
var MyObject = require("./MyObject");
var myObject = new MyObject("bar");
myObject.publicFun(); // Returns ">>bar"
myObject.privateFun(">>"); // ReferenceError: private is not defined
Предложение TC39 частные методы и геттеры / сеттеры для классов JavaScript - это этап 3. Это означает, что в ближайшее время JavaScript будет реализовывать частные методы изначально!
Обратите внимание, что Поля частного класса JavaScript уже существует в современных версиях JavaScript.
Вот пример того, как это используется:
class MyObject {
// Private field
#foo;
constructor(foo) {
this.#foo = foo;
}
#privateFun(prefix) {
return prefix + this.#foo;
}
publicFun() {
return this.#privateFun(">>");
}
}
Вам может понадобиться Транспилятор / компилятор JavaScript для запуска этого кода на старых движках JavaScript.
PS: Если вам интересно, почему префикс #, прочитайте это.
Предупреждение: предложение оператора связывания TC39 почти не работает https://github.com/tc39/proposal-bind-operator/issues/53#issuecomment-374271822
Оператор связывания :: - это ECMAScript предложение и реализовано в Babel (этап 0).
export default class MyObject {
constructor (foo) {
this._foo = foo;
}
publicFun () {
return this::privateFun(">>");
}
}
function privateFun (prefix) {
return prefix + this._foo;
}
Это лучший ответ. Преимущества приватных методов, никакого хлама.
@TaylorMac За исключением части .call.
@janje? В чем проблема с .call? кстати, вы можете перейти с ES5 и использовать оператор привязки :: (подумайте об использовании вавилон)
f() чище, чем f.call( this ). И ваш частный код может включать объектные литералы или конструкторы функций, которые используют собственный this.
@janje А? В этом суть вопроса, частного f() в javascript нет (на данный момент).
@YvesM. Суть вопроса - выбрать лучший узор, который его имитирует.
@changed, что является лучшим компромиссом для скрытых функций (не вызываемых извне), хорошего синтаксиса (без .call) и небольшой памяти (без функций в экземпляре)? Это вообще существует?
Цитата @destoryer: And your private code ... which use their own this неточен. Под вашим заявлением вы имеете в виду кого-то, кто использует this, который не ссылается на экземпляр в частном методе? f.call(this) необходим, как если бы «частный метод» не нуждался в ссылке на вызывающий объект, лучшим подходом было бы использование функции. Если ваша точка рассмотрения - синтаксис, следует ожидать this.f() вместо f() для частного метода, но это не сильно отличается от f.call(this) imo, Python фактически делает это все время, но опускает .call, то есть a.f() == f(a)
Почему вы добавили вариант # к своему ответу после этого, когда группа людей уже разместила его здесь до вас? Просто для большего количества голосов? Беспринципный.
Возьмите любое из решений, соответствующих шаблону Крокфорда частный или привилегированный. Например:
function Foo(x) {
var y = 5;
var bar = function() {
return y * x;
};
this.public = function(z) {
return bar() + x * z;
};
}
В любом случае, когда злоумышленник не имеет права «выполнять» в контексте JS, у него нет возможности получить доступ к каким-либо «общедоступным» или «частным» полям или методам. Если у злоумышленника есть такой доступ, он может выполнить эту однострочную строку:
eval("Foo = " + Foo.toString().replace(
/{/, "{ this.eval = function(code) { return eval(code); }; "
));
Обратите внимание, что приведенный выше код является общим для всех типов конструкторов конфиденциальности. Это не сработает с некоторыми решениями здесь, но должно быть ясно, что практически все решения на основе замыканий могут быть сломаны таким образом с другими параметрами replace().
После этого любой объект, созданный с помощью new Foo(), будет иметь метод eval, который можно будет вызвать для возврата или изменения значений или методов, определенных в закрытии конструктора, например:
f = new Foo(99);
f.eval("x");
f.eval("y");
f.eval("x = 8");
Единственная проблема, которую я вижу в этом, заключается в том, что он не будет работать в случаях, когда есть только один экземпляр, и он создается при загрузке. Но тогда нет причин для фактического определения прототипа, и в этом случае злоумышленник может просто воссоздать объект вместо конструктора, если у него есть способ передачи тех же параметров (например, они являются постоянными или вычисляются из доступных значений).
На мой взгляд, это в значительной степени делает решение Крокфорда бесполезным. Поскольку «конфиденциальность» легко нарушается, недостатки его решения (снижение читабельности и ремонтопригодности, снижение производительности, увеличение памяти) делают метод, основанный на прототипе «без конфиденциальности», лучшим выбором.
Я обычно использую ведущие подчеркивания для обозначения методов и полей __private и _protected (стиль Perl), но идея обеспечения конфиденциальности в JavaScript просто показывает, насколько это неправильно понимаемый язык.
Поэтому я не согласен с Crockford, кроме его первого предложения.
Итак, как получить настоящую конфиденциальность в JS? Поместите все, что требуется для приватности на стороне сервера, и используйте JS для выполнения вызовов AJAX.
Это серьезная проблема, о которой следует больше знать. Есть ли «защита» от этой атаки?
@ Джеймс Ничего из того, что я знаю, я думаю, это природа зверя. Как я уже отмечал, вы можете перенести функциональность на сервер, где она работает в защищенной среде. Однако в своем ответе я хотел бы остановиться на том, что решение Крокфорда не помогает, излишне усложняет код и скрывает необходимость что-то с этим делать.
Если пользователь вводит секретный пароль, он не может сделать это на стороне сервера. В какой-то момент пароль будет в «частной» переменной. Так мог ли злоумышленник это прочитать? Я доверяю своему коду, и в любом случае мои домашние стандарты не допускают eval (). Злоумышленником может быть какой-то вредоносный сторонний плагин или библиотека JavaScript, которые я не проверил должным образом - так что да, мне нужно это проверить. Злоумышленник также может быть чем-то вроде рекламы на стороне, которая не должна взаимодействовать с моим кодом. Я защищаюсь от этого, заключая весь свой код в анонимный (function () {allMyStuff}());, чтобы ничего глобального не было.
@James Это получает ОТ, если вы хотите продолжить, задайте новый вопрос. Да, злоумышленник может прочитать пароль. Из вашей "частной" переменной. Или из ДОМА. Или он может заменить AJAX API. Или он заменяет вашу страницу на что-то другое. Если он не может сделать ничего из вышеперечисленного, тогда нет необходимости в «конфиденциальности» JS, потому что он также не может читать никакие из ваших переменных JS. Дело в том, что «решение» Крокфорда, которое сейчас все используют, не помогает с этой проблемой.
Я считаю, что обфускация псевдослучайного кода может быть слабой защитой от этой атаки - сложнее изменить тело функции, когда вы не можете полагаться на функцию, имеющую фиксированное имя; сложнее сделать f.eval('nameOfVariable'), когда вы не знаете, что такое 'nameOfVariable' ...
Возможный обходной путь - просто изначально определить конструктор как свойство окна только для чтения, чтобы его нельзя было повторно объявить: Object.defineProperty(window, "MyClass", { get: () => function() { let _privateMethod = () => "somethingSecret"; } })
@bluejayke Я все еще могу узнать ваш секрет: eval("MyEvilClass = " + MyClass.toString().replace( /{/, "{ this.eval = function(code) { return eval(code); }; " ));inst = new MyEvilClass();inst.eval("_privateMethod()"); возвращает "somethingSecret". Бессмысленно, это просто игра в стены и лестницы. В какой-то момент ваш код настолько запутан, нечитабелен, медленен и настолько загружен памятью, что вы могли бы вместо этого написать Java-апплет.
Вот что мне больше всего понравилось в отношении частных / общедоступных методов / членов и создания экземпляров в javascript:
вот статья: http://www.sefol.com/?p=1090
и вот пример:
var Person = (function () {
//Immediately returns an anonymous function which builds our modules
return function (name, location) {
alert("createPerson called with " + name);
var localPrivateVar = name;
var localPublicVar = "A public variable";
var localPublicFunction = function () {
alert("PUBLIC Func called, private var is :" + localPrivateVar)
};
var localPrivateFunction = function () {
alert("PRIVATE Func called ")
};
var setName = function (name) {
localPrivateVar = name;
}
return {
publicVar: localPublicVar,
location: location,
publicFunction: localPublicFunction,
setName: setName
}
}
})();
//Request a Person instance - should print "createPerson called with ben"
var x = Person("ben", "germany");
//Request a Person instance - should print "createPerson called with candide"
var y = Person("candide", "belgium");
//Prints "ben"
x.publicFunction();
//Prints "candide"
y.publicFunction();
//Now call a public function which sets the value of a private variable in the x instance
x.setName("Ben 2");
//Shouldn't have changed this : prints "candide"
y.publicFunction();
//Should have changed this : prints "Ben 2"
x.publicFunction();
JSFiddle: http://jsfiddle.net/northkildonan/kopj3dt3/1/
у этого подхода есть один важный недостаток - если вы создали 2 объекта, в памяти будет 2 одинаковых метода (например, PublicFunction) 1000 объектов съедят всю вашу память.
Я знаю, что уже слишком поздно, но как насчет этого?
var obj = function(){
var pr = "private";
var prt = Object.getPrototypeOf(this);
if (!prt.hasOwnProperty("showPrivate")){
prt.showPrivate = function(){
console.info(pr);
}
}
}
var i = new obj();
i.showPrivate();
console.info(i.hasOwnProperty("pr"));
Шаблон модуля правильный в большинстве случаев. Но если у вас тысячи экземпляров, классы экономят память. Если экономия памяти вызывает беспокойство и ваши объекты содержат небольшой объем личных данных, но имеют много общедоступных функций, тогда вам нужно, чтобы все общедоступные функции находились в .prototype для экономии памяти.
Вот что я придумал:
var MyClass = (function () {
var secret = {}; // You can only getPriv() if you know this
function MyClass() {
var that = this, priv = {
foo: 0 // ... and other private values
};
that.getPriv = function (proof) {
return (proof === secret) && priv;
};
}
MyClass.prototype.inc = function () {
var priv = this.getPriv(secret);
priv.foo += 1;
return priv.foo;
};
return MyClass;
}());
var x = new MyClass();
x.inc(); // 1
x.inc(); // 2
Объект priv содержит частные свойства. Он доступен через общедоступную функцию getPriv(), но эта функция возвращает false, если вы не передадите ей secret, и это известно только внутри основного замыкания.
Это имитирует защищенные члены, типы, унаследованные от него, также могут получить доступ к защищенным членам. Я предпочитаю этот паттерн частному
Оберните весь код в анонимную функцию: тогда все функции будут закрытыми, ТОЛЬКО функции, прикрепленные к объекту window:
(function(w,nameSpacePrivate){
w.Person=function(name){
this.name=name;
return this;
};
w.Person.prototype.profilePublic=function(){
return nameSpacePrivate.profile.call(this);
};
nameSpacePrivate.profile=function(){
return 'My name is '+this.name;
};
})(window,{});
Использовать это :
var abdennour=new Person('Abdennour');
abdennour.profilePublic();
На этот вопрос уже есть много ответов, но мне ничего не подошло. Итак, я придумал собственное решение, надеюсь, оно кому-то пригодится:
function calledPrivate(){
var stack = new Error().stack.toString().split("\n");
function getClass(line){
var i = line.indexOf(" ");
var i2 = line.indexOf(".");
return line.substring(i,i2);
}
return getClass(stack[2])==getClass(stack[3]);
}
class Obj{
privateMethode(){
if (calledPrivate()){
console.info("your code goes here");
}
}
publicMethode(){
this.privateMethode();
}
}
var obj = new Obj();
obj.publicMethode(); //logs "your code goes here"
obj.privateMethode(); //does nothing
Как видите, эта система работает при использовании этого типа классов в javascript. Насколько я понял, ни один из методов, прокомментированных выше, не работал.
Любопытно: действительно ли вам нужно было действительно раскрыть функцию, но сделать ее запретной во время выполнения, а не скрывать ее от внешних вызывающих, как это делают все / большинство других ответов? Если да, то почему? В чем, по вашему мнению, преимущество такого подхода? Мне кажется, что это просто ненужные накладные расходы на производительность, нечеткий API и, что ж, вероятно, обязательно вызовет ад отладки, но я всегда открыт для новых перспектив ...
@JHH, честно говоря, когда я оглядываюсь на это, я в значительной степени чувствую себя пальцем. Накладные расходы, как правило, не стоят того, хотя для меня это не имело большого значения, так как я не часто обращался к этим классам. Причина, по которой я сделал это таким образом, заключается в том, что он относительно чист в том, как вы пишете и вызываете функции. К тому времени я не понимал символов и тому подобного, но теперь, когда я понимаю, я думаю, что в целом это правильный путь при использовании классов. Я рассматриваю возможность удаления этого ответа полностью. Я опубликовал несколько глупых ответов, но эй, живи и учись.
Спасибо за ответ! Я не был уверен, что я что-то неправильно понял. Но да, все мы живем и учимся!
Лично я предпочитаю следующий шаблон для создания классов в JavaScript:
var myClass = (function() {
// Private class properties go here
var blueprint = function() {
// Private instance properties go here
...
};
blueprint.prototype = {
// Public class properties go here
...
};
return {
// Public class properties go here
create : function() { return new blueprint(); }
...
};
})();
Как видите, он позволяет определять как свойства класса, так и свойства экземпляра, каждое из которых может быть публичным и частным.
var Restaurant = function() {
var totalfoodcount = 0; // Private class property
var totalrestroomcount = 0; // Private class property
var Restaurant = function(name){
var foodcount = 0; // Private instance property
var restroomcount = 0; // Private instance property
this.name = name
this.incrementFoodCount = function() {
foodcount++;
totalfoodcount++;
this.printStatus();
};
this.incrementRestroomCount = function() {
restroomcount++;
totalrestroomcount++;
this.printStatus();
};
this.getRestroomCount = function() {
return restroomcount;
},
this.getFoodCount = function() {
return foodcount;
}
};
Restaurant.prototype = {
name : '',
buy_food : function(){
this.incrementFoodCount();
},
use_restroom : function(){
this.incrementRestroomCount();
},
getTotalRestroomCount : function() {
return totalrestroomcount;
},
getTotalFoodCount : function() {
return totalfoodcount;
},
printStatus : function() {
document.body.innerHTML
+= '<h3>Buying food at '+this.name+'</h3>'
+ '<ul>'
+ '<li>Restroom count at ' + this.name + ' : '+ this.getRestroomCount() + '</li>'
+ '<li>Food count at ' + this.name + ' : ' + this.getFoodCount() + '</li>'
+ '<li>Total restroom count : '+ this.getTotalRestroomCount() + '</li>'
+ '<li>Total food count : '+ this.getTotalFoodCount() + '</li>'
+ '</ul>';
}
};
return { // Singleton public properties
create : function(name) {
return new Restaurant(name);
},
printStatus : function() {
document.body.innerHTML
+= '<hr />'
+ '<h3>Overview</h3>'
+ '<ul>'
+ '<li>Total restroom count : '+ Restaurant.prototype.getTotalRestroomCount() + '</li>'
+ '<li>Total food count : '+ Restaurant.prototype.getTotalFoodCount() + '</li>'
+ '</ul>'
+ '<hr />';
}
};
}();
var Wendys = Restaurant.create("Wendy's");
var McDonalds = Restaurant.create("McDonald's");
var KFC = Restaurant.create("KFC");
var BurgerKing = Restaurant.create("Burger King");
Restaurant.printStatus();
Wendys.buy_food();
Wendys.use_restroom();
KFC.use_restroom();
KFC.use_restroom();
Wendys.use_restroom();
McDonalds.buy_food();
BurgerKing.buy_food();
Restaurant.printStatus();
BurgerKing.buy_food();
Wendys.use_restroom();
McDonalds.buy_food();
KFC.buy_food();
Wendys.buy_food();
BurgerKing.buy_food();
McDonalds.buy_food();
Restaurant.printStatus();Это заставляет меня использовать классы es6 и посмотреть, что он передает
Я предпочитаю хранить личные данные в связанном WeakMap. Это позволяет вам сохранить ваши общедоступные методы в прототипе, где они и должны быть. Это кажется наиболее эффективным способом решения этой проблемы для большого количества объектов.
const data = new WeakMap();
function Foo(value) {
data.set(this, {value});
}
// public method accessing private value
Foo.prototype.accessValue = function() {
return data.get(this).value;
}
// private 'method' accessing private value
function accessValue(foo) {
return data.get(foo).value;
}
export {Foo};
Не будь таким многословным. Это Javascript. Используйте Соглашение об именовании.
После нескольких лет работы в классах es6 я недавно начал работу над проектом es5 (используя requireJS, который уже выглядит очень многословно). Я много раз повторял все упомянутые здесь стратегии, и все в основном сводится к использовать соглашение об именах:
private. Другие разработчики, использующие Javascript, знают об этом заранее. Поэтому простого соглашения об именах более чем достаточно. Простое соглашение об именах с префиксом с подчеркиванием решает проблему как частных свойств, так и частных методов.мой-tooltip.js
define([
'tooltip'
],
function(
tooltip
){
function MyTooltip() {
// Later, if needed, we can remove the underscore on some
// of these (make public) and allow clients of our class
// to set them.
this._selector = "#my-tooltip"
this._template = 'Hello from inside my tooltip!';
this._initTooltip();
}
MyTooltip.prototype = {
constructor: MyTooltip,
_initTooltip: function () {
new tooltip.tooltip(this._selector, {
content: this._template,
closeOnClick: true,
closeButton: true
});
}
}
return {
init: function init() {
new MyTooltip(); // <-- Our constructor adds our tooltip to the DOM so not much we need to do after instantiation.
}
// You could instead return a new instantiation,
// if later you do more with this class.
/*
create: function create() {
return new MyTooltip();
}
*/
}
});
Следует отметить, что ни язык Javascript, ни какой-либо типичный хост браузера не определяют какие-либо объекты, которые полагаются на соглашения об именах, чтобы «скрыть» частное состояние, поэтому, хотя вы, возможно, правы в том, что разработчики поймут эту концепцию, это по-прежнему приводит к очень не- Объектно-ориентированный подход к объектно-ориентированному программированию.
Могу я попросить хорошей рекомендации по изготовлению таких? В этом примере есть новые для меня части. define, constructor и сама структура. Хотя я в основном согласен с ответом, я начал работать с JS с большим влиянием ООП и даже слишком рано перешел на TS, поскольку у меня был предыдущий опыт работы с C#. Я думаю, что мне нужно отучиться от этих вещей и понять парадигму прототипирования / процедуры. (проголосовало, кстати)
@ColdCerberus в этом фрагменте кода используется es5. Вы можете увидеть полную картину этого подхода здесь: gist.github.com/jonnyreeves/2474026. Но имейте в виду, что вы захотите воспользоваться этим подходом и обновить его до использования классов es6: googlechrome.github.io/samples/classes-es6 и модулей es6 (синтаксис импорта / экспорта): hackernoon.com/…
Теперь вы можете сделать это с помощью es10 частные методы. Вам просто нужно добавить # перед именем метода.
class ClassWithPrivateMethod {
#privateMethod() {
return 'hello world';
}
getPrivateMessage() {
return #privateMethod();
}
}
За исключением того, что это этап 3, и он еще официально не входит в состав языка.
Уродливое решение, но оно работает:
function Class(cb) {
const self = {};
const constructor = (fn) => {
func = fn;
};
const addPrivate = (fnName, obj) => {
self[fnName] = obj;
}
const addPublic = (fnName, obj) => {
this[fnName] = obj;
self[fnName] = obj;
func.prototype[fnName] = obj;
}
cb(constructor, addPrivate, addPublic, self);
return func;
}
const test = new Class((constructor, private, public, self) => {
constructor(function (test) {
console.info(test)
});
public('test', 'yay');
private('qwe', 'nay');
private('no', () => {
return 'hello'
})
public('asd', () => {
return 'this is public'
})
public('hello', () => {
return self.qwe + self.no() + self.asd()
})
})
const asd = new test('qweqwe');
console.info(asd.hello());Имена частных методов начинаются с префикса #, и к ним можно получить доступ Только внутри класса, в котором он определен.
class Restaurant {
// private method
#private_stuff() {
console.info("private stuff");
}
// public method
buy_food() {
this.#private_stuff();
}
};
const restaurant = new Restaurant();
restaurant.buy_food(); // "private stuff";
restaurant.private_stuff(); // Uncaught TypeError: restaurant.private_stuff is not a function
Это экспериментальное предложение. Ожидается, что версия ECMAScript 2021 будет выпущена в июне 2021 года.
Старый вопрос, но это довольно простая задача, которую можно решить правильно с помощью основного JS ... без абстракции класса ES6. На самом деле, насколько я могу судить, абстракция класса даже не решает эту проблему.
Мы можем выполнить эту работу как с помощью старой доброй функции конструктора, так и даже лучше с помощью Object.create(). Сначала займемся конструктором. По сути, это будет решение, аналогичное ответ джорджброка, которое подвергается критике, потому что все рестораны, созданные конструктором Restaurant, будут иметь одинаковые частные методы. Я постараюсь преодолеть это ограничение.
function restaurantFactory(name,menu){
function Restaurant(name){
this.name = name;
}
function prototypeFactory(menu){
// This is a private function
function calculateBill(item){
return menu[item] || 0;
}
// This is the prototype to be
return { constructor: Restaurant
, askBill : function(...items){
var cost = items.reduce((total,item) => total + calculateBill(item) ,0)
return "Thank you for dining at " + this.name + ". Total is: " + cost + "\n"
}
, callWaiter : function(){
return "I have just called the waiter at " + this.name + "\n";
}
}
}
Restaurant.prototype = prototypeFactory(menu);
return new Restaurant(name,menu);
}
var menu = { water: 1
, coke : 2
, beer : 3
, beef : 15
, rice : 2
},
name = "Silver Scooop",
rest = restaurantFactory(name,menu);
console.info(rest.callWaiter());
console.info(rest.askBill("beer", "beef"));Теперь очевидно, что мы не можем получить доступ к menu извне, но мы можем легко переименовать свойство name ресторана.
Это также можно сделать с помощью Object.create(), и в этом случае мы пропускаем функцию конструктора и просто делаем как var rest = Object.create(prototypeFactory(menu)), а затем добавляем свойство name к объекту rest, например, rest.name = name.
Примечание для современных читателей: этот вопрос был задан (и дан ответ) до того, как использовались современные версии Javascript (известный как ECMAScript (или ES)). Если вы только изучаете JavaScript, разница в синтаксисе / отсутствие
classзаключается в том, что этот вопрос старый. Существует экспериментальная функция, которая в настоящее время разрешает частные методы с использованием#в качестве префикса, но не имеет 100% поддержки по состоянию на 2020-08-19 во всех браузерах. См .: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…