Только для читателей Lifeexample возможно открыть интернет-магазин на Moguta.CMS со скидкой в 15%
Javascript классы
Здравствуйте, уважаемые читатели блога LifeExample. Сегодня, занимаясь оптимизацией moguta.cms я решил найти способ и структурировать свой JavaScript код.
Я давно знаком с принципами ООП и поэтому использование, классов в языках их поддерживающих, является обычной для меня практикой.
По умолчанию такого понятия как классы в javaScript не существует, что доставляет мелкие неудобства при проектировании одностраничных веб-приложений, но в JavaScript есть объекты.
Для программиста знакомого с ООП, должно быть странным такое положение дел, поскольку в рамках ООП – объекты являются экземплярами классов. В JavaScript, классы отсутствуют, но их экземпляры можно создать. Немного похоже на бред, но это отнюдь не так. Чтобы понять, как такое возможно нужно изучить сущность и свойства понятия литеральная нотация.
Литеральная нотация – это специальный формат задания объекта в JavaScript.
Не будем пока вдаваться в подробности, и зададим простой объект следующим образом.
1 2 3 4 | var apple = { color:"green", cost: 3 } |
Этой записью, мы определили объект "яблоко" с полями – цвет и стоимость. Тут все просто, получить свойство объекта можно так:
1 | alert(apple.color) |
Кроме простых типов данных, свойства могут являться себе функциями:
1 2 3 4 5 6 7 | var apple = { color:"green", cost: 3, getColor: function(){ return apple.color; } } |
Теперь получить цвет яблока можно еще одним способом:
1 | alert(apple.getColor()); |
Ну как? Не напоминает ничего родного и близкого из области ООП?
Если немного пофантазировать, то можно с помощью объекта задать приватные поля и функции, тем самым реализовать JavaScript классы.
Пример класса на Javascript
На самом деле существует масса теории и вариантов на тему создания классов на Javascript, и некоторые выкладки я приведу в конце статьи.
Для себя я выделил способ создания класса на JS именно при помощи литеральной нотации совмещенный с паттерном "Модуль" И хочу попробовать донести до вас его суть следующим примером.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | /** * Модуль LifeexampleClass. */ var LifeexampleClass = (function () { var privateField1 = "значение приватного поля1"; var privateField2 = "значение приватного поля2"; // Функция доступная только внутри класса. function privateFunction () { alert("Из тела класса вызван приватный метод: [privateFunction] "); alert("Приватный метод обратился к приватному полю: [privateField1 = '"+privateField1+"']"); alert("Приватный метод обратился к публичному полю: [publicField1 = '"+this.publicField1+"']"); return true; } return { // Публичные поля класса. publicField1: "значение публичного поля1", publicField2: "значение публичного поля2", // Публичный метод класса. init: function() { alert('Вызван публичный метод класса: [init]'); // Обращение к публичному полю, доступному как внутри класса так и из вне. alert('Метод [init] обратился к публичному полю [privateField1='+privateField1+']'); // Вызов приватной функции доступной только внутри класса. privateFunction(); } } })(); // обращение к приватному полю, доступному только внутри класса. alert("Снаружи класса произошла попытка обратиться к приватному полю [privateField1] результат = "+LifeexampleClass.privateField1); try{ // обращение к приватному методу, доступному только внутри класса следующая строка вызовет ошибку LifeexampleClass.privateFunction() alert("Удалось обратиться к приватному методу класса! [Ошибка в архитектуре класса]"); } catch(err){ alert("Произошла ошибка "+err+"! Обращение к приватному методу класса запрещено!"); } // обращение к публичному полю, доступному как внутри класса так и из вне. alert("Снаружи класса произошла попытка обратиться к публичному полю [publicField1] результат = "+LifeexampleClass.publicField1); try{ // обращение к публичному методу, доступному как внутри класса так и из вне. LifeexampleClass.init() } catch(err){ alert("Не удалось обратиться к публичному методу класса! [Ошибка в архитектуре класса]"); } |
Скомпилируйте данный код, чтобы проверить его работоспособность, но даже визуально, полагаясь, только на комментарии можно уловить суть и смысл задания класса на JS. Конечно это по по-прежнему не класс, а объект LifeexampleClass, но для структурирования кода на JS, этого достаточно.
Получившийся псевдокласс имеет приватные и публичный поля и методы. Для полного привычного понимания класса ему не хватает только возможности наследовать и быть родителем для дочерних объектов.
Хоть в JS и нет ООП, но наследование свойств предусмотрено свойством прототипирования: prototype.
Приведу несколько полезных аспектов, осознавая которые можно решить для себя массу задач даже без привычного смысла классов и ООП.
Запомните:
- Классов нет, есть только объекты!
- Function – тоже является объектом , потому что в Javascript все является объектами. 🙂
- Конструктором объекта может быть только объект встроенного класса (обычно это Function).
Далее я приведу все стандартные примеры реализации javascript классов, встретившиеся мне в различных источниках и ресурсах интернета
Класс на JS заданный через function
Самый распространенный способ реализовать класс на JS – создать функцию, внутри которой определить поля и методы. Создать экземпляр этого класса можно будет привычным образом с помощью ключевого слова new. При этом можно использовать конструктор.
1 2 3 4 5 6 7 8 9 10 11 12 | // A car "javascript class" function Car( model ) { this.model = model; this.color = "silver"; this.year = "2012"; this.getInfo = function () { return this.model + " " + this.year; }; } |
Используйте данный javascript класс следующим образом:
1 2 3 | var myCar = new Car("ford"); myCar.year = "2010"; console.log( myCar.getInfo() ); |
Класс на JS как объект заданный литералами
Преимущества такого подхода в том, что мы можем инкапсулировать данные этого класса дальше в потомки класса.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | var myModule = { myProperty: "someValue", // object literals can contain properties and methods. // e.g we can define a further object for module configuration: myConfig: { useCaching: true, language: "en" }, // a very basic method myMethod: function () { console.log( "Where in the world is Paul Irish today?" ); }, // output a value based on the current configuration myMethod2: function () { console.log( "Caching is:" + ( this.myConfig.useCaching ) ? "enabled" : "disabled" ); }, // override the current configuration myMethod3: function( newConfig ) { if ( typeof newConfig === "object" ) { this.myConfig = newConfig; console.log( this.myConfig.language ); } } }; // myModule it is class javascript // Outputs: Where in the world is Paul Irish today? myModule.myMethod(); // Outputs: enabled myModule.myMethod2(); // Outputs: fr myModule.myMethod3({ language: "fr", useCaching: false }); |
Класс основанный на паттерне "Модуль"
Преимущество в том, что данный паттерн предоставляет полноценное ограничение доступа к определённым компонентам объекта.
Следующие два примера были отражены в самом первом способе описанном мной в начале статьи, но тем не менее я выложу и эти два, для закрепления материала.
Пример с публичными полями и методами:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // testModule it is class javascript var testModule = (function () { var counter = 0; return { incrementCounter: function () { return counter++; }, resetCounter: function () { console.log( "counter value prior to reset: " + counter ); counter = 0; } }; })(); // Usage: // Increment our counter testModule.incrementCounter(); // Check the counter value and reset // Outputs: 1 testModule.resetCounter(); // end example class javascript |
Пример с приватными методами и полями:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | var myNamespace = (function () { var myPrivateVar, myPrivateMethod; // A private counter variable myPrivateVar = 0; // A private function which logs any arguments myPrivateMethod = function( foo ) { console.log( foo ); }; return { // A public variable myPublicVar: "foo", // A public function utilizing privates myPublicFunction: function( bar ) { // Increment our private counter myPrivateVar++; // Call our private method using bar myPrivateMethod( bar ); } }; })(); |
Паттерн синглтон в JS
Для особо жаждущих выкладываю реализацию синглтона на JS.
Однажды я описывал как устроен паттерн синглтон на PHP, теперь встречайте его реализацию на JS.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | var mySingleton = (function () { // Instance stores a reference to the Singleton var instance; function init() { // Singleton // Private methods and variables function privateMethod(){ console.log( "I am private" ); } var privateVariable = "Im also private"; return { // Public methods and variables publicMethod: function () { console.log( "The public can see me!" ); }, publicProperty: "I am also public" }; }; return { // Get the Singleton instance if one exists // or create one if it doesn't getInstance: function () { if ( !instance ) { instance = init(); } return instance; } }; })(); // Usage: var singleA = mySingleton; var singleB = mySingleton; console.log( singleA === singleB ); // true |
Также советую почитать примеры изложенные на этом сайте:
Для примеров описанных в той статье понадобится маленькая библиотека Oop.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | // // Create proper-derivable "class". // // Version: 1.2 // function newClass(parent, prop) { // Dynamically create class constructor. var clazz = function() { // Stupid JS need exactly one "operator new" calling for parent // constructor just after class definition. if (clazz.preparing) return delete(clazz.preparing); // Call custom constructor. if (clazz.constr) { this.constructor = clazz; // we need it! clazz.constr.apply(this, arguments); } } clazz.prototype = {}; // no prototype by default if (parent) { parent.preparing = true; clazz.prototype = new parent; clazz.prototype.constructor = parent; clazz.constr = parent; // BY DEFAULT - parent constructor } if (prop) { var cname = "constructor"; for (var k in prop) { if (k != cname) clazz.prototype[k] = prop[k]; } if (prop[cname] && prop[cname] != Object) clazz.constr = prop[cname]; } return clazz; } |
Пример работы с библиотекой oop.js :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <script src="Oop.js"></script> <script> // Базовый "javascript класс". Car = newClass(null, { constructor: function() { document.writeln("Вызван конструктор Car()."); }, drive: function() { document.writeln("Вызван Car.drive()"); } }); // Производный "javascript класс". Zaporojets = newClass(Car, { constructor: function() { document.writeln("Вызван конструктор Zaporojets()."); this.constructor.prototype.constructor.call(this); }, crack: function() { document.writeln("Вызван Zaporojets.crack()"); }, drive: function() { document.writeln("Вызван Zaporojets.drive()"); return this.constructor.prototype.drive.call(this); } }); document.writeln("Программа запущена."); // Создаем объект производного "класса". var vehicle = new Zaporojets(); vehicle.drive(); // вызывается функция базового объекта // Создаем еще один объект того же класса. var vehicle = new Zaporojets(); vehicle.crack(); // функция производного объекта </script> |
На этом я заканчиваю статью о том, как реализовать JavaScript классы, подписывайтесь на обновления блога, у меня еще много интересных тем. 😉
Читайте также похожие статьи:
Чтобы не пропустить публикацию следующей статьи подписывайтесь на рассылку по E-mail или RSS ленту блога.
Комментарии
Классная статья