Для тех, кто приходит в Javascript с класс-ориентированных языков программирования, этот язык вызывает некоторые вопросы (у меня они точно возникли). У Javascript свой подход к ООП. Помимо создания объектов просто через фигурные скобки, язык предоставляет возможность использования конструкторов. И здесь начинаются интересные вещи. Если поискать в интернете, то существует два основных подхода к созданию конструкторов.
Первый: создать конструктор и в него поместить все нужные методы и свойства будущего объекта:
function Animal(name) {
this.speed = 0;
this.name = name;
this.run = function(speed) {
this.speed += speed;
alert( this.name + ' бежит, скорость ' + this.speed );
};
};
Второй: создать конструктор как конструктор, а необходимые методы поместить в prototype:
function Animal(name) {
this.name = name;
this.speed = 0;
}
// методы в прототипе
Animal.prototype.run = function(speed) {
this.speed += speed;
alert( this.name + ' бежит, скорость ' + this.speed );
};
При этом в первом способе можно эмулировать приватные свойства через обычные переменные. Но второй способ является более производительным. Причина проста: в первом варианте каждый раз создавая объект, мы заново определяем его «класс» (то есть все его методы и свойства). Зато во втором способе мы не можем пользоваться приватными свойствами.
Пробуя разобраться в Javascript'е, мне захотелось найти реализацию приватных свойств с прототипами. Эта идея была лишена рациональной цели. Просто захотелось. Поиски не были особо успешными, поэтому настало время самому прогуляться по граблям для постройки своего велосипеда. На ум пришел паттерн модуль:
var weekDay = function() {
var names = ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"];
return {
name: function(number) { return names[number]; },
number: function(name) { return names.indexOf(name); }
};
}();
Приватные свойства есть, наследования нет. Тогда был написан следующий код:
let Example = (function () {
let exampleProperty = 'example-property';
let contstructor = function () {};
constructor.prototype.exampleMethod = function () {
console.log(exampleProperty);
};
return constructor;
})();
Это, в принципе, тот же модуль, только возвращает он не объект, а другую функцию (непосредственно конструктор). Это значит от «класса» Example можно без труда унаследоваться, как и сделать его наследуемым:
let Example = (function () {
let exampleProperty = 'example-property';
let contstructor = function () {};
//наследуемся
constructor.prototype = Object.create(Parent.prototype);
constructor.prototype.exampleMethod = function () {
console.log(exampleProperty);
};
return constructor;
})();
Все работает как надо. Желание было удовлетворено. Изучая код, после его проверки я обнаружил, что такое создание объектов даже быстрее, чем функциональный способ создания «классов». Ведь функция у нас самовызывающаяся, а значит определение методов и свойств происходит всего один раз. Хотя этот способ не быстрее обычного способа с прототипами. Позже я проверил насколько это соответствует действительности. Я сделал это достаточно топорно: запустил цикл на создание объектов и помещении их в массив и измерил с помощью console.time() в двух браузерах. Функциональный способ действительно был медленнее (порядка в десять раз), а вот мой велосипед то оставал, то перегонял родной способ на прототипах.
А теперь главный вопрос. Нужна ли приватность методов и свойств в прототипно-ориентированном языке? Кто-то говорит нет (это же не классический классовый ООП), кто-то говорит, что достаточно знака подчеркивания в начале названия, кто использует $$, а кто-то говорит, что без инкапсуляции жить нельзя. Подливают масла в огонь и сами разработчики языка, добавляя в него ключевое слово class. И пусть это синтаксический сахар над прототипами, но зачем он нужен если язык работает не на классах? В общем, ответ вопрос о необходимости приватности в Javascript для меня интересный и я хотел бы увидеть мнение в комментариях. Спасибо за внимание.
Автор: друже