Только для читателей Lifeexample возможно открыть интернет-магазин на Moguta.CMS со скидкой в 15%

<<< Событие mousePress на JavaScript и jQuery || Js include и Js require >>>

Javascript классы

11.02.2013
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 ленту блога.

Нравится

Комментарии

  • Джек Воробей

    Классная статья

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

    Не отвечать

    Подписаться на комментарии к этой статье по RSS

    Яндекс.Метрика