представлять
В этой статье мы рассматриваем различные аспекты объектно-ориентированного программирования в Ecmascript (хотя эта тема обсуждалась во многих статьях ранее). Мы рассмотрим эти проблемы больше с теоретической точки зрения. В частности, мы рассмотрим алгоритм создания объекта, как взаимосвязь между объектами (включая основные отношения - наследство) также доступна в обсуждении (я надеюсь, что некоторая концептуальная двусмысленность ООП в JavaScript будет удалена).
Оригинальный английский текст: http://dmitrysoshnikov.com/ecmascript/chapter-7-1-oop-general-heory/
Введение, парадигма и мысли
Перед проведением технического анализа ООП в Ecmascript необходимо освоить некоторые основные характеристики ООП и прояснить основные понятия во введении.
ECMAMSCRING поддерживает различные методы программирования, включая структурированные, объектно-ориентированные, функциональные, императивные и т. Д., А в некоторых случаях он также поддерживает ориентированное на аспект программирование; Но в этой статье обсуждается объектно-ориентированное программирование, поэтому мы дадим определение объектно-ориентированного программирования в Ecmascript:
Ecmascript-это объектно-ориентированный язык программирования, основанный на реализации прототипа.
Существует много различий между ООП на основе прототипов и статическими методами на основе классов. Давайте посмотрим на их прямые подробные различия.
На основе класса и на основе прототипа
Обратите внимание, что важный момент в предыдущем предложении уже указал - он полностью основан на статических классах. Со словом «статическое» мы понимаем статические объекты и статические классы, сильно напечатанные (хотя и не требуются).
Что касается этой ситуации, многие документы на форуме подчеркивают, что это главная причина, по которой они выступают против сравнения «классов с прототипами» в JavaScript. Хотя их различия в реализации (такие как Python и Ruby, основанные на динамических классах) не слишком противоположны точке (некоторые условия написаны, хотя есть некоторые различия в идеях, JavaScript не так альтернативна), но их оппозиция - это статические классы и динамические прототипы (статитика + классы против динамики + прототипы). Чтобы быть точным, статический класс (такой как C ++, Java) и его подчиненные и механизмы определения метода могут позволить нам увидеть точную разницу между IT и реализацией прототипа.
Но давайте перечислим их один за другим. Давайте рассмотрим общие принципы и основные понятия этих парадигм.
На основе статических классов
В классной модели есть концепция о классах и экземплярах. Экземпляры классов также часто называют объектами или парадигмами.
Классы и объекты
Класс представляет абстракцию экземпляра (то есть объект). Это немного похоже на математику, но мы называем это типом или классификацией.
Например (примеры здесь и ниже являются псевдокодом):
Кода -копия выглядит следующим образом:
C = class {a, b, c} // class c, включая функции A, b, c
Характеристики экземпляра: атрибуты (описание объекта) и методы (активность объекта). Сами характеристики также можно рассматривать как объекты: то есть, независимо от того, является ли атрибут записаться, настраивается, устанавливается (Getter/Setter) и т. Д. Следовательно, состояние хранилища объектов (то есть конкретные значения всех атрибутов, описанных в классе), и классы определяют строго инвариантные структуры (свойства) и строгое инвариантное поведение (методы) для их сустей.
Кода -копия выглядит следующим образом:
C = class {a, b, c, метод1, метод2}
c1 = {a: 10, b: 20, c: 30} // class c - это экземпляр: объект С1
c2 = {a: 50, b: 60, c: 70} // class c - это экземпляр: объект С2, который имеет свое собственное состояние (то есть значение атрибута)
Иерархическое наследство
Чтобы улучшить повторное использование кода, классы могут быть расширены от одного к другому, добавляя дополнительную информацию. Этот механизм называется (иерархическим) наследством.
Кода -копия выглядит следующим образом:
D = class extends c = {d, e} // {a, b, c, d, e}
d1 = {a: 10, b: 20, c: 30, d: 40, e: 50}
При вызове партии в экземпляре класса нативный класс обычно ищет метод сейчас. Если это не найдено, перейдите в родительский класс, чтобы найти напрямую. Если это не найдено, перейдите в родительский класс родительского класса для поиска (например, в строгой цепочке наследования). Если обнаружено, что верхняя часть наследования не была обнаружена, результат: объект не имеет подобного поведения и не может получить результат.
Кода -копия выглядит следующим образом:
d1.method1 () // d.method1 (no) -> c.method1 (да)
d1.method5 () // d.method5 (no) -> c.method5 (no) -> Нет результата
По сравнению с методами наследования, которые не копируются на подкласс, свойства всегда осложняются в подклассах. Мы можем видеть, что подкласс D наследуется от родительского класса C: атрибуты A, B, C копируются, а структура D составляет {a, b, c, d, e}}. Однако метод {method1, method2} не копирует прошлое, но наследует прошлое. Следовательно, то есть, если очень глубокий класс обладает некоторыми свойствами, которые объекты вообще не нужны, то подкласс также обладает этими свойствами.
Ключевые понятия на основе классов
Поэтому у нас есть следующие ключевые понятия:
1. Перед созданием объекта класс должен быть объявлен. Во -первых, необходимо определить его класс.
2. Следовательно, объект будет создан из класса, абстрагируемого в его собственное «пиктографическое и сходство» (структура и поведение)
3. Метод обрабатывается через строгую, прямую и неизменную цепочку наследования.
4. Подкласс содержит все атрибуты в цепочке наследования (даже если некоторые атрибуты не требуются подклассом);
5. Создайте экземпляр класса. Класс не может (из -за статической модели) изменить характеристики (атрибуты или методы) своего экземпляра;
6. Примеры (из -за строгих статических моделей) не могут иметь дополнительное поведение или атрибуты, за исключением поведения и атрибутов, объявленных в соответствующем классе в экземпляр.
Давайте посмотрим, как заменить модель ООП в JavaScript, которая является ООП на основе прототипа, который мы рекомендуем.
На основе прототипа
Основная концепция здесь - это динамические изменчивые объекты. Преобразование (полное преобразование, включая не только значения, но и функции) напрямую связано с динамическим языком. Такие объекты, как следующее, могут независимо хранить все свои свойства (свойства, методы) без классов.
Кода -копия выглядит следующим образом:
Object = {a: 10, b: 20, c: 30, метод: fn};
Object.a; // 10
object.c; // 30
object.method ();
Кроме того, из -за динамики они могут легко изменить (добавить, удалять, изменять) свои собственные функции:
Кода -копия выглядит следующим образом:
object.method5 = function () {...}; // Добавить новый метод
Object.d = 40; // Добавить новый атрибут "D"
DELETE object.c; // Удалить атрибут "С"
Object.a = 100; // Измените атрибут "а"
// Результат: Object: {a: 100, b: 20, D: 40, метод: Fn, метод5: fn};
То есть при назначении, если какой -то функции не существует, она создается, и назначение инициализируется с ней, и если она существует, она просто обновляется.
В этом случае повторное использование кода не реализуется путем расширения класса (обратите внимание, что мы не сказали, что класс не может быть изменен, потому что здесь нет концепции класса), но реализуется прототипами.
Прототип - это объект, который используется в качестве примитивной копии других объектов, или если некоторые объекты не имеют своих собственных необходимых характеристик, прототип может использоваться в качестве делегата для этих объектов и действовать как вспомогательный объект.
На основе делегирования
Любой объект может использоваться в качестве объекта прототипа для другого объекта, потому что объект может легко изменить динамику прототипа во время выполнения.
Обратите внимание, что в настоящее время мы рассматриваем введение, а не конкретную реализацию, и когда мы обсудим конкретную реализацию в Ecmascript, мы увидим некоторые из их собственных характеристик.
Пример (Pseudocode):
Кода -копия выглядит следующим образом:
x = {a: 10, b: 20};
y = {a: 40, c: 50};
y. [[прототип]] = x; // x - прототип Y
ya; // 40, свои собственные характеристики
YC; // 50, это также его собственная характеристика
Yb; // 20 Получить из прототипа: yb (no) -> y. [[[Прототип]]. B (Да): 20
Удалить тебя; // Удалить свой собственный "А"
ya; // 10 Получите его из прототипа
z = {a: 100, e: 50}
y. [[[прототип]] = z; // Измените прототип y к z
ya; // 100 получить из прототипа Z
ye // 50, также получен из прототипа z
ZQ = 200 // Добавить новый атрибут в прототип
модификация YQ // также применяется к Y
В этом примере показаны важные функции и механизмы прототипа в качестве атрибутов вспомогательного объекта, точно так же, как вам нужны свои собственные атрибуты. По сравнению с вашими собственными атрибутами эти атрибуты являются атрибутами делегатов. Этот механизм называется делегатом, а модель прототипа, основанная на нем, представляет собой прототип делегата (или прототип на основе делегата). Справочный механизм называется отправкой информации в объект. Если объект не получает ответа, он будет делегирован в прототип, чтобы найти его (требуя от него ответить на сообщение).
Повторное использование кода в этом случае называется наследование на основе делегатов или наследование на основе прототипа. Поскольку любой объект может рассматриваться как прототип, то есть прототип также может иметь свой собственный прототип. Эти прототипы соединены вместе, чтобы сформировать так называемую прототипную цепь. Цепи также иерархические в статических классах, но их можно легко переставить, менять иерархии и структуры.
Кода -копия выглядит следующим образом:
x = {a: 10}
y = {b: 20}
y. [[Прототип]] = x
z = {c: 30}
z. [[Прототип]] = y
ZA // 10
// ZA найден в цепочке прототипа:
// ZA (нет) ->
// Z. [[[Прототип]]. A (нет) ->
// z. [[Прототип]].
Если объект и его цепочка прототипа не могут ответить на отправку сообщения, объект может активировать соответствующий сигнал системы, который может обрабатывать другие делегаты в цепочке прототипа.
Этот системный сигнал доступен во многих реализациях, включая системы, основанные на динамических классах: #DoesNotunderStand в SmallTalk, Method_missing в Ruby; __getAttr__ в Python, __call в PHP; и __nosuchmethod__ реализация в Ecmascript и т. Д.
Пример (реализация Ecmascript от Spidermonkey):
Кода -копия выглядит следующим образом:
var object = {
// поймать системный сигнал, который не может ответить на сообщения
__nosuchmethod__: function (name, args) {
оповещение ([name, args]);
if (name == 'test') {
return '.test () метод обрабатывается';
}
вернуть делегат [name] .apply (this, args);
}
};
var delegate = {
Square: function (a) {
вернуть * a;
}
};
Alert (Object.Square (10)); // 100
alert (object.test ()); // .test () метод обрабатывается
То есть, основываясь на реализации статических классов, когда сообщение нельзя ответить, вывод заключается в том, что текущий объект не имеет необходимых характеристик, но если вы попытаетесь получить его из цепочки прототипа, вы все равно можете получить результат, или у объекта есть характеристика после серии изменений.
Относительно ECMASCRIPT, конкретная реализация: использование прототипа на основе делегатов. Однако, как мы увидим из спецификаций и реализаций, они также имеют свои собственные характеристики.
Конкатенативная модель
Честно говоря, необходимо сказать что -то о другой ситуации (не используется в Ecmascript как можно скорее): эта ситуация, когда прототип сложна от других объектов до исходной замены собственных объектов. В этом случае повторное использование кода является реальной копией (клон) объекта, а не делегата на этапе создания объекта. Этот прототип называется конкатенативным прототипом. Копирование свойств всех прототипов объекта может дополнительно полностью изменить свои свойства и методы, и в качестве прототипа вы также можете изменить себя (в модели на основе делегатов это изменение не изменит поведение существующих объектов, но изменит свои характеристики прототипа). Преимущество этого метода заключается в том, что он может сократить время планирования и делегирования, в то время как недостаток заключается в том, что он использует память.
Утка тип
Вернемся к объекту изменения динамически слабого типа, по сравнению со статической моделью, основанной на классе, необходимо проверить, может ли он делать эти вещи и какой тип (класс) имеет объект, но может ли он быть связан с соответствующим сообщением (то есть, имеет ли он возможность делать это после проверки, необходимо).
Например:
Кода -копия выглядит следующим образом:
// в статической модели
if (exance Object Of someClass) {
// некоторые поведения работают
}
// в динамической реализации
// Неважно, какой тип объекта в настоящее время
// Потому что мутации, типы и характеристики могут быть изменены свободно.
// могут ли важные объекты отвечать на тестовые сообщения
if (isfunction (object.test)) // ecmascript
if object.respond_to? (: test) // Ruby
if hasattr (объект, 'test'): // python
Это называется типом дока. То есть объекты могут быть идентифицированы по их собственным характеристикам при проверке, а не местоположением объектов в иерархии или они принадлежат любому конкретному типу.
Ключевые понятия на основе прототипов
Давайте посмотрим на основные особенности этого метода:
1. Основная концепция - это объект
2. Объекты полностью динамичны и изменяются (теоретически это может быть полностью преобразовано из одного типа в другой)
3. Объекты не имеют строгих классов, которые описывают их собственную структуру и поведение, а объекты не нужны классы.
4. Объекты не имеют классов класса, но могут иметь прототипы. Если они не могут ответить на сообщения, они могут делегировать прототипы.
5. Прототип объекта может быть изменен в любое время во время выполнения;
6. В модели на основе делегатов изменение характеристик прототипа будет влиять на все объекты, связанные с прототипом;
7. В модели конкатенативного прототипа прототип представляет собой оригинальную копию, клонированную из других объектов, и, кроме того, становится полностью независимой оригиналом копирования. Преобразование характеристик прототипа не повлияет на клонированный объект.
8. Если на сообщение нельзя ответить, его вызывающий абонент может принять дополнительные меры (например, изменить планирование)
9. Отказ объекта может быть определен не по их уровню и каким классом они принадлежат, а текущими характеристиками.
Тем не менее, есть еще одна модель, которую мы также должны рассмотреть.
На основе динамических классов
Мы полагаем, что разница «прототип класса против», показанная в приведенном выше примере, не так важна в этой динамической модели на основе класса (особенно, если цепочка прототипа неизменна, все еще необходимо рассмотреть статический класс для более точного различия). В качестве примера, его также можно использовать в Python или Ruby (или на других подобных языках). Все эти языки используют динамические парадигмы на основе классов. Однако в некоторых аспектах мы можем увидеть некоторые функции, реализованные на основе прототипов.
В следующем примере мы можем видеть, что только на основе прототипа делегата мы можем усилить класс (прототип), тем самым влияя на все объекты, связанные с этим классом, мы также можем динамически изменить класс этого объекта во время выполнения (предоставление нового объекта для делегата) и т. Д.
Кода -копия выглядит следующим образом:
# Python
класс A (объект):
def __init __ (self, a):
self.a = a
def Square (Self):
вернуть Self.a * self.a
a = a (10) # создать экземпляр
Печать (AA) # 10
Ab = 20 # предоставьте новое свойство для класса
Печать (AB) # 20 Вы можете получить к нему доступ в экземпляре «А»
a = 30 # создать собственное свойство
Печать (AB) # 30
del ab # удалить свои собственные атрибуты
Печать (AB) # 20 - Получите его из класса снова (прототип)
# Как модель на основе прототипа
# Может изменить прототип объекта во время выполнения
класс B (объект): # пустой класс B
проходить
b = b () # b экземпляр
b .__ Class__ = a # класс динамического изменения (прототип)
BA = 10 # создать новый атрибут
Print (b.square ()) # 100 - метод класса A доступен в настоящее время
# Может отображать ссылки на удаленные классы
дель а
дель б
# Но у объекта все еще есть неявные ссылки, и эти методы все еще доступны
Print (b.square ()) # 100
# Но вы не можете изменить класс в это время
# Это функция реализации
b .__ class__ = dict # ошибка
Реализация в Ruby аналогична: она также использует полностью динамичные классы (кстати, в текущей версии Python, по сравнению с Ruby и Ecmascript, усиление классов (прототипы) не может быть сделано), мы можем полностью изменить характеристики объекта (или класса) (добавить методы/атрибуты в класс, и эти изменения будут влиять на существующие объекты), но он не может динамически изменять класс объекта.
Тем не менее, эта статья не специально для Python и Ruby, поэтому мы не будем много говорить, давайте продолжим обсуждать сам Ecmascript.
Но до этого мы должны взглянуть на «синтетический сахар», который встречается в некоторых ООП, потому что многие предыдущие статьи о JavaScript часто охватывают эти проблемы.
Единственное неправильное предложение, которое необходимо отметить в этом разделе: «JavaScript - это не класс, оно имеет прототип и может заменить класс». Очень необходимо знать, что не все классовые реализации совершенно разные. Даже если мы можем сказать «JavaScript отличается», также необходимо рассмотреть (помимо концепции «классов»), что есть и другие связанные функции.
Другие функции различных реализаций ООП
В этом разделе мы кратко представляем другие функции и различные реализации ООП о повторном использовании кода, включая реализации ООП в ECMascript. Причина в том, что предыдущее появление реализации ООП в JavaScript имеет некоторые привычные ограничения на размышления. Единственным основным требованием является то, что его следует доказано технически и идеологически. Нельзя сказать, что вы не обнаружили функцию синтаксического сахара в других реализациях ООП, и вы не знаете, что JavaScript не является чистым языком ООП, что неправильно.
Полиморфный
В Ecmascript есть несколько полиморфизмов, в которых означают объекты.
Например, функция может быть применена к разным объектам, как и свойства нативного объекта (потому что это значение определяется при входе в контекст выполнения):
Кода -копия выглядит следующим образом:
функциональный тест () {
предупреждение ([this.a, this.b]);
}
test.call ({a: 10, b: 20}); // 10, 20
test.call ({a: 100, b: 200}); // 100, 200
var a = 1;
var b = 2;
тест(); // 1, 2
Тем не менее, есть исключения: Date.Prototype.getTime () Метод, согласно стандарту, всегда должен быть объект даты, в противном случае будет брошено исключение.
Кода -копия выглядит следующим образом:
alert (date.prototype.gettime.call (new Date ())); // время
alert (date.prototype.gettime.call (new String (''))); // typeError
Так называемый полиморфизм параметров при определении функции эквивалентен всем типам данных, но он принимает только полиморфные параметры (например, метод сортировки. Сортировка и его параметры - функция сортировки полиморфизма). Кстати, приведенный выше пример также может рассматриваться как полиморфизм параметра.
Метод в прототипе может быть определен как пустой, и все созданные объекты должны быть пересмотрены (реализованы) метод (то есть «один интерфейс (подпись), несколько реализаций»).
Полиморфизм связан с типом утки, о котором мы упомянули выше: то есть тип объекта и его положение в иерархии не так важны, но его можно легко принять, если он имеет все необходимые функции (то есть общий интерфейс важен, и реализация может быть разной).
Упаковка
Часто есть неправильные взгляды на инкапсуляцию. В этом разделе мы обсудим некоторые синтаксические сахара в реализации ООП - то есть известные модификаторы: в этом случае мы обсудим некоторые реализации ООП, удобные «сахара» - хорошо известные модификаторы: частные, защищенные и общедоступные (или модификаторы доступа объектов или модификаторы доступа).
Здесь я хотел бы напомнить вам об основной цели инкапсуляции: инкапсуляция - это абстрактное дополнение, а не выбирать скрытого «вредоносного хакера», который пишет что -то прямо в ваш класс.
Это большая ошибка: используйте скрытую, чтобы скрыть.
Уровни доступа (частные, защищенные и общедоступные), для удобства программирования, были реализованы во многих объектно-ориентированных (действительно очень удобный синтаксис-сахар), а также описывают и создают систему более абстрактно.
Они можно увидеть в некоторых реализациях (таких как Python и Ruby уже упомянутые). С одной стороны (в Python) эти __private _protected Properties (через спецификацию имен подчеркивания) недоступны извне. Python, с другой стороны, можно получить извне через специальные правила (_classname__field_name).
Кода -копия выглядит следующим образом:
класс A (объект):
def __init __ (self):
Self.public = 10
Self .__ Private = 20
def get_private (self):
вернуть себя .__ Частный
# снаружи:
a = a () # Пример
Печать (A.Public) # OK, 30
print (a.get_private ()) # ok, 20
print (a .__ Private) # Не удалось, потому что он может быть доступен только в
# Но в Python можно получить специальные правила
Печать (A._A__Private) # OK, 20
В Ruby: с одной стороны, он обладает способностью определять характеристики частного и защищенного, а с другой стороны, существуют также специальные методы (такие как exante_variable_get, exance_varible_set, отправка и т. Д.) Для получения инкапсулированных данных.
Кода -копия выглядит следующим образом:
класс а
DEF Инициализировать
@a = 10
конец
def public_method
private_method (20)
конец
Частный
def private_method (b)
вернуть @a + b
конец
конец
a = a.new # новый экземпляр
a.public_method # ok, 30
AA # не удалось, @A - это переменная частного экземпляра
# "private_method" является частным и может быть доступен только в классе A
A.private_method # ошибка
# Но есть специальное имя метода метаданных, которое может получить данные
a.send (: private_method, 20) # OK, 30
a.instance_variable_get (:@a) # ok, 10
Основная причина - это инкапсуляция (обратите внимание, что я не использую «скрытые») данные, которые программист хочет получить. Если эти данные в каком -то смысле изменяются неправильно или есть какие -либо ошибки, то вся ответственность - это программист, но не просто «раскручивать» или «изменить определенные поля случайно». Но если это происходит часто, это плохая привычка и стиль программирования, потому что обычно это общий API для «общения» с объектами.
Повторите, основная цель инкапсуляции состоит в том, чтобы абстрагировать его от пользователя вспомогательных данных, а не предотвратить скрытие хакеров. Более серьезная инкапсуляция не заключается в использовании частных для изменения данных для достижения цели безопасности программного обеспечения.
Инкапсуляция вспомогательных объектов (локальные), мы предоставляем выполнимость для изменений поведения общественных интерфейсов с минимальными затратами, локализацией и прогнозирующими изменениями, что в точности является целью инкапсуляции.
Кроме того, важной целью метода сеттера является абстрактные сложные расчеты. Например, element.innerhtml Setter - Abstract Comtence - «HTML внутри этого элемента теперь следующим образом», а функция сеттера в атрибуте Innerhtml будет трудно рассчитать и проверить. В этом случае проблема в основном включает в себя абстракцию, но инкапсуляция может произойти.
Концепция инкапсуляции связана не только с ООП. Например, это может быть простая функция, которая инкапсулирует все виды расчетов, так что она абстрактная (не нужно сообщать пользователю, например, как реализована функция Math.Round (......), пользователь просто называет его). Это инкапсуляция, обратите внимание, что я не сказал, что это «частная, защищенная и публичная».
Текущая версия спецификации ECMascript не определяет частные, защищенные и публичные модификаторы.
Тем не менее, на практике можно увидеть что -то названное «Имитировать инкапсуляцию JS». Как правило, цель этого контекста должна использоваться (как правило, сам конструктор). К сожалению, часто реализация этой «имитации» может быть сделана, и программисты могут создавать псевдо-абсолютно неапстроктные сущности для установки метода «Getter/Setter» (я говорю это снова, это неправильно):
Кода -копия выглядит следующим образом:
функция a () {
var _a; // "Частный" а
this.geta = function _geta () {
вернуть _a;
};
this.seta = function _seta (a) {
_a = a;
};
}
var a = new a ();
A.Seta (10);
оповещение (A._A); // неопределенная, "частная"
Alert (a.geta ()); // 10
Следовательно, все понимают, что для каждого созданного объекта также создается метод GETA/SETA, что также является причиной увеличения памяти (по сравнению с определением прототипа). Хотя, теоретически, объект может быть оптимизирован в первом случае.
Кроме того, в некоторых статьях JavaScript часто упоминается концепция «частного метода». Примечание. Стандарт ECMA-262-3 не определяет какую-либо концепцию «частного метода».
Однако в некоторых случаях он может быть создан в конструкторе, поскольку JS является идеологическим языком - объекты полностью изменяются и имеют уникальные характеристики (при определенных условиях в конструкторе некоторые объекты могут получить дополнительные методы, в то время как другие не могут).
Кроме того, в JavaScript, если инкапсуляция по-прежнему неверно истолкована как способ предотвращения автоматического написания определенных значений определенных значений вместо использования метода сеттера, так называемые «скрытые» и «частные» не являются «скрытыми». Некоторые реализации могут получить значения в соответствующей цепочке областей (и все соответствующие объекты переменных), вызывая контекст с функцией Eval (которая может быть проверена на Spidermonkey 1.7).
Кода -копия выглядит следующим образом:
eval ('_ A = 100', A.Geta); // или a.seta, потому что «_a» методы на [[[Scope]]
a.geta (); // 100
В качестве альтернативы, в реализации, разрешайте прямой доступ к активному объекту (например, носорога), доступа к соответствующим свойствам объекта, значение внутренней переменной может быть изменено:
Кода -копия выглядит следующим образом:
// носорог
var foo = (function () {
var x = 10; // "частный"
return function () {
print (x);
};
}) ();
foo (); // 10
foo .__ Parent __. X = 20;
foo (); // 20
Иногда в JavaScript цель «частных» и «защищенных» данных достигается путем подчеркивания переменных (но по сравнению с Python, это всего лишь спецификация именования):
var _myprivatedata = 'testString';
Он часто используется для охвата контекста выполнения с помощью кронштейнов, но для реальных вспомогательных данных он не имеет прямой связи с объектами, и это просто удобно абстрагировать от внешних API:
Кода -копия выглядит следующим образом:
(function () {
// Инициализировать контекст
}) ();
Множественное наследство
Многоэтажный-очень удобный синтаксический сахар для улучшений повторного использования кода (если мы можем наследовать по одному классу за раз, почему мы не можем наследовать по 10 за раз?). Тем не менее, из -за некоторых недостатков во многих наследованиях, оно не стало популярным в реализации.
Ecmascript не поддерживает множественное наследование (то есть только один объект может использоваться в качестве прямого прототипа), хотя языки самопрограммирования его предков имеют такие возможности. Но в некоторых реализациях (таких как Spidermonkey) с использованием __nosuchmethod__ можно использовать для управления планированием и делегировать для замены прототипов цепочек.
Микшины
Mixins - это удобный способ повторного использования кода. Смешины были рекомендованы в качестве замены для множественного наследства. Все эти независимые элементы могут быть смешаны с любым объектом для расширения их функциональности (поэтому объекты также могут смешивать несколько микшинов). Спецификация ECMA-262-3 не определяет концепцию «микшинов», но в соответствии с определением микшинов и Ecmascript есть динамические изменчивые объекты, поэтому нет никаких препятствий для простого расширения функций с использованием микшинов.
Типичные примеры:
Кода -копия выглядит следующим образом:
// помощник для увеличения
Object.extend = function (destination, source) {
для (свойства в источнике) if (source.hashownproperty (свойство)) {
пункт назначения [свойство] = источник [свойство];
}
ДОСТОЯНИЕ ВОЗВРАЩЕНИЯ;
};
var x = {a: 10, b: 20};
var y = {c: 30, d: 40};
Object.extend (x, y); // смешать y в x
оповещение ([XA, XB, XC, XD]); 10, 20, 30, 40
Обратите внимание, что я принял эти определения («Mixin», «Mix») в цитатах, упомянутых в ECMA-262-3. В спецификации нет такой концепции, и это не смесь, а обычно используемый способ расширить объекты через новые функции. (Концепция микшинов в Ruby определяется. Mixin создает ссылку на модуль вместо того, чтобы просто копировать все атрибуты модуля в другой модуль - Фактически: создание дополнительного объекта (прототип) для делегата).
Черты
Черты и микшины имеют сходные концепции, но они имеют много функций (по определению, потому что микшины могут быть применены, чтобы они не могли содержать состояния, потому что он может вызвать конфликты именования). Согласно Ecmascript, черты и микшины следуют тем же принципам, поэтому спецификация не определяет концепцию «признаков».
интерфейс
Интерфейсы, реализованные в некоторых ООП, похожи на микшины и черты. Однако, по сравнению со микшинами и признаками, интерфейс обеспечивает соблюдение класса реализации для реализации поведения подписи своего метода.
Интерфейсы можно рассматривать как абстрактные классы. Однако по сравнению с абстрактными классами (методы в абстрактных классах могут реализовать только часть из них, а другая часть все еще определяется как подписи), наследование может быть только одним базовым классом наследования, но он может наследовать несколько интерфейсов. Вот почему интерфейсы (смешанные множественные) можно рассматривать как альтернативу множеству наследования.
Стандарт ECMA-262-3 не определяет концепцию «интерфейса» и концепцию «абстрактного класса». Однако, как и подражать, он может быть реализован объектом, который «пустой» метод (или исключение, добавленное пустым методом, сообщая разработчику, что этот метод должен быть реализован).
Комбинация объекта
Комбинация объектов также является одним из методов повторного использования динамического кода. Комбинации объектов отличаются от высокой гибкости наследования, которое реализует динамический и переменный делегат. И это также основано на основном прототипе. В дополнение к динамическим изменяющимся прототипам объектом может быть агрегированный объект делегата (создать комбинацию в качестве результата - агрегация), а также отправлять сообщения в объект и делегировать делегату. Это может быть более двух делегатов, потому что его динамическая природа определяет, что его можно изменить во время выполнения.
Упомянутый пример __nosuchmethod__ является таким, но также позволяет нам показать, как явно использовать делегатов:
Например:
Кода -копия выглядит следующим образом:
var _delegate = {
foo: function () {
Alert ('_ Delegate.foo');
}
};
var aggregate = {
делегат: _delegate,
foo: function () {
вернуть this.delegate.foo.call (это);
}
};
Aggregate.foo (); // Delegate.foo
aggregate.delegate = {
foo: function () {
оповещение ('foo от нового делегата');
}
};
Aggregate.foo (); // foo от нового делегата
Эта объектная связь называется «has-a», а интеграция-это отношения «is-a».
Из -за отсутствия комбинаций дисплеев (гибкость по сравнению с наследством) также можно добавить промежуточный код.
AOP функции
В качестве функции, ориентированной на аспект, можно использовать декораторы функций. Спецификация ECMA-262-3 не имеет четко определенной концепции «декораторов функций» (в отличие от Python, слово официально определено в Python). Тем не менее, функции с функциональными параметрами в некоторых отношениях являются декоративными и активируются (применяя так называемые предложения):
Самый простой пример декоратора:
Кода -копия выглядит следующим образом:
Функция CheckDecorator (OriginalFunction) {
return function () {
if (foobar! = 'test') {
оповещение ('неправильный параметр');
вернуть ложь;
}
вернуть оригинал function ();
};
}
функциональный тест () {
Alert ('test function');
}
var testwithCheck = CheckDecorator (тест);
var foobar = false;
тест(); // 'Тестовая функция'
testwithcheck (); // 'Неправильный параметр'
foobar = 'test';
тест(); // 'Тестовая функция'
testwithcheck (); // 'Тестовая функция'
в заключение
В этой статье мы разобрались с Внедрением в ООП (я надеюсь, что эта информация была для вас полезна), и в следующей главе мы будем продолжать реализовать ECMASCRIPT в объектно-ориентированном программировании.