diff options
Diffstat (limited to 'files/ru/conflicting/learn/javascript/objects/index.html')
-rw-r--r-- | files/ru/conflicting/learn/javascript/objects/index.html | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/files/ru/conflicting/learn/javascript/objects/index.html b/files/ru/conflicting/learn/javascript/objects/index.html new file mode 100644 index 0000000000..d07cf043cc --- /dev/null +++ b/files/ru/conflicting/learn/javascript/objects/index.html @@ -0,0 +1,356 @@ +--- +title: Вступление в Объектно-ориентированный JavaScript +slug: Web/JavaScript/Introduction_to_Object-Oriented_JavaScript +translation_of: Learn/JavaScript/Objects +translation_of_original: Web/JavaScript/Introduction_to_Object-Oriented_JavaScript +--- +<p>Объектно-ориентированный до основания, JavaScript предоставляет мощные и гибкие {{Glossary("OOP")}} возможности. Эта статья начинается с введения в объектно-ориентированное программирование, затем рассматривает модель объекта JavaScript и, наконец, демонстрирует концепции объектно-ориентированного программирования в JavaScript.</p> + +<h2 id="JavaScript_Review" name="JavaScript_Review">Обзор JavaScript</h2> + +<p>Если вы неуверенно владеете такими концепциями JavaScript, как переменные, типы, функции и области видимости, вы можете прочитать об этих темах в <a href="/ru/docs/Web/JavaScript/A_re-introduction_to_JavaScript">Повторное вступление в JavaScript</a>. Вы также можете обратиться к <a href="/ru/docs/Web/JavaScript/Guide">JavaScript Guide</a>.</p> + +<h2 id="Object-oriented_programming" name="Object-oriented_programming">Объектно-ориентированное программирование</h2> + +<p>Объектно-ориентированное программирование (ООП) — это парадигма программирования, которая использует {{glossary("абстракции")}}, чтобы создавать модели, основанные на объектах реального мира. ООП использует несколько техник из ранее признанных парадигм, включая {{glossary("модульность")}}, {{glossary("полиморфизм")}} и {{glossary("инкапсуляция")}}. На сегодняшний день многие популярные языки программирования (такие как Java, JavaScript, C#, C++, Python, PHP, Ruby и Objective-C) поддерживают ООП.</p> + +<p>ООП представляет программное обеспечение как совокупность взаимодействующих объектов, а не набор функций или просто список команд (как в традиционном представлении). В ООП, каждый объект может получать сообщения, обрабатывать данные, и отправлять сообщения другим объектам. Каждый объект может быть представлен как маленькая независимая машина с отдельной ролью или ответственностью.</p> + +<p>ООП способствует большей гибкости и поддерживаемости в программировании, и широко распространена в крупномасштабном программном инжиниринге. Так как ООП настоятельно подчеркивает модульность, объектно-ориентированный код проще в разработке и проще для понимания впоследствии. Объектно-ориентированный код способствует более точному анализу, кодированию и пониманию сложных ситуаций и процедур, чем методы программирования с меньшей модульностью.<a href="#cite-1"><sup>1</sup></a></p> + +<h2 id="Terminology" name="Terminology">Терминология</h2> + +<dl> + <dt>{{Glossary("Пространство имён")}}</dt> + <dd>Контейнер, который позволяет разработчикам связать весь функционал под уникальным, специфичным для приложения именем.</dd> + <dt>{{Glossary("Класс")}}</dt> + <dd>Определяет характеристики объекта. Класс является описанием шаблона свойств и методов объекта.</dd> + <dt>{{Glossary("Объект")}}</dt> + <dd>Экземпляр класса.</dd> + <dt>{{Glossary("Свойство")}}</dt> + <dd>Характеристика объекта, например, цвет.</dd> + <dt>{{Glossary("Метод")}}</dt> + <dd>Возможности объекта, такие как ходьба. Это подпрограммы или функции, связанные с классом.</dd> + <dt>{{Glossary("Конструктор")}}</dt> + <dd>Метод, вызываемый в момент создания экземпляра объекта. Он, как правило, имеет то же имя, что и класс, содержащий его.</dd> + <dt>{{Glossary("Наследование")}}</dt> + <dd>Класс может наследовать характеристики от другого класса.</dd> + <dt>{{Glossary("Инкапсуляция")}}</dt> + <dd>Способ комплектации данных и методов, которые используют данные.</dd> + <dt>{{Glossary("Абстракция")}}</dt> + <dd>Совокупность комплексных наследований, методов и свойств объекта должны адекватно отражать модель реальности.</dd> + <dt>{{Glossary("Полиморфизм")}}</dt> + <dd>Поли означает "<em>много</em>", а морфизм "<em>формы</em>". Различные классы могут объявить один и тот же метод или свойство.</dd> +</dl> + +<p>Для более обширного описания объектно-ориентированного программирования, см {{interwiki("wikipedia", "Объектно-ориентированное_программирование")}} в Wikipedia.</p> + +<h2 id="Прототипное_программирование">Прототипное программирование</h2> + +<p>Прототипное программирование — это модель ООП которая не использует классы, а вместо этого сначала выполняет поведение класса и затем использует его повторно (эквивалент наследования в языках на базе классов), декорируя (или расширяя) существующие<em> </em>объекты <em>прототипы</em>. (Также называемое бесклассовое, прототипно-ориентированное, или экземплярно-ориентированное программирование.)</p> + +<p>Оригинальный (и наиболее каноничный) пример прототипно-ориентированного языка это {{interwiki("wikipedia", "Self (programming language)", "Self")}} разработанный Дэвидом Ангаром и Ренделлом Смитом. Однако бесклассовый стиль программирования стал набирать популярность позднее, и был принят для таких языков программирования, как JavaScript, Cecil, NewtonScript, Io, MOO, REBOL, Kevo, Squeak (при использовании фреймворка Viewer для манипуляции компонентами Morphic) и некоторых других.<a href="#cite-1"><sup>1</sup></a></p> + +<h2 id="JavaScript_Object_Oriented_Programming" name="JavaScript_Object_Oriented_Programming">Объектно-ориентированное программирование в JavaScript</h2> + +<h3 id="Пространство_имён">Пространство имён</h3> + +<p>Пространство имён — это контейнер, который позволяет разработчикам собрать функциональность под уникальным именем приложения. <strong>Пространство имён в JavaScript — это объект, содержащий методы, свойства и другие объекты.</strong></p> + +<div class="note"> +<p>Важно отметить, что на уровне языка в JavaScript нет разницы между пространством имён и любым другим объектом. Это отличает JS от множества других объектно-ориентированных языков и может стать причиной путаницы у начинающих JS программистов.</p> +</div> + +<p>Принцип работы пространства имён в JS прост: создать один глобальный объект и все переменные, методы и функции объявлять как свойства этого объекта. Также использование пространств имён снижает вероятность возникновения конфликтов имён в приложении так как каждый объект приложения является свойством глобального объекта.</p> + +<p>Давайте создадим глобальный объект MYAPP:</p> + +<pre class="brush: js">// Глобальное пространство имён +var MYAPP = MYAPP || {};</pre> + +<p>Во фрагменте кода выше мы сначала проверяем определён ли объект MYAPP (в текущем файле или другом файле). Если да, то используем существующий глобальный объект MYAPP, иначе создаём пустой объект MYAPP, в котором мы инкапсулируем все методы, функции, переменные и объекты.</p> + +<p>Также мы можем создать подпространство имён (учтите, что сначала нужно объявить глобальный объект):</p> + +<pre class="brush: js">// Подпространство имён +MYAPP.event = {};</pre> + +<p>Далее следует пример синтаксиса создания пространства имён и добавления переменных, функций и методов:</p> + +<pre class="brush: js">// Создаём контейнер MYAPP.commonMethod для общих методов и свойств +MYAPP.commonMethod = { + regExForName: "", // определяет регулярное выражение для валидации имени + regExForPhone: "", // определяет регулярное выражение для валидации телефона + validateName: function(name){ + // Сделать что-то с name, вы можете получить доступ к переменной regExForName + // используя "this.regExForName" + }, + + validatePhoneNo: function(phoneNo){ + // Сделать что-то с номером телефона + } +} + +// Объект вместе с объявлением методов +MYAPP.event = { + addListener: function(el, type, fn) { + // код + }, + removeListener: function(el, type, fn) { + // код + }, + getEvent: function(e) { + // код + } + + // Можно добавить другие свойства и методы +} + +// Синтаксис использования метода addListener: +MYAPP.event.addListener("yourel", "type", callback);</pre> + +<h3 id="Core_Objects" name="Core_Objects">Стандартные встроенные объекты</h3> + +<p>В JavaScript есть несколько объектов, встроенных в ядро, например {{jsxref("Math")}}, {{jsxref("Object")}}, {{jsxref("Array")}} и {{jsxref("String")}}. Пример ниже показывает как использовать объект Math, чтобы получить случайное число, используя его метод random().</p> + +<pre class="brush: js">console.log(Math.random()); +</pre> + +<div class="note"><strong>Примечание:</strong> В данном примере и далее мы будем использовать глобальную функцию {{domxref("console.log()")}}. Если точнее, то функция <code>console.log()</code> не является частью JavaScript, но она поддерживается многими браузерами для облегчения отладки.</div> + +<p>Смотрите <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects" title="en-US/docs/Web/JavaScript/Reference/Global_Objects">JavaScript Reference: Standard built-in objects</a>, чтобы ознакомиться со списком всех встроенных объектов JavaScript.</p> + +<p>Каждый объект в JavaScript является экземпляром объекта <code><a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Object">Object</a></code>, следовательно наследует все его свойства и методы.</p> + +<h3 id="Custom_Objects" name="Custom_Objects">Объекты, создаваемые пользователем</h3> + +<h4 id="The_Class" name="The_Class">Класс</h4> + +<p>JavaScript — это прототипно-ориентированный язык, и в нём нет оператора <code>class</code>, который имеет место в C++ или Java. Иногда это сбивает с толку программистов, привыкших к языкам с оператором <code>class</code>. Вместо этого JavaScript использует функции как конструкторы классов. Объявить класс так же просто как объявить функцию. В примере ниже мы объявляем новый класс Person с пустым конструктором:</p> + +<pre class="brush: js">var Person = function () {}; +</pre> + +<h4 id="The_Object_.28Class_Instance.29" name="The_Object_.28Class_Instance.29">Объект (экземпляр класса)</h4> + +<p>Для создания нового экзмепляра объекта <code>obj</code> мы используем оператор <code>new obj</code>, присваивая результат (который имеет тип <code>obj</code>) в переменную.</p> + +<p>В примере выше мы определили класс <code>Person</code>. В примере ниже мы создаём два его экземпляра (<code>person1</code> и <code>person2</code>).</p> + +<pre class="brush: js">var person1 = new Person(); +var person2 = new Person(); +</pre> + +<div class="note">Ознакомьтесь с {{jsxref("Object.create()")}}, новым, дополнительным методом инстанцирования, который создаёт неинициализированный экземпляр.</div> + +<h4 id="The_Constructor" name="The_Constructor">Конструктор</h4> + +<p>Конструктор вызывается в момент создания экземпляра класса (в тот самый момент, когда создается объект). Конструктор является методом класса. В JavaScript функция служит конструктором объекта, поэтому нет необходимости явно определять метод конструктор. Любое действие определенное в конструкторе будет выполненно в момент создания экземпляра класса.</p> + +<p>Конструктор используется для задания свойств объекта или для вызова методов, которые подготовят объект к использованию. Добавление методов и их описаний производится с использованием другого синтаксиса, описанного далее в этой статье.</p> + +<p>В примере ниже, конструктор класса <code>Person</code> выводит в консоль сообщение в момент создания нового экземпляра <code>Person</code>.</p> + +<pre class="brush: js">var Person = function () { + console.log('instance created'); +}; + +var person1 = new Person(); +var person2 = new Person(); +</pre> + +<h4 id="The_Property_.28object_attribute.29" name="The_Property_.28object_attribute.29">Свойство (аттрибут объекта)</h4> + +<p>Свойства — это переменные, содержащиеся в классе; каждый экземпляр объекта имеет эти свойства. Свойства устанавливаются в конструкторе (функции) класса, таким образом они создаются для каждого экземпляра.</p> + +<p>Ключевое слово <code>this</code>, которое ссылается на текущий объект, позволяет вам работать со свойствами класса. Доступ (чтение и запись) к свойствам снаружи класса осуществляется синтаксисом <code>InstanceName.Property,</code> так же как в C++, Java и некоторых других языках. (Внутри класса для получения и изменения значений свойств используется синтаксис <code>this.Property</code>)</p> + +<p>В примере ниже, мы определяем свойство <code>firstName</code> для класса <code>Person</code> при создании экземпляра:</p> + +<pre class="brush: js">var Person = function (firstName) { + this.firstName = firstName; + console.log('Person instantiated'); +}; + +var person1 = new Person('Alice'); +var person2 = new Person('Bob'); + +// Выводит свойство firstName в консоль +console.log('person1 is ' + person1.firstName); // выведет "person1 is Alice" +console.log('person2 is ' + person2.firstName); // выведет "person2 is Bob" +</pre> + +<h4 id="The_methods" name="The_methods">Методы</h4> + +<p>Методы — это функции (и определяются как функции), но с другой стороны следуют той же логике, что и свойства. Вызов метода похож на доступ к свойству, но вы добавляете () на конце имени метода, возможно, с аргументами. Чтобы объявить метод, присвойте функцию в именованное свойство свойства <code>prototype</code> класса. Потом вы сможете вызвать метод объекта под тем именем, которое вы присвоили функции.</p> + +<p>В примере ниже мы определяем и используем метод <code>sayHello()</code> для класса <code>Person</code>.</p> + +<pre class="brush: js">var Person = function (firstName) { + this.firstName = firstName; +}; + +Person.prototype.sayHello = function() { + console.log("Hello, I'm " + this.firstName); +}; + +var person1 = new Person("Alice"); +var person2 = new Person("Bob"); + +// вызываем метод sayHello() класса Person +person1.sayHello(); // выведет "Hello, I'm Alice" +person2.sayHello(); // выведет "Hello, I'm Bob" +</pre> + +<p>В JavaScript методы это — обычные объекты функций, связанные с объектом как свойства: это означает, что вы можете вызывать методы "вне контекста". Рассмотрим следующий пример:</p> + +<pre class="brush: js">var Person = function (firstName) { + this.firstName = firstName; +}; + +Person.prototype.sayHello = function() { + console.log("Hello, I'm " + this.firstName); +}; + +var person1 = new Person("Alice"); +var person2 = new Person("Bob"); +var helloFunction = person1.sayHello; + +// выведет "Hello, I'm Alice" +person1.sayHello(); + +// выведет "Hello, I'm Bob" +person2.sayHello(); + +// выведет "Hello, I'm undefined" (or fails +// with a TypeError in strict mode) +helloFunction(); + +// выведет true +console.log(helloFunction === person1.sayHello); + +// выведет true +console.log(helloFunction === Person.prototype.sayHello); + +// выведет "Hello, I'm Alice" +helloFunction.call(person1);</pre> + +<p>Как показывает пример, все ссылки, которые мы имеем на функцию <code>sayHello</code> — <code>person1</code>, <code>Person.prototype</code>, переменная <code>helloFunction</code> и т.д. — ссылаются на одну и ту же функцию. Значение <code>this</code> в момент вызова функции зависит от того, как мы её вызываем. Наиболее часто мы обращаемся к <code>this</code> в выражениях, где мы получаем функцию из свойства объекта — <code>person1.sayHello()</code> — <code>this</code> устанавливается на объект, из которого мы получили функцию (<code>person1</code>), вот почему <code>person1.sayHello()</code> использует имя "Alice", а <code>person2.sayHello()</code> использует имя "Bob". Но если вызов будет совершён иначе, то <code>this</code> будет иным: вызов <code>this</code> из переменной — <code>helloFunction()</code> — установит <code>this</code> на глобальный объект (<code>window</code> в браузерах). Так как этот объект (вероятно) не имеет свойства <code>firstName</code>, функция выведет "Hello, I'm undefined" (так произойдёт в нестрогом режиме; в <a href="https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Strict_mode">strict mode</a> всё будет иначе (ошибка), не будем сейчас вдаваться в подробности, чтобы избежать путаницы). Или мы можем указать <code>this</code> явно с помощью <code>Function#call</code> (или <code>Function#apply</code>) как показано в конце примера.</p> + +<div class="note"><strong>Примечание:</strong> Смотрите подробнее о <code>this</code> в <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/call" title="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call">Function#call</a> и <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/apply" title="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply">Function#apply</a></div> + +<h4 id="Inheritance" name="Inheritance">Наследование</h4> + +<p>Наследование — это способ создать класс как специализированную версию одного или нескольких классов (JavaScript поддерживает только одиночное наследование). Специализированный класс, как правило, называют потомком, а другой класс родителем. В JavaScript наследование осуществляется присвоением экземпляра класса родителя классу потомку. В современных браузерах вы можете реализовать наследование с помощью <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Classical_inheritance_with_Object.create" title="/en-US/docs/JavaScript/Reference/Global_Objects/Object/create#Classical_inheritance_with_Object.create">Object.create</a>.</p> + +<div class="note"><strong>Примечание:</strong> JavaScript не обнаружит <code>prototype.constructor</code> класса потомка (смотрите <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype">Object.prototype</a>) так что мы должны указать его вручную. Смотрите вопрос "<a href="http://stackoverflow.com/questions/8453887/why-is-it-necessary-to-set-the-prototype-constructor">Why is it necessary to set the prototype constructor?</a>" на Stackoverflow.</div> + +<p>В примере ниже мы определяем класс <code>Student</code> как потомка класса <code>Person</code>. Потом мы переопределяем метод <code>sayHello()</code> и добавляем метод <code>addGoodBye()</code>.</p> + +<pre class="brush: js">// Определяем конструктор Person +var Person = function(firstName) { + this.firstName = firstName; +}; + +// Добавляем пару методов в Person.prototype +Person.prototype.walk = function(){ + console.log("I am walking!"); +}; + +Person.prototype.sayHello = function(){ + console.log("Hello, I'm " + this.firstName); +}; + +// Определяем конструктор Student +function Student(firstName, subject) { + // Вызываем конструктор родителя, убедившись (используя Function#call) + // что "this" в момент вызова установлен корректно + Person.call(this, firstName); + + // Инициируем свойства класса Student + this.subject = subject; +}; + +// Создаём объект Student.prototype, который наследуется от Person.prototype. +// Примечание: Рспространённая ошибка здесь, это использование "new Person()", чтобы создать +// Student.prototype. Это неверно по нескольким причинам, не в последнюю очередь +// потому, что нам нечего передать в Person в качестве аргумента "firstName" +// Правильное место для вызова Person показано выше, где мы вызываем +// его в конструкторе Student. +Student.prototype = Object.create(Person.prototype); // Смотрите примечание выше + +// Устанавливаем свойство "constructor" для ссылки на класс Student +Student.prototype.constructor = Student; + +// Заменяем метод "sayHello" +Student.prototype.sayHello = function(){ + console.log("Hello, I'm " + this.firstName + ". I'm studying " + + this.subject + "."); +}; + +// Добавляем метод "sayGoodBye" +Student.prototype.sayGoodBye = function(){ + console.log("Goodbye!"); +}; + +// Пример использования: +var student1 = new Student("Janet", "Applied Physics"); +student1.sayHello(); // "Hello, I'm Janet. I'm studying Applied Physics." +student1.walk(); // "I am walking!" +student1.sayGoodBye(); // "Goodbye!" + +// Проверяем, что instanceof работает корректно +console.log(student1 instanceof Person); // true +console.log(student1 instanceof Student); // true +</pre> + +<p>Относительно строки <code>Student.prototype = Object.create(Person.prototype);</code>: В старых движках JavaScript, в которых нет <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create" title="Object.create">Object.create</a></code> можно использовать полифилл (ещё известный как "shim") или функцию которая достигает тех же результатов, такую как:</p> + +<pre class="brush: js">function createObject(proto) { + function ctor() { } + ctor.prototype = proto; + return new ctor(); +} + +// Пример использования: +Student.prototype = createObject(Person.prototype); +</pre> + +<div class="note"><strong>Примечание:</strong> Смотрите <a href="/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/create" title="Object.create">Object.create</a> для более подробной информации, и shim для реализации на старых движках.</div> + +<h4 id="Encapsulation" name="Encapsulation">Инкапсуляция</h4> + +<p>В примере выше классу <code>Student</code> нет необходимости знать о реализации метода <code>walk()</code> класса <code>Person</code>, но он может его использовать; Класс <code>Student</code> не должен явно определять этот метод, пока мы не хотим его изменить. Это называется <strong>инкапсуляция</strong>, благодаря чему каждый класс собирает данные и методы в одном блоке.</p> + +<p>Сокрытие информации распространённая особенность, часто реализуемая в других языках программирования как приватные и защищённые методы/свойства. Однако в JavaScript можно лишь имитировать нечто подобное, это не является необходимым требованием объектно-ориентированного программирования.<a href="#cite-2"><sup>2</sup></a></p> + +<h4 id="Abstraction" name="Abstraction">Абстракция</h4> + +<p>Абстракция это механизм который позволяет смоделировать текущий фрагмент рабочей проблемы, с помощью наследования (специализации) или композиции. JavaScript достигает специализации наследованием, а композиции возможностью экземплярам класса быть значениями атрибутов других объектов.</p> + +<p>В JavaScript класс <code>Function</code> наследуется от класса <code>Object</code> (это демонстрирует специализацию), а свойство <code>Function.prototype</code> это экземпляр класса <code>Object</code> (это демонстрирует композицию).</p> + +<pre class="brush: js">var foo = function () {}; + +// выведет "foo is a Function: true" +console.log('foo is a Function: ' + (foo instanceof Function)); + +// выведет "foo.prototype is an Object: true" +console.log('foo.prototype is an Object: ' + (foo.prototype instanceof Object));</pre> + +<h4 id="Polymorphism" name="Polymorphism">Полиморфизм</h4> + +<p>Так как все методы и свойства определяются внутри свойства <code>prototype</code>, различные классы могут определять методы с одинаковыми именами; методы находятся в области видимости класса в котором они определены, пока два класса не имеют связи родитель-потомок (например, один наследуется от другого в цепочке наследований).</p> + +<h2 id="Notes" name="Notes">Примечания</h2> + +<p>Это не все способы которыми можно реализовать объектно-ориентированное программирование в JavaScript, который очень гибок в этом отношении. Также способы рассмотренные здесь не отражают всех возможностей JavaScript и не подражают реализации теории объектов в других языках.</p> + +<p>Существуют другие способы, которые реализуют ещё более продвинутое объектно-ориентированное программирование на JavaScript, но они выходят за рамки этой вводной статьи.</p> + +<h2 id="References" name="References">Ссылки</h2> + +<ol> + <li><a name="cite-1"></a>Wikipedia. "<a href="http://en.wikipedia.org/wiki/Object-oriented_programming">Object-oriented programming</a>"</li> + <li><a name="cite-2"></a>Wikipedia. "<a href="http://en.wikipedia.org/wiki/Encapsulation_%28object-oriented_programming%29">Encapsulation (object-oriented programming)</a>"</li> +</ol> |