Дисциплины - Объектно-ориентированное программирование

ООП в JavaScript - Прототипирование

В объектном прототипировании, то, что принято называть классами, это сочитание функции-конструктора и ассоциированного с ней прототипа.

Конструктор – это функция предназначенная для инициализации объектов. Важной особенностью использования конструктора является свойство prototype конструктора в качестве прототипа нового объекта. Роль конструктора в языке JavaScript может играть любая функция, для которой определено свойство prototype.

function Range(from, to){
 this.from = from; // определение свойств
 this.to = to;
}
Range.prototype = { // определение методов
 includes: function(x){
    return this.from <= x && x <= this.to;
  },
 foreach: function(f){
    for(var x= Math.ceil(this.from); x<= this.to; x++){
     f(x);
    }
  },
 toString: function(){
    return "(" + this.from + "…" + this.to + ")";
  }
};
var f = new Range(1,3);
f.includes(2) // true: число 2 входит в диапазон
f.foreach(console.log) // выведет: 1 2 3

Функция Range поставляется с имеющимся по умолчанию свойством prototype. Как видно из листинга, объект f получает объект сохраненный в Range.prototype.

В JavaScript имеется специальная функция для получения прототипа существующего объекта. Например, после создания объекта f мы можем провести следующую проверку:

Object.getPrototypeOf(f) === Range.prototype

Процесс определения класса можно свести к следующим этапам:

  1. Создать функцию конструктор.

  2. Определить методы экземпляров в прототипе конструктора.

  3. Определить свойства в самом конструкторе.

Рассмотрим еще один способ определения классов объектов, где методы определяются как свойства функции объекта-прототипа.

function Complex(real, imaginary) {
    this.x = real;      // Вещественная часть числа
    this.y = imaginary; // Мнимая часть числа
}
// Возвращает модуль комплексного числа. Он определяется как расстояние
// на комплексной плоскости до числа от начала координат (0,0).

Complex.prototype.magnitude = function() {
    return Math.sqrt(this.x*this.x + this.y*this.y);
};

// Возвращает комплексное число с противоположным знаком.
Complex.prototype.negative = function() {
  return new Complex(-this.x,  -this.y);
};
// Складывает данное комплексное число с заданным и возвращает 
// сумму в виде нового объекта.
Complex.prototype.add = function(that) {
    return new Complex(this.x + that.x, this.y + that.y);
}
// Умножает данное комплексное число на заданное и возвращает 
// произведение в виде нового объекта.
Complex.prototype.multiply = function(that) {
    return new Complex(this.x * that.x - this.y * that.y,
                       this.x * that.y + this.y * that.x);
}
// Преобразует объект Complex в строку в понятном формате.
// Вызывается, когда объект Complex используется как строка.
Complex.prototype.toString = function() {
    return "{" + this.x + "," + this.y + "}";
};
// Проверяет равенство данного комплексного числа с заданным.
Complex.prototype.equals = function(that) {
    return this.x == that.x && this.y == that.y;
}
// Возвращает вещественную часть комплексного числа.
// Эта функция вызывается, когда объект Complex рассматривается
// как числовое значение.
Complex.prototype.valueOf = function() { return this.x; }
/*
 * Третий шаг в определении класса – это определение методов класса,
 * констант и других необходимых свойств класса как свойств самой
 * функциииконструктора (а не как свойств объектаапрототипа
 * конструктора). Обратите внимание, что методы класса не используют
 * ключевое слово this, они работают только со своими аргументами.
 */
// Складывает два комплексных числа и возвращает результат.
Complex.prototype.add = function (a, b) {
    return new Complex(a.x + b.x, a.y + b.y);
};
// Умножает два комплексных числа и возвращает полученное произведение.
Complex.prototype.multiply = function(a, b) {
    return new Complex(a.x * b.x - a.y * b.y,
                       a.x * b.y + a.y * b.x);
};
// Несколько предопределенных комплексных чисел. 
// Они определяются как свойства класса, в котором могут использоваться как "константы".
// (Хотя в JavaScript невозможно определить свойства, доступные только для чтения.)
Complex.ZERO = new Complex(0,0);
Complex.ONE = new Complex(1,0);
Complex.I = new Complex(0,1);

Свойство prototype автоматически определяется, как только мы обращаемся к любой функции с ключевым словом new. Однако, любой конструктор можно переписать так, чтобы он вызвался без ключевого слова new. Для этого необходимо задействовать функцию Object.create():

function User(name, password){
self = this instanceof User ? this: Object .create(User.prototype);
  self.name = name;
  self.password = password;
  return self;
}

Храним свойства – в конструкторе, методы – в прототипе

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

function User(name, password){
 this.toString = function(){
  return “User ” + name;
 }
 this.checkPassword = function(password){
  return password;
 }
}

Храним состояние экземпляра в объекте-экземпляре

Изменяющиеся данные при совместном использовании разными объектами могут стать источником проблем, а прототипы совместно используются всеми созданными на основе их объектами. Отсюда и следуюет вывод: изменяемые состояния храним в объекте-экземпляре.

function Tree(x){
  this.value = x;
  this.children = [];
}
Tree.prototype = {
  addChild: function(x){
   this.children.push(x);
  }
}

Особенности работы this

У каждой функции есть неявная связь с this, чье значение определяется при вызове функции. Следовательно, this в методе прототипе отличается от this в функции обратнгого вызова.

Рассмотрим пример, где в прототипе в функции обратного вызова используется ссылка на this:

function User(name){
  this.name = name;
}
User.prototype.read = function(){
  var lines = ['a','b','c'];
  return lines.map(function(line){
   return line+'-' + this.name;
  }, this);
}
a = new User('Вася');
a.read();

Обратите внимание на второй параметр функции map. Чтобы функция map явным образом увидела ссылки this, мы передаем this вторым параметром.

Но не все функции обладают такой степенью продуманности. Если функция не принимает дополнительного аргумента на понадобится способ сохранения связи с this:

User.prototype.read = function(){
 var self = this;
 …
}

Прототипное наследование

Прототип функции является обычным объектом, и поэтому существует несколько способов копирования его функциональных возможностей (методы и свойства), чтобы осуществить наследование. Чтобы создать класс, наследующий и расширяющий прототип другого класса, можно воспользоваться Object.create():

function Actor(scene, x, y){
  this.scene = scene;
  this.x = x;
  this.y = y;
}
function MyClass(){

}
MyClass.prototype = Object.create(Actor.prototype);

Сейчас в классе MyClass, мы можем переопределить свойства и методы родительского класса Actor.

Чтобы определить цепочку прототипов (объект тип Actor) как человека (объект типа Person), человека (объект типа Person) - как млекопетающее и т.д., до самого объекта типа Object, лучше всего создать экземпляр одного объекта в качестве прототипа другого объекта:

SubClass.prototype = new SuperClass();
SubSubClass.prototype = new SubClass.prototype;

Таким образом, цепочка прототипов сохраняется.
Все элементы модели DOM наследуются от конструктора класса HTMLElement, который также можно расширять.

Количество комментариев: 0

Для того, чтобы оставить коментарий необходимо зарегистрироваться
814301 БГУИР
814302 БГУИР
814303 БГУИР
894351 БГУИР
90421 БГУИР


Изображения Видео

1. Абстрактная фабрика https://www.youtube.com/watch?v=1mVONOCxfLg
2. Фабричный метод https://www.youtube.com/watch?v=5UqUDR6_2cY
3. Шаблон декоратор https://www.youtube.com/watch?v=Lwb9bm8yKD0
4. Dessign patterns on PHP https://github.com/domnikl/DesignPatternsPHP
5. Приёмы объектно-ориентированного проектирования. Паттерны проектирования Э. Гамма, Р. Хелм, Р. Джонсон, Д. Влиссидес; [пер. с англ.: А. Слинкин науч. ред.: Н. Шалаев]. — Санкт-Петербург [и др.] : Питер, 2014. — 366 с. : ил. ; 24 см.
6. Приемы объектно-ориентированного проектирования. Паттерны проектирования Э. Гамма, Р. Хелм, Р. Джонсон, Д. Влиссидес; [пер. с англ.: А. Слинкин науч. ред.: Н. Шалаев]. — Санкт-Петербург [и др.] : Питер, 2014. — 366 с. : ил. ; 24 см.
7. Ajax http://erud.by/ajax
8. Ajax http://erud.by/ajax
9. Ajax http://erud.by/ajax
10. Документация Laravel http://laravel.com
Задание к курсовой работе
Задание к курсовой работе
Вопросы к экзамену