Примеры ооп javascript: Понимание ООП в JavaScript [Часть 1] / Блог компании Enterra / Хабр

Содержание

Понимание ООП в JavaScript [Часть 1] / Блог компании Enterra / Хабр

— Прототипное наследование — это прекрасно
JavaScript — это объектно-ориентированный (ОО) язык, уходящий корнями в язык Self, несмотря на то, что внешне он выглядит как Java. Это обстоятельство делает язык действительно мощным благодаря некоторым приятным особенностям.

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

К счастью, в ECMAScript 5 появилось множество вещей, которые позволили поставить язык на правильный путь (некоторые из них раскрыты в этой статье). Также будет рассказано о недостатках дизайна JavaScript и будет произведено небольшое сравнение с классической моделью прототипного ОО (включая его достоинства и недостатки).

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

1. Объекты

Объект в JavaScript — это просто коллекция пар ключ-значение (и иногда немного внутренней магии).

Однако, в JavaScript нет концепции класса. К примеру, объект с свойствами {name: Linda, age: 21} не является экземпляром какого-либо класса или класса Object. И Object, и Linda являются экземплярами самих себя. Они определяются непосредственно собственным поведением. Тут нет слоя мета-данных (т.е. классов), которые говорили бы этим объектам как нужно себя вести.

Вы можете спросить: «Да как так?», особенно если вы пришли из мира классических объектно-ориентированных языков (таких как Java или C#). «Но если каждый объект обладает собственным поведением (вместо того чтобы наследовать его от общего класса), то если у меня 100 объектов, то им соответствует 100 разных методов? Разве это не опасно? А как мне узнать, что, например, объект действительно является Array-ем?»

Чтобы ответить на все эти вопросы необходимо забыть о классическом ОО-подходе и начать всё с нуля. Поверьте, оно того стоит.

Модель прототипного ОО приносит несколько новых динамичных и экспрессивынх путей решения старых проблем. В ней также представлены мощные модели расширения и повторного использования кода (а это и интересует людей, которые говорят об объектно-ориентированном программировании). Однако, эта модель даёт меньше гарантий. Например, нельзя полагаться, что объект x всегда будет иметь один и тот же набор свойств.

1.1. А что такое объекты?

Ранее упоминалось, что объекты — это просто пары уникальных ключей с соответствующими значениями — такие пары называются свойства. К примеру, вы хотите описать несколько аспектов своего старого друга (назовём его Мишей, он же Mikhail), таких как возраст, имя и пол:

Объект в JavaScript создаётся с помощью функции Object.create
. Эта функция из родителя и опционального набора свойств создаёт новую сущность. Пока что мы не будем беспокоиться о параметрах.

Пустой объект — это объект без родителя, без свойств. Посмотрим на синтакс создания такого объекта в JavaScript:

var mikhail = Object.create(null)
1.2. Создание свойств

Так, значит у нас уже есть объект, но у него пока нет свойств — мы должны исправить эту ситуацию для описания нашего объекта Mikhail.

Свойства в JavaScript являются динамическими. Это означает, что мы их можем создавать или удалять в любое время. Свойства уникальны в том смысле, что ключ свойства внутри объекта соответствует ровно одному значению.

Создадим новые свойства через функцию Object.defineProperty, которая в качестве аргументов использует объект, имя свойства для создания и дескриптор, описывающий семантику свойства.

Object.defineProperty(mikhail, 'name', { value:        'Mikhail'
                                       , writable:     true
                                       , configurable: true
                                       , enumerable:   true })

Object.defineProperty(mikhail, 'age', { value:        19
                                      , writable:     true
                                      , configurable: true
                                      , enumerable:   true })

Object.defineProperty(mikhail, 'gender', { value:        'Male'
                                         , writable:     true
                                         , configurable: true
                                         , enumerable:   true })

Функция Object.defineProperty создаёт новое свойство, если свойство с данным ключём ранее не существовало (в противном случае произойдёт обновление семантики и значения существующего свойства).

Кстати, вы также можете использовать Object.defineProperties когда необходимо добавить больше одного свойства в объект:

Object.defineProperties(mikhail, { name:   { value:        'Mikhail'
                                           , writable:     true
                                           , configurable: true
                                           , enumerable:   true }

                                 , age:    { value:        19
                                           , writable:     true
                                           , configurable: true
                                           , enumerable:   true }

                                 , gender: { value:        'Male'
                                           , writable:     true
                                           , configurable: true
                                           , enumerable:   true }})

Очевидно, что оба вызова аналогичны, они вполне конфигурируемы, но не предназначены для конечного пользователя кода. Лучше создать уровень абстракции над ними.
1.3. Дескрипторы

Маленькие объекты, которые содержат в себе семантику, называются дескрипторами (мы их использовали при вызове Object.defineProperty). Дескрипторы бывают одного из двух типов — дескрипторы данных и дескрипторы доступа.

Оба типа дескрипторов содержат флаги, которые определяют как свойство будет рассматриваться языком. Если флаг не установлен, то его значение по умолчанию false (к сожалению это не всегда хорошее значение по умолчанию, что влечёт возрастание объёма описания дескрипторов).

Рассмотрим некоторые флаги:

  • writable — значение свойства может быть изменено, используется только для дескрипторов данных.
  • configurable — тип свойства может быть изменён или свойство может быть удалено.
  • enumerable — свойство используется в общем перечислении.
    Дескрипторы данных таковы, что определяют конкретное значение, которое соответствует дополнительному value-параметру, описывающему конкретные данные, привязанные к свойству:
  • value — значение свойства

Дескрипторы доступа определяют доступ к конкретному значению через getter-ы и setter-ы функций. Если не установлены, то по умолчанию равны undefined.

  • get() — функция вызывается без аргументов, когда происходит запрос к значению свойства.
  • set(new_value) — функция вызывается с аргументом — новым значением для свойства, когда пользователь пытается

модифицировать значение свойства.
1.4. Стремимся к лаконичности

К счастью, дескрипторы свойств — это не единственный путь работать со свойствами в JavaScript — их можно создавать более лаконично.

JavaScript также понимает ссылки на свойства, используя так называемую скобочную запись. Основное правило записывается следующим образом:

<bracket-access> ::= <identifier> "[" <expression> "]"

Тут identifier — это переменная, которая хранит объект, содержащий свойство, значение которого мы хотим установить, а expression — любое валидное JavaScript-выражение, определяющее имя свойства. Нет ограничений на то, какое имя может иметь свойство, всё позволяется.

Таким образом, мы можем переписать предыдущий пример:

mikhail['name']   = 'Mikhail'
mikhail['age']    = 19
mikhail['gender'] = 'Male'

На заметку: все имена свойств в конечном счёте конвертируются в строку, т.е. записи
object[1]
, object[[1]], object[‘1’] и object[variable] (где значение variable равно 1) эквивалентны.

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

Общее правило для точечной записи:

<dot-access> ::= <identifier> "." <identifier-name>

Таким образом, предыдущий пример стал ещё более красивым:
mikhail.name   = 'Mikhail'
mikhail.age    = 19
mikhail.gender = 'Male'

Оба варианта синтаксиса выполняют эквивалентный процесс создания свойств, с выставлением семантических флагов в значение
true
.
1.5. Доступ к свойствам

Очень просто получить значение, хранящиеся в заданном свойстве — синтаксис очень похож на создание свойства с той лишь разницей, что в нём нет присваивания.
Например, если мы хотим узнать возраст Миши, то мы напишем:
mikhail['age']
// => 19

Но если мы попробуем получить значение свойства, которого не существует в нашем объекте, то мы получим undefined:
mikhail['address']
// => undefined
1.6. Удаление свойств

Для удаления свойства из объекта в JavaSCript предусмотрен оператор delete. К примеру, если вы хотите удалить свойство gender из нашего объекта mikhail:
delete mikhail['gender']
// => true

mikhail['gender']
// => undefined

Оператор delete вернёт true, если свойство удалено, и
false
в противном случае. Не будем углубляться в то, как работает этот оператор. Но если вам всё-таки интересно, то вы можете почитать самую прекрасную статью о том как работает delete.
1.6. Getter-ы и setter-ы

Getter-ы и setter-ы обычно используются в классических объектно-ориентированных языках для обеспечения инкапсуляции. Они не особо нужны в JavaScript, но, у нас динамический язык, и я против этой функциональности.

Но, с любой точки зрения, они позволяет обеспечить proxy для запросов на чтение и запись свойств. Например, у нас были отдельные слоты для имени и фамилии, но мы хотим иметь удобный способ читать и устанавливать их.

Для начала, создадим имя и фамилию нашего друга, описав соответствующие свойства:

Object.defineProperty(mikhail, 'first_name', { value:    'Mikhail'
                                             , writable: true })

Object.defineProperty(mikhail, 'last_name', { value:    'Weiß'
                                            , writable: true })

Затем мы опишем общий способ получения и установки сразу двух свойств за один раз — назовём их объединение name:
// () → String
// Returns the full name of object.
function get_full_name() {
    return this.first_name + ' ' + this.last_name
}

// (new_name:String) → undefined
// Sets the name components of the object, from a full name.
function set_full_name(new_name) { var names
    names = new_name.trim().split(/\s+/)
    this.first_name = names[⁣'0'] || ''
    this.last_name  = names['1'] || ''
}

Object.defineProperty(mikhail, 'name', { get: get_full_name
                                       , set: set_full_name
                                       , configurable: true
                                       , enumerable:   true })

Теперь, каждый раз когда мы попытаемся узнать значение свойства
name
нашего друга на самом деле вызовется функция get_full_name:
mikhail.name
// => 'Mikhail Weiß'

mikhail.first_name
// => 'Mikhail'

mikhail.last_name
// => 'Weiß'

mikhail.last_name = 'White'
mikhail.name
// => 'Mikhail White'

Мы также можем установить name объекта, обратившись к соответствующему свойству, но на самом деле вызов set_full_name выполнит всю грязную работу:
mikhail.name = 'Michael White'

mikhail.name
// => 'Michael White'

mikhail.first_name
// => 'Michael'

mikhail.last_name
// => 'White'

Есть сценарии, в которых действительно удобно так делать, но стоит помнить, что такой механизм работает очень медленно.
Кроме того, следует учитывать что getter-ы и setter-ы обычно используются в других языках для инкапсуляции, а в ECMAScript 5 вы всё ещё не можете так делать — все свойства объекта являются публичными.
1.8. Перечисление свойств

Ввиду того, что свойства являются динамическими, JavaScript обеспечивает функционал по проверке набора свойств объекта. Существует два способа перечисления всех свойств объекта, зависящих от того, какой вид свойств вас интересует.

Первый способ заключается в вызове функции Object.getOwnPropertyNames, которая вернёт вам Array, содержащий имена всех свойств, установленных для данного объекта — мы будет называть эти свойства собственными. Например, посмотрим, что мы знаем о Мише:

Object.getOwnPropertyNames(mikhail)
// => [ 'name', 'age', 'gender', 'first_name', 'last_name' ]

Второй способ заключается в использовании Object.keys, который вернёт список собственных свойств, которые помечены флагом enumerable :
Object.keys(mikhail)
// => [ 'name', 'age', 'gender' ]
1.9. Литералы

Простой способ создать объект заключается в использовании литерального синтаксиса JavaScript. Литеральный объект определяет новый объект, родитель которого Object.prototype (о родителях поговорим немного позже).

В любом случае, синтаксис литеральных объектов позволяет определять простые объекты и инициализировать их свойства. Перепишем пример создания объекта Mikhail:

var mikhail = { first_name: 'Mikhail'
              , last_name:  'Weiß'
              , age:        19
              , gender:     'Male'

              // () → String
              // Returns the full name of object.
              , get name() {
                    return this.first_name + ' ' + this.last_name }

              // (new_name:String) → undefined
              // Sets the name components of the object,
              // from a full name.
              , set name(new_name) { var names
                    names = new_name.trim().split(/\s+/)
                    this.first_name = names['0'] || ''
                    this.last_name  = names['1'] || '' }
              }

Невалидные имена свойств могут быть заключены в кавычки. Учитывайте, что запись для getter/setter в литеральном виде определяется анонимными функциями. Если вы хотите связать ранее объявленную функцию с getter/setter, то вы должны использовать метод Object.defineProperty.

Посмотрим на общее правила литерального синтаксиса:

<object-literal>  ::= "{" <property-list> "}"
                    ;
<property-list>   ::= <property> ["," <property>]*
                    ;
<property>        ::= <data-property>
                    | <getter-property>
                    | <setter-property>
                    ;
<data-property>   ::= <property-name> ":" <expression>
                    ;
<getter-property> ::= "get" <identifier>
                    :       <function-parameters>
                    :       <function-block>
                    ;
<setter-property> ::= "set" <identifier>
                    :       <function-parameters>
                    :       <function-block>
                    ;
<property-name>   ::= <identifier>
                    | <quoted-identifier>
                    ;

Литеральные объекты могут появляться внутри выражений в JavaScript. Из-за некоторой неоднозначности новички иногда путаются:
// This is a block statement, with a label:
{ foo: 'bar' }
// => 'bar'

// This is a syntax error (labels can't be quoted):
{ "foo": 'bar' }
// => SyntaxError: Invalid label

// This is an object literal (note the parenthesis to force
// parsing the contents as an expression):
({ "foo": 'bar' })
// => { foo: 'bar' }

// Where the parser is already expecting expressions,
// object literals don't need to be forced. E.g.:
var x = { foo: 'bar' }
fn({foo: 'bar'})
return { foo: 'bar' }
1, { foo:
2. Методы

До сих пор объект Mikhail имел только слоты для хранения данных (ну, за исключением getter/setter для свойства name). Описание действий, которые можно делать с объектом делается в JavaScript очень просто. Просто — потому что в JavaScript нет разницы между манипулированием такими вещами, как Function, Number, Object. Всё делается одинаково (не забываем, что функции в JavaScript являются сущностями первого класса).

Опишем действие над данным объектом, просто установив функцию, как значение нашего свойства. К примеру, мы хотим, чтобы Миша мог приветствовать других людей:

// (person:String) → String
// Greets a random person
mikhail.greet = function(person) {
    return this.name + ': Why, hello there, ' + person + '.'
}

После выставления значения свойства, мы можем использовать аналогичный способ для выставления конкретных данных, связанных с объектом. Таким образом, доступ к свойствам будет возвращать ссылку на функцию, хранящуюся в нём, которую мы можем вызвать:
mikhail.greet('you')
// => 'Michael White: Why, hello there, you.'

mikhail.greet('Kristin')
// => 'Michael White: Why, hello there, Kristin.'
2.1. Динамический this

Следует учитывать одну вещь при описании функции greet — эта функция должна обращаться к getter/setter свойства name, а для этого она использует магическую переменную this.

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

Функции являются generic-ами. Т.е. в JavaScript переменная this определяет динамическую ссылку, которая разрешается в момент исполнения функции.

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

2.2. Разрешение this

Существует четыре различных способа разрешения this в функции, зависящие от того, как функция вызывается: непосредственно, как метод, явно применяется, как конструктор. Мы посмотрим первые три, а к конструкторам вернёмся позже.

Для следующих примеров вы примем:

// Returns the sum of the object's value with the given Number
function add(other, yet_another) {
    return this.value + other + (yet_another || 0)
}

var one = { value: 1, add: add }
var two = { value: 2, add: add }
2.2.1 Вызов как метод

Если функция вызывается, как метод объекта, то this внутри функции ссылается на сам объект. Т.е. когда мы явно указываем какой объект выполняет действие, то объект и будет значением this в нашей функции.

Это произойдёт, когда мы вызовем mikhail.greet(). Эта запись говорит JavaScript-у, что мы хотим применить действие greet к объекту mikhail.

one.add(two.value) // this === one
// => 3

two.add(3)         // this === two
// => 5

one['add'](two.value) // brackets are cool too
// => 3
2.2.2 Непосредственный вызов

Когда функция вызывается непосредственно, то this разрешается в глобальный объект движка (window в браузере, global в Node.js)
add(two.value)  // this === global
// => NaN

// The global object still has no `value' property, let's fix that.
value = 2
add(two.value)  // this === global
// => 4
2.2.3. Явное применение

В заключении, функция может быть явно применена к любому объекту, несмотря на то, есть ли в объекте соответствующее свойство или нет. Эта функциональность достигается с помощью методов call или apply.

Различие между двумя методами заключается в параметрах передаваемых в функцию и времени исполнения — apply работает примерно в 55 раз медленнее, чем непосредственный вызов, а вот call обычно не особо хуже. Всё очень зависит от текущего движка, так что используйте Perf test, чтобы быть уверенными — не оптимизируйте код раньше времени.

В любом случае, call ожидает объект, как первый параметр функции, за которым следуют обычные аргументы исходной функции:

add.call(two, 2, 2)      // this === two
// => 6

add.call(window, 4)      // this === global
// => 6

add.call(one, one.value) // this === one
// => 2

С другой стороны, apply позволяет описывать вторым параметром массив параметров исходной функции:
add.apply(two, [2, 2])       // equivalent to two.add(2, 2)
// => 6

add.apply(window, [ 4 ])       // equivalent to add(4)
// => 6

add.apply(one, [one.value])  // equivalent to one.add(one.value)
// => 2

На заметку. Учтите, что разрешение this в null или undefined зависит от семантики используемого движка. Обычно результат бывает таким же, как и применение функции к глобальному объекту. Но если движок работает в strict mode, то this будет разрешено как и ожидается — ровно в ту вещь, к которой применяется:
window.value = 2
add.call(undefined, 1) // this === window
// => 3

void function() {
  "use strict"
  add.call(undefined, 1) // this === undefined
  // => NaN
  // Since primitives can't hold properties.
}()
2.3. Связывание методов

Отвлечёмся от динамической сущности функций в JavaScript, пойдём по пути создания функций, связывая их с определёнными объектами, так чтобы this внутри функции всегда указывал на данный объект, несмотря на то, как он вызывается — как метод объекта или непосредственно.

Функция обеспечивает функциональность, называемую bind: берётся объект и дополнительный параметр (очень похоже на вызов call) и возвращается новая функция, которая будет применять параметры к исходной функции при вызове:

var one_add = add.bind(one)

one_add(2) // this === one
// => 3

two.one_adder = one_add
two.one_adder(2) // this === one
// => 3

one_add.call(two) // this === one
// => 3
3. Наследование

До сих пор мы видели, как объекты могут определять своё поведение и как мы можем использовать их действия на других объектах, но, мы до сих пор не увидели нормального пути для повторного использования кода и его расширяемости.

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

Модель прототипирования идёт дальше. Хоть она и поддерживает такие технологии, как «selective extensibility» и «behaviour sharing», но мы их не будем особо изучать. Печальная вещь: конкретные модели прототипного ОО, реализованные в JavaScript несколько ограниченны. Мы можем обойти эти ограничения, но накладные расходы будут велики.

3.1. Прототипы

Наследование в JavaScript осуществляется через клонирование поведения объекта и расширение его специализированным поведением. Объект, поведение которого клонируют, называется прототипом.

Прототип — это обычный объект, который делится своим поведением с другими объектами — в этом случае он выступает в качестве родителя.

Концепт клонирования поведения не означает, что вы будете иметь две различные копии одной и той же функции или данных. На самом деле JavaScript реализует наследование через делегирование, т.е. все свойства хранятся в родителе, а доступ к ним расширен через ребёнка.

Как упоминалось ранее, родитель (или [[Prototype]]) объекта определяется вызовом Object.create с первым аргументом, ссылающимся на объект-родитель.

Вернёмся к примеру с Мишей. Выделим его имя и способность приветствовать людей в отдельный объект, который поделится с Мишей своим поведением. Вот как будет выглядеть наша модель:

Реализуем её на JavaScript:

var person = Object.create(null)

// Here we are reusing the previous getter/setter functions
Object.defineProperty(person, 'name', { get: get_full_name
                                      , set: set_full_name
                                      , configurable: true
                                      , enumerable:   true })

// And adding the `greet' function
person.greet = function (person) {
    return this.name + ': Why, hello there, ' + person + '.'
}

// Then we can share those behaviours with Mikhail
// By creating a new object that has it's [[Prototype]] property
// pointing to `person'.
var mikhail = Object.create(person)
mikhail.first_name = 'Mikhail'
mikhail.last_name  = 'Weiß'
mikhail.age        = 19
mikhail.gender     = 'Male'

// And we can test whether things are actually working.
// First, `name' should be looked on `person'
mikhail.name
// => 'Mikhail Weiß'

// Setting `name' should trigger the setter
mikhail.name = 'Michael White'

// Such that `first_name' and `last_name' now reflect the
// previously name setting.
mikhail.first_name
// => 'Michael'
mikhail.last_name
// => 'White'

// `greet' is also inherited from `person'.
mikhail.greet('you')
// => 'Michael White: Why, hello there, you.'

// And just to be sure, we can check which properties actually
// belong to `mikhail'
Object.keys(mikhail)
// => [ 'first_name', 'last_name', 'age', 'gender' ]
3.2 Но как же [⁣[Prototype]⁣] работает?

Как вы видели в прошлом примере, ни одно из свойств, определённых в Person мы не определяли явно в Mikhail, но всё же смогли получить к ним доступ. Это произошло благодаря тому, что JavaScript реализует делегирование доступа к свойствам, т.е. свойство ищется через всех родителей объекта.

Эта цепь родителей определяется скрытым слотом в каждом объекте, который называется [⁣[Prototype]⁣]. Вы не можете изменить его непосредственно, существует только один способ задать ему значение — при создании нового объекта.

Когда свойство запрашивается из объекта, движок сначала пытается получить свойство из целевого объекта. Если свойство не найдено, то рассматривается непосредственный родитель объекта, затем родитель родителя и т.д.

Это означает, что мы можем изменить поведение прототипа в середине программы, то автоматически изменится поведение всех объектов, которые были от него унаследованы. Например, пусть мы хотим изменить приветствие по умолчанию:

// (person:String) → String
// Greets the given person
person.greet = function(person) {
    return this.name + ': Harro, ' + person + '.'
}

mikhail.greet('you')
// => 'Michael White: Harro, you.'
3.3. Перегрузка свойств

Так, прототипирование (т.е. наследование) используется для того, чтобы можно было поделиться данными с другими объектами. Причём этот способ работает быстро и экономичен по отношению к памяти, т.к. мы всегда имеем только один экземпляр используемых данных.

Но что если мы хотим добавить специализированное поведение, которым можно было бы делиться с другим объектами? Мы уже видели как объекты определяют своё поведение с помощью свойств, специализированное поведение определяется аналогичным образом — вы просто устанавливаете значение нужному свойству.

Для лучшей демонстрации положим, что Person реализует только обобщённое приветствие, а каждый наследник Person будет реализовывать своё уникальное приветствие. Также добавим новую персону в наш сценарий, чтобы лучше показать как расширяется объект:

Учтите, что и mikhail, и kristin определяют собственную версию метода greet. В этом случае мы вызовем метод greet из собственной версии поведения объекта, а не обобщённый метод greet, унаследованный от Person:

// Here we set up the greeting for a generic person

// (person:String) → String
// Greets the given person, formally
person.greet = function(person) {
    return this.name + ': Hello, ' + (person || 'you')
}

// And a greeting for our protagonist, Mikhail

// (person:String) → String
// Greets the given person, like a bro
mikhail.greet = function(person) {
    return this.name + ': \'sup, ' + (person || 'dude')
}

// And define our new protagonist, Kristin
var kristin = Object.create(person)
kristin.first_name = 'Kristin'
kristin.last_name  = 'Weiß'
kristin.age        = 19
kristin.gender     = 'Female'

// Alongside with her specific greeting manners

// (person:String) → String
// Greets the given person, sweetly
kristin.greet = function(person) {
    return this.name + ': \'ello, ' + (person || 'sweetie')
}

// Finally, we test if everything works according to the expected

mikhail.greet(kristin.first_name)
// => 'Michael White: \'sup, Kristin'

mikhail.greet()
// => 'Michael White: \'sup, dude'

kristin.greet(mikhail.first_name)
// => 'Kristin Weiß: \'ello, Michael'

// And just so we check how cool this [[Prototype]] thing is,
// let's get Kristin back to the generic behaviour

delete kristin.greet
// => true

kristin.greet(mikhail.first_name)
// => 'Kristin Weiß: Hello, Michael'

Продолжение следует…

Понятие ООП в JavaScript

ООП (Объекто-ориентированное программирование) — это принципиально новый вид программирования. ООП используется не только в JavaScript, но и в других языках тоже. И о понятии ООП я Вам и расскажу в этой статье.

В основе ООП лежит объект. Объект — это некая сущность, у которой есть свойства и методы, позволяющие этими свойствами манипулировать. Вот такое сухое определение, но зато полностью отражающее суть объекта. Чтобы стало ещё понятнее, давайте приведу пример из жизни. Самый популярный пример — это автомобиль. Какие свойства могут быть у автомобиля? Например, может быть максимальная скорость, ускорение, количество передач, масса, цвет, текущие координаты местоположения автомобиля и так далее. Теперь, какие могут быть методы ? Это может быть метод «перекраски автомобиля», который занимается перекрашиванием автомобиля в другой цвет. Это может быть метод «движения», которая занимается изменением текущих координат местоположения и так далее.

Думаю, что с объектами всё достаточно понятно.

Теперь о классах. Класс — это некий шаблон для создания объектов. Имено в классе написаны, какие свойства и методы будут у объекта. Также у класса есть конструктор, который занимается созданием объектов. Другими словами, класс — это шаблон и фабрика для создания объектов.

Теперь поговорим о трёх китах ООП: инкапсуляция, наследование и полиморфизм.

Начнём с инкапсуляции. Инкапсуляция — это процесс сокрытия части объекта от пользователя. Для того чтобы было легче понять, что такое инкапсуляция, привожу пример. Допустим, Вы набираете текст в Word. Знаете ли Вы, что в этот момент Вы посылаете огромное количество бит из 0 и 1? Известно ли Вам, как они обрабатываются? Известно ли Вам, как они превращаются в символы, которые Вы видите на экране? И вообще, как происходит вывод на ЖК-монитор? Скорее всего, Вы об этом даже не задумывались. То есть реализация всей этой физики от Вас скрыта. Это и есть инкапсуляция. Вы можете спокойно использовать объекты и их функциональность, но при этом Вам совсем не надо знать, как это работает. И это очень удобно и для пользователя, и для программиста.

Теперь поговорим о наследовании. Наследование — механизм ООП, позволяющий создавать классы на основе других классов, забирая их свойства и методы. Снова пример из жизни. Ведь согласитесь, что объект «Автомобиль» — это абстрактность. Ведь существуют элементарно легковые и грузовые автомобили. То есть родительский класс (или как его ещё называют супер-класс) «Автомобиль» и два дочерних (или наследника, или производных): «Легковые автомобили» и «Грузовые автомобили». Принцип легковых и грузовых автомобилей один и тот же. То есть у каждого из них есть свойства «автомобиля» (максимальная скорость, ускорение и прочее) и методы (перемещения, перекраски). Однако, свойства будут немного другого значения (например, масса у грузовых машин, как правило, выше). А реализация методов будет другая (ведь перекрасить грузовую машину — это немного другое, нежели перекрасить легковую, хоть и принцип примерно один и тот же).

И последняя парадигма ООП — это полиморфизм. Самый сложный для понимания «кит», однако, не менее важный, чем другие. Полиморфизм — это взаимозаменяемость объектов. Или другими словами, объект может иметь много форм. Например, объект «Автомобиль» может иметь две формы «Грузовые автомобили» и «Легковые автомобили». И если мы вызовем метод «перекраски автомобиля», то в зависимости от того, с каким объектом мы работаем, объект будет перекрашиваться как «грузовой автомобиль» или как «легковой автомобиль». Ведь мы можем, например, потребовать в качестве параметра функции объект «Автомобиля», но ведь мы не знаем, какая именно форма придёт в функцию: «легковой автомбиль» или «грузовой автомобиль». Однако, мы точно знаем, что все методы, которые мы применяем внутри функции к этому объекту будут корректно обрабатываться. Надеюсь, что-то прояснилось, однако, если это не так, то ничего страшного. В принципе, для JavaScript ООП не так важно, поэтому тут достаточно и общих понятий.

А вообще тема ООП достаточно сложная и по ней пишут огромное количество толстенных книг, поэтому в одной статье трудно раскрыть эту тему. Однако, я постарался это сделать максимально понятным языком, и в то же время, чтобы у Вас появились общие понятия.

Что же касается JavaScript, то тут ООП как такового нет, однако, концепции ООП JavaScript подчиняется. Существуют две части JavaScript (их несколько больше, но для 90% случаев их вполне достаточно). Первая часть — это ядро языка JavaScript. Здесь содержатся классы, отвечающие за математические операции, строковые операции, работу с массивами и так далее. Вторая часть — это клиентская. Здесь содержатся классы, которые отвечают за объекты на HTML-страницах, такие как: формы, кнопки, текстовые поля, ссылки, изображения, различные другие элементы страницы. Со многими объектами мы познакомимся в следующих статьях. А пока хватит.

Спасибо за внимание!

  • Создано 10.10.2010 17:44:55
  • Михаил Русаков

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

Урок #16 — Что такое ООП в JavaScript?

За урок мы познакомимся с понятием ООП и узнаем про основные концепции данной технологии. Нами будут изучены базовые понятия ООП, а также мы разберем что такое классы и объекты в языке JavaScript.

На начальном этапе ООП – это тёмный лес, в котором многое непонятно и слишком усложнено. На самом деле всё вовсе не так. Предлагаем абстрагироваться от специфических (непонятных) определений и познакомиться с ООП простыми словами. 

Поскольку на примере все усвоить гораздо проще, то давайте за пример возьмем робота, которого постараемся описать за счёт классов в ООП.

Класс в случае с роботом – это его чертёж. Экземпляром класса (объектом) называет целый робот, который создан точно по чертежу.


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

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

А инкапсуляция является для нас бронёй, защищающей робота. Под пластырем брони находятся уязвимые элементы, вроде проводов и микросхем. После прикрытия брешей с помощью брони, робот полностью защищён от внешних вмешательств. По сути, мы делаем доступ ко всем полям лишь за счёт методов, тем самым прямой доступ к полю будет закрыт.

У всех классов методы могут отличаться, как и поля с конструкторами. Каждый класс позволяет создавать любое количество разных объектов, все из них имеют собственные характеристики.

Введение в объекты JavaScript — Изучение веб-разработки

В JavaScript большинство сущностей являются объектами, начиная с самого основного функционала JavaScript, такого как строки (strings) и массивы (array), и заканчивая встроенными в браузер API. Вы можете даже создавать свои собственные объекты, чтобы инкапсулировать связанные между собой функции и переменные в эффективные пакеты и действовать как удобные хранилища данных. Понимание объектно-ориентированной природы JavaScript очень важно, если Вы хотите продолжить дальнейшее более углубленное изучение языка. Поэтому мы предоставляем Вам данный модуль, чтобы помочь Вам разобраться в этом. Здесь мы детально обучим Вас теории и синтаксису объектов, а затем рассмотрим, как создавать свои собственные объекты. 

Необходимые знания

Перед тем, как начать изучение данного модуля, Вы должны иметь некоторое представление о HTML и CSS. Мы советуем Вам поработать над разделами Введение в HTML и Введение в CSS перед изучением этого модуля JavaScript.

Также Вам необходимо знать основы JavaScript перед подробным изучением объектов JavaScript. Предварительно поработайте с разделами Первые шаги в JavaScript и Структурные элементы в JavaScript перед началом изучения данного модуля.

Примечание: Если Вы работаете за компьютером/планшетом/другим устройством, на котором у Вас нет возможности создавать собственные файлы, постарайтесь поработать с примерами кода на платформах онлайн-программирования, таких, как JSBin or Thimble.

Руководства

Основы объектов
В первой статье мы рассмотрим объекты в JavaScript. Мы будем разбирать основы синтаксиса объектов JavaScript и заново изучим некоторый функционал JavaScript, который мы уже исследовали ранее на курсе, подтвердив тот факт, что большая часть функционала, с которым мы уже столкнулись, в действительности является объектами.
Объектно-ориентированный JavaScript для начинающих
Закончив с основами, мы сфокусируемся на объектно-ориентированном JavaScript (OOJS) —  эта статья представляет основы теории объектно-ориентированного программирования (ООП). Затем мы изучим, как JavaScript эмулирует классы объектов через конструктор функций, и как создавать экземпляры объектов.
Прототипы объектов
Прототипы — это механизм, благодаря которому объекты в JavaScript наследуют функционал друг друга, но при этом они работают иначе по сравнению с механизмами наследования в классических объектно-ориентированных языках. В этой статье мы изучим эти отличия, объясним, как работает цепочка прототипов, и рассмотрим, как свойство прототипа может быть использовано для добавления методов к существующим конструкторам.
Наследование в JavaScript
После знакомства с самыми жуткими подробностями OOJS, эта статья покажет, как создавать «дочерные» классы объектов (конструкторы), которые наследуют функционал от своих «родительских» классов. В дополнении, мы дадим Вам пару советов о том, где и когда можно использовать OOJS.
Работа с JSON-данными
Представление объектов в JavaScript (JavaScript Object Notation) (JSON) — это стандартный формат для представления структурированных данных в виде объектов JavaScript, который обычно используется для представления и передачи данных на веб-сайтах (т.е. передача некоторых данных от сервера к клиенту — таким образом они могут быть отображены на веб-странице). Вы довольно часто будете с этим сталкиваться, поэтому в данной статье мы предоставим вам все, что необходимо для работы с JSON с помощью JavaScript, в том числе доступ к элементам данных в объекте JSON и написания собственного JSON-кода.
Практика построения объектов
В предыдущих статьях мы рассматривали самые основные моменты в теории и синтаксисе объектов в JavaScript, дав Вам твердую основу для начала. В этой статье мы погрузимся в практические занятия, получим больше практической работы в построении собственных объектов в JavaScript, чтобы сделать кое-что веселое и красочное — несколько цветных прыгающих шариков.

Задания

Добавление функционала к демо с прыгающими шариками
В этом задании, мы ожидаем, что Вы, используя демо с прыгающими шариками из предыдущей статьи как отправную точку, добавите немного нового и интересного функционала в него.
Учебное пособие по объектно-ориентированному JavaScript (OOJS)

с примером

  • Home
  • Testing

      • Back
      • Agile Testing
      • BugZilla
      • Cucumber
      • 000
      • J2 9000 Testing
      • 9000 База данных
      • 000 Тестирование ET4000 9000 Testing 9000
        • Назад
        • JUnit
        • LoadRunner
        • Ручное тестирование
        • Мобильное тестирование
        • Mantis
        • Почтальон
        • QTP
        • Назад
        • Центр качества
        • 000300030003 SoapUI
        • Управление тестированием
        • TestLink
    • SAP

        • Назад
        • 900 03 ABAP
        • APO
        • Начинающий
        • Basis
        • BODS
        • BI
        • BPC
        • CO
        • Назад
        • CRM
        • Crystal Reports
        • Crystal Reports
        • FICO3
        • Заработная плата
        • Назад
        • PI / PO
        • PP
        • SD
        • SAPUI5
        • Безопасность
        • Менеджер решений
        • Successfactors
        • SAP Back Tutorials
        • Apache
        • AngularJS
        • ASP.Net
        • C
        • C #
        • C ++
        • CodeIgniter
        • СУБД
        • JavaScript
        • Назад
        • Java
        • JSP
        • Kotlin
        • Linux
        • Linux
        • Kotlin
        • Linux
        • js
        • Perl
        • Назад
        • PHP
        • PL / SQL
        • PostgreSQL
        • Python
        • ReactJS
        • Ruby & Rails
        • Scala
        • SQL
        • 000
        • SQL
        • 000 0003 SQL 000 0003 SQL 000
        • UML
        • VB.Net
        • VBScript
        • Веб-службы
        • WPF
    • Обязательно учите!

        • Назад
        • Бухгалтерский учет
        • Алгоритмы
        • Android
        • Блокчейн
        • Business Analyst
        • Создание веб-сайта
        • CCNA
        • Облачные вычисления
        • 00030003 COBOL
            9000 Compiler
              9000 Встроенные системы
            • 00030003 9000 Compiler 9000
            • Ethical Hacking
            • Учебные пособия по Excel
            • Программирование на Go
            • IoT
            • ITIL
            • Jenkins
            • MIS
            • Сети
            • Операционная система
            • 00030003
            • Назад
            • Управление проектами Обзоры
            • Salesforce
            • SEO
            • Разработка программного обеспечения
            • VB A
        • Big Data

            • Назад
            • AWS
            • BigData
            • Cassandra
            • Cognos
            • Хранилище данных
            • 0003
            • HBOps
            • 0003
            • HBOps
            • 0003
            • MicroStrategy
        .

        Введение в объектно-ориентированное программирование на JavaScript

        , Райнер Ханекамп

        JavaScript и объектно-ориентированное программирование

        Эта статья предназначена для студентов, изучающих JavaScript, которые не имеют никаких предварительных знаний в области объектно-ориентированного программирования (ООП). Я сосредоточен на тех частях ООП, которые относятся только к JavaScript, а не к ООП в целом. Я пропускаю полиморфизм, потому что он лучше подходит для языка со статической типизацией.

        Зачем вам это нужно знать?

        Вы выбрали JavaScript в качестве первого языка программирования? Вы хотите быть опытным разработчиком, работающим над гигантскими корпоративными системами, охватывающими сотни тысяч строк кода или более?

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

        Различное мышление

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

        То же верно и для парадигм программирования. Есть разные способы подойти к проблеме и разработать решение.

        Объектно-ориентированное программирование или ООП — это парадигма современной разработки приложений. Он поддерживается основными языками, такими как Java, C # или JavaScript.

        Объектно-ориентированная парадигма

        С точки зрения ООП приложение — это совокупность «объектов», которые взаимодействуют друг с другом. Мы основываем эти объекты на вещах из реального мира, таких как товары в инвентаре или учетные записи сотрудников. Объекты содержат данные и выполняют некоторую логику на основе своих данных. В результате код ООП очень легко понять. Что не так просто, так это решить, как в первую очередь разбить приложение на эти небольшие объекты.

        Если бы вы были похожи на меня, когда я услышал это в первый раз, вы понятия не имеете, что это на самом деле означает — все это звучит очень абстрактно.Чувствовать это совершенно нормально. Более важно, чтобы вы услышали идею, запомнили ее и попытались применить ООП в своем коде. Со временем вы приобретете опыт и приведете больше кода в соответствие с этой теоретической концепцией.

        Урок : ООП, основанный на реальных объектах, позволяет любому прочитать ваш код и понять, что происходит.

        Объект как центральный элемент

        Простой пример поможет вам увидеть, как JavaScript реализует фундаментальные принципы ООП. Рассмотрим вариант использования покупок, когда вы кладете продукты в корзину, а затем рассчитываете общую цену, которую должны заплатить.Если вы воспользуетесь знаниями JavaScript и запрограммируете вариант использования без ООП, он будет выглядеть следующим образом:

          const bread = {name: 'Bread', price: 1}; const water = {name: 'Water', price: 0,25};  
          const корзина = []  
        .

        Введение в объекты JavaScript — Изучение веб-разработки

        В JavaScript большинство вещей являются объектами, от основных функций JavaScript, таких как массивы, до API-интерфейсов браузера, построенных на основе JavaScript. Вы даже можете создавать свои собственные объекты, чтобы инкапсулировать связанные функции и переменные в эффективные пакеты и действовать как удобные контейнеры данных. Если вы хотите углубить свои знания языка, важно понимать объектную природу JavaScript, поэтому мы предоставили этот модуль, чтобы помочь вам.Здесь мы подробно изучаем теорию объектов и синтаксис, а затем рассмотрим, как создавать свои собственные объекты.

        Хотите стать интерфейсным веб-разработчиком?

        Мы составили курс, который включает в себя всю важную информацию, необходимую для достижения вашей цели.

        Начать

        Предварительные требования

        Перед тем, как приступить к этому модулю, вы должны немного познакомиться с HTML и CSS. Рекомендуется проработать модули Введение в HTML и Введение в CSS, прежде чем начинать работу с JavaScript.

        Вам также следует немного познакомиться с основами JavaScript, прежде чем подробно рассматривать объекты JavaScript. Перед тем, как приступить к использованию этого модуля, проработайте первые шаги JavaScript и строительные блоки JavaScript.

        Примечание : Если вы работаете на компьютере / планшете / других устройствах, где у вас нет возможности создавать свои собственные файлы, вы можете опробовать (большую часть) примеры кода в онлайн-программе кодирования, такой как JSBin. или глюк.

        Направляющие

        Основные сведения об объекте
        В первой статье, посвященной объектам JavaScript, мы рассмотрим фундаментальный синтаксис объекта JavaScript и вернемся к некоторым функциям JavaScript, которые мы уже рассмотрели ранее в курсе, повторяя тот факт, что многие из функций, которые вы уже имели с фактически объектами.
        Объектно-ориентированный JavaScript для начинающих
        Изложив основы, мы сосредоточимся на объектно-ориентированном JavaScript (OOJS) — в этой статье представлен базовый взгляд на теорию объектно-ориентированного программирования (ООП), а затем исследуется, как JavaScript эмулирует классы объектов с помощью функций-конструкторов, и как создавать экземпляры объектов.
        Прототипы объекта
        Прототипы — это механизм, с помощью которого объекты JavaScript наследуют функции друг от друга, и они работают иначе, чем механизмы наследования в классических объектно-ориентированных языках программирования.В этой статье мы исследуем это различие, объясним, как работают цепочки прототипов, и посмотрим, как свойство prototype можно использовать для добавления методов к существующим конструкторам.
        Наследование в JavaScript
        После объяснения большинства кровавых деталей OOJS в этой статье показано, как создавать «дочерние» классы объектов (конструкторы), которые наследуют функции от своих «родительских» классов. Кроме того, мы даем несколько советов о том, когда и где вы можете использовать OOJS.
        Работа с данными JSON
        JavaScript Object Notation (JSON) — это стандартный текстовый формат для представления структурированных данных на основе синтаксиса объекта JavaScript, который обычно используется для представления и передачи данных на веб-сайтах (т.е. отправка некоторых данных с сервера клиенту, чтобы их можно было отобразить на веб-странице). Вы будете сталкиваться с этим довольно часто, поэтому в этой статье мы даем вам все необходимое для работы с JSON с помощью JavaScript, включая анализ JSON, чтобы вы могли получить доступ к элементам данных в нем и написать свой собственный JSON.
        Объект строительной практики
        В предыдущих статьях мы рассмотрели все важные детали теории объектов JavaScript и синтаксиса, что дало вам прочную основу для начала. В этой статье мы погрузимся в практическое упражнение, дающее вам больше практики в создании пользовательских объектов JavaScript, которые создают что-то веселое и красочное — несколько цветных прыгающих мячей.

        Оценки

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

        См. Также

        Изучить JavaScript
        Отличный ресурс для начинающих веб-разработчиков. Изучите JavaScript в интерактивной среде с короткими уроками и интерактивными тестами, управляемыми автоматической оценкой.Первые 40 уроков бесплатны, а полный курс доступен за небольшую единовременную оплату.
        .

        oop — классы JavaScript — qaru.

        Переполнение стека
        1. Около
        2. Продукты
        3. Для команд
        1. Переполнение стека Общественные вопросы и ответы
        2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
        3. Вакансии Программирование и связанные с ним технические возможности карьерного роста
        4. Талант Нанимайте технических специалистов и создавайте свой бренд работодателя
        5. Реклама Обратитесь к разработчикам и технологам со всего мира
        6. О компании
        .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *