Частные методы JavaScript

Чтобы создать класс 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 (известный как ECMAScript (или ES)). Если вы только изучаете JavaScript, разница в синтаксисе / отсутствие class заключается в том, что этот вопрос старый. Существует экспериментальная функция, которая в настоящее время разрешает частные методы с использованием # в качестве префикса, но не имеет 100% поддержки по состоянию на 2020-08-19 во всех браузерах. См .: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…

Josh Desmond 19.08.2020 21:29

См. этот ответ для чистого и простого решения «класса» с частным и общедоступным интерфейсом и поддержкой композиции.

kofifus 22.02.2017 04:13
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
502
2
361 900
32
Перейти к ответу Данный вопрос помечен как решенный

Ответы 32

Ответ принят как подходящий

Вы можете это сделать, но недостатком является то, что он не может быть частью прототипа:

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/…

Mike Samuel 29.09.2008 21:37

@mikesamuel - правда, но только когда в этих интерпретаторах есть ошибки :)

jvenema 15.05.2010 19:26

Это, конечно, частный метод, но он, как правило, использует намного больше памяти, чем обычный метод-прототип, особенно если вы создаете много таких объектов. Для каждого экземпляра объекта создается отдельная функция, привязанная к объекту, а не к классу. Кроме того, сборщик мусора не производится до тех пор, пока сам объект не будет уничтожен.

Arindam 07.02.2012 02:37

Если вы сделаете объект McDonalds () унаследованным от Restaurant (), McDonalds не сможет переопределить частные методы, если вы объявите их таким образом. Что ж, на самом деле вы можете, но это не сработает, если какой-то другой метод вызывает частный, он вызовет исходную версию метода, и вы также не сможете вызвать родительский метод. Пока я не нашел хорошего способа объявить частные методы, которые хорошо работают с наследованием. Это, а также влияние на производительность делают этот шаблон проектирования не очень хорошим. Я бы порекомендовал сделать какой-то префикс для обозначения частных методов, например, начиная с подчеркивания.

Hoffmann 25.06.2013 21:56

Частные методы не должны переопределяться - они частные.

17 of 26 25.06.2013 22:55

Если вам нужны частные методы (не частные данные), просто объявите их вне прототипа и назовите их следующим образом: myFoo.apply (this, args). Тогда вы можете оставить общедоступный api маленьким, сохранив при этом частные методы, работающие с вашим локальным объектом.

oligofren 17.02.2014 19:34

А что, если у меня есть частная функция внутри другой функции, которая была добавлена ​​в прототип, например. Thing.prototype.public = function() { var private = function() {...}; private(); }; Это все еще нерациональное использование памяти?

RTF 08.03.2014 23:16

Пожалуйста, подумайте об использовании самозапускающаяся функция + вызов, как описано здесь: stackoverflow.com/a/25172901/1480391

Yves M. 28.08.2014 14:32

eval("Restaurant = " + Restaurant.toString().replace( /{/, "{ this.eval = function(code) { return eval(code); }; " )); var r = new Restaurant(); r.eval("myPrivateVar = \"I can set this too!\"");

Fozi 05.02.2015 19:03

Если вы приедете сюда в 2019 году: это действительно плохой путь. Эти функции не только частные, но и СТАТИЧЕСКИЕ. См. Способ самостоятельного вызова функции

Javier S 04.06.2019 18:30

@Arindam, если вы используете только один объект, я думаю, он не использует больше памяти, чем другие решения? Я думаю, это самое простое и удобочитаемое решение, не нужно использовать "странные" JavaScript-штуки вроде prototype или call.

baptx 22.07.2019 20:57

@MikeSamuel, тогда просто сделай delete window.eval;, и тогда все в порядке.

Sapphire_Brick 13.12.2019 00:37

@Sapphire_Brick Это только запрещает доступ к eval коду, который еще не запущен, и нарушает работу кода, который требует eval для работы. Загруженные модули могут захватывать eval и удерживать его. Я написал это в 2008 году, и современные интерпретаторы этого уже не делают.

Mike Samuel 14.12.2019 00:20

@MikeSamuel Если предположить, что никто не зависит от eval, в чем проблема?

Sapphire_Brick 15.12.2019 20:18

@Sapphire_Brick. Я не принимаю это предположение. Есть очень широко используемые модули, которые используют eval для метапрограммирования.

Mike Samuel 20.12.2019 18:30

ну, они не должны использовать eval, и хорошо, что мы не говорим о метапрограммировании. Кроме того, мы можем поместить оператор delete window.eval в нижнюю часть кода, чтобы вы по-прежнему могли выполнять свое злое метапрограммирование в верхней части кода и иметь инкапсуляцию в нижней части.

Sapphire_Brick 22.12.2019 02:48

Почти ... 1) У вас по-прежнему не может быть двух экземпляров с собственными копиями myPrivateVar. 2) Общедоступным методам должен быть разрешен доступ к myPrivateVar. В этой реализации вы можете получить доступ к частным данным только из частных методов.

CodingYoshi 26.03.2020 00:57

Вы можете смоделировать частные методы следующим образом:

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

Jared 11.09.2008 05:30

Он упомянул эту ссылку в вопросе.

Gulzar Nazim 11.09.2008 05:31

Обратной стороной этого метода является то, что у вас не может быть доступа private_stuff () к другим частным данным в ресторане, а другие методы ресторана не могут вызывать private_stuff (). Положительным моментом является то, что если вам не нужно ни одно из условий, о которых я только что упомянул, вы можете сохранить use_restroom () в прототипе.

17 of 26 11.09.2008 05:33

Мне также не нравится многословие этого подхода, поэтому, вероятно, я не буду использовать его в общем случае. Это изящно, как он добавляется в прототип, и в JS обычно производительность побеждает все остальное.

Wayne Kao 11.09.2008 06:13

Это должно быть решение и принятый ответ, потому что автор явно использует свойство prototype.

Gabriel Llamas 31.01.2012 14:00

Что насчет использования call, тогда частные методы смогут получить доступ к закрытым членам. private_stuff.call (это)

Anders 27.03.2012 10:55

В соответствии с шаблоном, предложенным @georgebrock, частные данные все будут совместно использоваться среди объектов ресторана все. Это похоже на статические частные переменные и функции в ООП на основе классов. Я предполагаю, что OP этого хочет. Чтобы проиллюстрировать, что я имею в виду, я создал jsFiddle.

feklee 30.07.2012 13:39

@feklee: общедоступные методы определяются как свойства объекта-прототипа, который используется многими экземплярами. Здесь мы делаем то же самое с частными методами: единый общий объект function, доступный для всех экземпляров. Вы правы, указывая, что этот метод не работает для частных данных, но вопрос не об этом.

georgebrock 30.07.2012 16:00

@georgebrock: Судя по ответу, принятому OP, он действительно хочет иметь закрытых членов за объект, а не в объекте-прототипе. Публикуемый им вариант использования также предполагает это. Состояние туалета, например, обычно соответствует ресторану.

feklee 30.07.2012 16:49

Вам нужна деталь constructor:Restaurant? Когда я устанавливаю прототип конструктора, я обычно делаю, например, Restaurant.prototype = {};, и вроде работает. Я не понимаю, чем будет отличаться Restaurant.prototype = (function () { return {}; })();.

Paul D. Waite 03.07.2013 18:28

Как это будет работать с наследованием? Например, вы не можете установить Restaurant.prototype на Object.create(House.prototype), поскольку он уже установлен.

Evi1M4chine 30.07.2013 19:27

@ PaulD.Waite: хороший вопрос. В этом примере конструктор - пустая функция, но что, если это не так? Кажется, что отсутствие части constructor: Restaurant не мешает правильно связать свойство конструктора с функцией Restaurant. Разве это не должно быть undefined?

Nicolas Le Thierry d'Ennequin 10.04.2014 14:07

При таком подходе «частные» методы не могут быть вызваны из конструктора. Мне это кажется серьезным ограничением.

Nicolas Le Thierry d'Ennequin 10.04.2014 14:38

@feklee i обновил свою скрипку, чтобы показать, как использовать свойство члена вместо внутри прототипа.

Tom Kay 28.05.2015 13:00

@ NicolasLeThierryd'Ennequin Чтобы получить доступ к закрытым методам в конструкторе, вы можете явно определить открытый конструктор прототипа и вызвать его из реального конструктора: function Restaurant() { this.constructor(); } и constructor: function(){ private_stuff(); }

pishpish 10.02.2016 21:05

В этих ситуациях, когда у вас есть общедоступный 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 :(

Click Upvote 09.06.2011 06:33

Но это не класс, поэтому у вас не может быть 2 «экземпляров» этого класса с разными состояниями.

DevAntoine 21.08.2012 19:26

удалите часть (), и у вас будет «класс». по крайней мере, где вы можете создавать экземпляры разных isntances с разными состояниями. хотя шаблон модуля довольно интенсивно использует память ...

oligofren 17.02.2014 19:56

@DevAntoine Посмотрите комментарии к ответу 17 из 26. В JavaScript расширяемые классы и частные методы нелегко идут рука об руку. В этом случае я предлагаю отдать предпочтение композиции, а не наследованию. Создайте расширяемый прототип с помощью тех же методов, что и закрытый конкретный объект. Затем внутри вашего прототипа вы можете решить, когда вызывать методы вашего конкретного объекта.

user4903 17.02.2014 21:21

Есть ли недостатки в вызове общедоступных переменных из частных функций, например aPrivateMethod = function() { MYLIB.aPublicProperty}?

Hanna 06.02.2015 01:50

Это не позволяет частным методам получать доступ к общедоступным методам.

sbichenko 28.02.2015 18:49

Апофеоз паттерна модуля: Выявление паттерна модуля

Небольшое аккуратное продолжение очень прочного узора.

Все это закрытие будет стоить вам. Убедитесь, что вы проверили влияние скорости, особенно в IE. Вы обнаружите, что лучше использовать соглашение об именах. Есть еще много корпоративных веб-пользователей, которые вынуждены использовать IE6 ...

Те 9%, которые все еще используют IE6, не заботятся о скорости, оптимизации и всех современных функциях HTML5. Так что закрытие ничего не будет стоить.

Gabriel Llamas 31.01.2012 14:06

Сейчас 0,5% (август 2012 г.) w3schools.com/browsers/browsers_explorer.asp

Lorenzo Polidori 05.10.2012 18:24

@LorenzoPolidori пользователи w3schools! == корпоративные веб-пользователи;]

WynandB 19.02.2014 04:58

Соглашение об именах (например, добавление символа подчеркивания) - это правильный путь. Код легче поддерживать, а методы все еще определены в прототипе. Однако в настоящее время ... я просто помечаю этот метод как частный в машинописном тексте.

David Sherret 10.02.2015 21:39

Если вам нужен полный набор общедоступных и частных функций с возможностью для общедоступных функций доступ к частным функциям, разметьте код для объекта, подобного этому:

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 () {
  };
}

Может кто-нибудь сказать мне, почему это было отклонено? Мне нравится.

thomasrutter 22.03.2010 04:03

Каждый раз, когда вы делаете new MyObject, прототип MyObject заменяется теми же значениями.

bpierre 12.11.2011 06:39

-1. Никогда не назначайте .prototype внутри конструктора.

Bergi 07.08.2014 06:03

Я придумал это: Обновлено: На самом деле кто-то связался с идентичным решением. Ага!

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 - это скорость, и вы хотите предупреждать о скорости. Спасибо!

Jason 02.06.2010 20:43

Разобрался, может var Car = function(speed) { this.speed = speed; } и `return {constructor: Car, ...`

Jason 02.06.2010 23:23

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: хорошие части"

oligofren 02.10.2012 20:30

that используется вместо this, чтобы избежать проблем с областью видимости, когда функции привязаны к другому объекту. Здесь вы сохраняете this в that и никогда не используете его снова, что равносильно тому, что вы не делаете этого вообще. Вы должны заменить this на that во всех внутренних функциях Constructor (а не в объявлении методов). Если employee каким-то образом связан с apply или call, эти методы могут вызвать ошибку, поскольку this будет неправильно привязан.

Maroshii 26.07.2013 02:57

Кроме того, у каждого экземпляра будет полная копия частных функций, что неэффективно. Это вдобавок к тому факту, что общедоступные методы не могут получить доступ к частным варам классов, заставляет меня переключиться на дротик. К сожалению, angulardart - супер бета.

Ray Foss 27.05.2014 09:13

Поскольку каждый размещал здесь свой код, я тоже сделаю это ...

Мне нравится Крокфорд, потому что он ввел в 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 07.08.2014 06:05

@Bergi. Собственно, ты прав. Это сработало бы, но также потребовало бы ресурсов (это так называется?). Я отредактировал свой ответ по этому поводу.

Flex Elektro Deimling 09.08.2014 18:40

Спасибо за обновления. Не знаю, как назвать предыдущую версию (но "баг" :-)

Bergi 09.08.2014 18:43

Я думаю, что такие вопросы возникают снова и снова из-за непонимания закрытий. Скрытность - это самое главное в 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

Hudon 08.10.2013 21:18

Не работает. Переменная name здесь действует как статическая переменная и используется всеми экземплярами Restaurant. Вот jsbin: jsbin.com/oqewUWa/2/edit?js,output

Shital Shah 06.11.2013 10:24

это хорошая попытка, но, как заметил Шитал, переменная имени содержит ошибки.

oligofren 17.02.2014 20:00

добавление сюда моего 2c, чтобы подтвердить, что это не работает, выглядит красиво, но, как указано выше, "name" будет служить статической переменной, то есть совместно используемой для всех экземпляров

Paul Carroll 19.09.2014 02:47

Вот что я разработал:

Требуется один класс кода сахара, который вы можете найти здесь. Также поддерживает защищенный, наследование, виртуальный, статический материал ...

;( 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, потому что вы ничего не возвращаете из анонимной функции.

user4815162342 05.02.2014 14:13

Где и как? Предполагая, что ссылка на созданный Ресторан утеряна, privateVars не будет иметь ссылки на своего владельца. Контрольный граф ациклический. Что мне не хватает?

Evan Leis 12.08.2014 23:50

На самом деле это единственный ответ, который поддерживает частные свойства помимо методов. Только две проблемы уже отмечены в ответе.

pishpish 10.02.2016 21:22

Я вижу утечку памяти: когда экземпляр Restaurant был обработан сборщиком мусора, его значения остаются в пределах privateVars. В этом случае WeakMap может стать хорошей заменой Array.

Gershy 26.02.2020 19:37

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/nooning/JSClass

Я создал новый инструмент, который позволит вам иметь настоящие частные методы на прототипе. 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?

Bergi 07.08.2014 06:06

Использование функции автоматического вызова и вызова

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

Если вы используете 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

(новинка!) Собственные частные методы в будущих версиях JavaScript

Предложение 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: Если вам интересно, почему префикс #, прочитайте это.

(устарело) ES7 с оператором привязки

Предупреждение: предложение оператора связывания 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 07.10.2014 10:43

@TaylorMac За исключением части .call.

pishpish 10.02.2016 21:10

@janje? В чем проблема с .call? кстати, вы можете перейти с ES5 и использовать оператор привязки :: (подумайте об использовании вавилон)

Yves M. 11.02.2016 01:59

f() чище, чем f.call( this ). И ваш частный код может включать объектные литералы или конструкторы функций, которые используют собственный this.

pishpish 11.02.2016 16:57

@janje А? В этом суть вопроса, частного f() в javascript нет (на данный момент).

Yves M. 17.02.2016 02:28

@YvesM. Суть вопроса - выбрать лучший узор, который его имитирует.

pishpish 17.02.2016 22:07

@changed, что является лучшим компромиссом для скрытых функций (не вызываемых извне), хорошего синтаксиса (без .call) и небольшой памяти (без функций в экземпляре)? Это вообще существует?

Ciprian Tomoiagă 01.11.2016 18:12

Цитата @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)

Valen 16.12.2018 19:16

Почему вы добавили вариант # к своему ответу после этого, когда группа людей уже разместила его здесь до вас? Просто для большего количества голосов? Беспринципный.

Thielicious 21.01.2021 18:09

Возьмите любое из решений, соответствующих шаблону Крокфорда частный или привилегированный. Например:

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.

Это серьезная проблема, о которой следует больше знать. Есть ли «защита» от этой атаки?

James 03.02.2015 13:06

@ Джеймс Ничего из того, что я знаю, я думаю, это природа зверя. Как я уже отмечал, вы можете перенести функциональность на сервер, где она работает в защищенной среде. Однако в своем ответе я хотел бы остановиться на том, что решение Крокфорда не помогает, излишне усложняет код и скрывает необходимость что-то с этим делать.

Fozi 03.02.2015 22:08

Если пользователь вводит секретный пароль, он не может сделать это на стороне сервера. В какой-то момент пароль будет в «частной» переменной. Так мог ли злоумышленник это прочитать? Я доверяю своему коду, и в любом случае мои домашние стандарты не допускают eval (). Злоумышленником может быть какой-то вредоносный сторонний плагин или библиотека JavaScript, которые я не проверил должным образом - так что да, мне нужно это проверить. Злоумышленник также может быть чем-то вроде рекламы на стороне, которая не должна взаимодействовать с моим кодом. Я защищаюсь от этого, заключая весь свой код в анонимный (function () {allMyStuff}());, чтобы ничего глобального не было.

James 04.02.2015 14:20

@James Это получает ОТ, если вы хотите продолжить, задайте новый вопрос. Да, злоумышленник может прочитать пароль. Из вашей "частной" переменной. Или из ДОМА. Или он может заменить AJAX API. Или он заменяет вашу страницу на что-то другое. Если он не может сделать ничего из вышеперечисленного, тогда нет необходимости в «конфиденциальности» JS, потому что он также не может читать никакие из ваших переменных JS. Дело в том, что «решение» Крокфорда, которое сейчас все используют, не помогает с этой проблемой.

Fozi 04.02.2015 18:15

Я считаю, что обфускация псевдослучайного кода может быть слабой защитой от этой атаки - сложнее изменить тело функции, когда вы не можете полагаться на функцию, имеющую фиксированное имя; сложнее сделать f.eval('nameOfVariable'), когда вы не знаете, что такое 'nameOfVariable' ...

Gershy 26.02.2020 19:44

Возможный обходной путь - просто изначально определить конструктор как свойство окна только для чтения, чтобы его нельзя было повторно объявить: Object.defineProperty(window, "MyClass", { get: () => function() { let _privateMethod = () => "somethingSecret"; } })

bluejayke 14.04.2020 09:27

@bluejayke Я все еще могу узнать ваш секрет: eval("MyEvilClass = " + MyClass.toString().replace( /{/, "{ this.eval = function(code) { return eval(code); }; " ));inst = new MyEvilClass();inst.eval("_privateMethod()"); возвращает "somethingSecret". Бессмысленно, это просто игра в стены и лестницы. В какой-то момент ваш код настолько запутан, нечитабелен, медленен и настолько загружен памятью, что вы могли бы вместо этого написать Java-апплет.

Fozi 15.04.2020 05:07

Вот что мне больше всего понравилось в отношении частных / общедоступных методов / членов и создания экземпляров в 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 объектов съедят всю вашу память.

Artem G 22.06.2015 14:44

Я знаю, что уже слишком поздно, но как насчет этого?

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, и это известно только внутри основного замыкания.

Это имитирует защищенные члены, типы, унаследованные от него, также могут получить доступ к защищенным членам. Я предпочитаю этот паттерн частному

HMR 03.02.2015 03:04

Оберните весь код в анонимную функцию: тогда все функции будут закрытыми, ТОЛЬКО функции, прикрепленные к объекту 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();

FIDDLE

На этот вопрос уже есть много ответов, но мне ничего не подошло. Итак, я придумал собственное решение, надеюсь, оно кому-то пригодится:

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 08.01.2018 16:42

@JHH, честно говоря, когда я оглядываюсь на это, я в значительной степени чувствую себя пальцем. Накладные расходы, как правило, не стоят того, хотя для меня это не имело большого значения, так как я не часто обращался к этим классам. Причина, по которой я сделал это таким образом, заключается в том, что он относительно чист в том, как вы пишете и вызываете функции. К тому времени я не понимал символов и тому подобного, но теперь, когда я понимаю, я думаю, что в целом это правильный путь при использовании классов. Я рассматриваю возможность удаления этого ответа полностью. Я опубликовал несколько глупых ответов, но эй, живи и учись.

thegunmaster 19.02.2018 18:38

Спасибо за ответ! Я не был уверен, что я что-то неправильно понял. Но да, все мы живем и учимся!

JHH 19.02.2018 19:56

Лично я предпочитаю следующий шаблон для создания классов в 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 и посмотреть, что он передает

sheriffderek 20.10.2018 23:47

Я предпочитаю хранить личные данные в связанном 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, который уже выглядит очень многословно). Я много раз повторял все упомянутые здесь стратегии, и все в основном сводится к использовать соглашение об именах:

  1. В Javascript нет таких ключевых слов, как private. Другие разработчики, использующие Javascript, знают об этом заранее. Поэтому простого соглашения об именах более чем достаточно. Простое соглашение об именах с префиксом с подчеркиванием решает проблему как частных свойств, так и частных методов.
  2. Давайте воспользуемся прототипом из соображений скорости, но не будем вдаваться в подробности. Давайте постараемся, чтобы «класс» es5 выглядел как можно ближе к тому, что мы могли бы ожидать в других языках программирования (и рассматривать каждый файл как класс, даже если нам не нужно возвращать экземпляр).
  3. Давайте продемонстрируем более реалистичную ситуацию с модулем (мы будем использовать старый es5 и старый requireJs).

мой-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, ни какой-либо типичный хост браузера не определяют какие-либо объекты, которые полагаются на соглашения об именах, чтобы «скрыть» частное состояние, поэтому, хотя вы, возможно, правы в том, что разработчики поймут эту концепцию, это по-прежнему приводит к очень не- Объектно-ориентированный подход к объектно-ориентированному программированию.

rich remer 09.11.2018 00:32

Могу я попросить хорошей рекомендации по изготовлению таких? В этом примере есть новые для меня части. define, constructor и сама структура. Хотя я в основном согласен с ответом, я начал работать с JS с большим влиянием ООП и даже слишком рано перешел на TS, поскольку у меня был предыдущий опыт работы с C#. Я думаю, что мне нужно отучиться от этих вещей и понять парадигму прототипирования / процедуры. (проголосовало, кстати)

Cold Cerberus 02.08.2019 09:43

@ColdCerberus в этом фрагменте кода используется es5. Вы можете увидеть полную картину этого подхода здесь: gist.github.com/jonnyreeves/2474026. Но имейте в виду, что вы захотите воспользоваться этим подходом и обновить его до использования классов es6: googlechrome.github.io/samples/classes-es6 и модулей es6 (синтаксис импорта / экспорта): hackernoon.com/…

prograhammer 03.08.2019 19:24

Теперь вы можете сделать это с помощью es10 частные методы. Вам просто нужно добавить # перед именем метода.

class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
    return #privateMethod();
  }
}

За исключением того, что это этап 3, и он еще официально не входит в состав языка.

misterhtmlcss 28.12.2019 07:59

Уродливое решение, но оно работает:

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());

ES2021 / ES12 - Частные методы

Имена частных методов начинаются с префикса #, и к ним можно получить доступ Только внутри класса, в котором он определен.

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.

Другие вопросы по теме