javascript. oop (in russian)

77

Upload: mikhail-davydov

Post on 26-May-2015

261 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: JavaScript. OOP (in russian)
Page 2: JavaScript. OOP (in russian)

Михаил Давыдов Разработчик JavaScript

JavaScript ООП

Page 3: JavaScript. OOP (in russian)

3

JavaScript ООП

• Нет классов –  Но можно эмулировать их

• Есть прототипы • Есть наследование на прототипах

–  Делегирующее прототипное наследование

• Все можно менять во время работы –  Цепочку наследования можно менять –  Прототипы можно менять –  На классах так сделать нельзя

• Можно изменить прототипы базовых "классов"

Page 4: JavaScript. OOP (in russian)

4

Сказка о мутантах

Page 5: JavaScript. OOP (in russian)

5

Сказка о мутантах

• В далекой-далекой галактике • Нет привычного нам наследования • Есть телепатическое наследование

–  "Телегенез"

• Действующие лица: –  Дедушка –  Отец –  Сын

Page 6: JavaScript. OOP (in russian)

6

Структура мутанта

Мутант

"Телепатические" гены

Собственные гены

Движение генов

Page 7: JavaScript. OOP (in russian)

7

Все зеленые

Color  

Дед Отец Сын

Page 8: JavaScript. OOP (in russian)

8

Дед: хочу стать синим!

Color  

Дед Отец Сын

Page 9: JavaScript. OOP (in russian)

9

Все посинели

Color  

Дед Отец Сын

Page 10: JavaScript. OOP (in russian)

10

Отец: верну-ка я цвет

Color   Color  

Дед Отец Сын

Page 11: JavaScript. OOP (in russian)

11

Дед синий, отец и сын зеленые

Color   Color  

Дед Отец Сын

Page 12: JavaScript. OOP (in russian)

12

Сын: хочу быть черным

Color  

Color   Color  

Дед Отец Сын

Page 13: JavaScript. OOP (in russian)

13

Мутанты и JavaScript

Size,  Age  

Color  

Объект

Свойства прототипа

Собственные свойства

Делегирование

Цепочка прототипов

Page 14: JavaScript. OOP (in russian)

14

Собственные свойства и прототип

• Собственные свойства • Свойства прототипа • Любой объект имеет ссылку на прототип

–  И примитив также* –  Имеет с рождения –  По умолчанию – Object.prototype

• Делегирование –  Мы можем пользоваться функциями прототипа не имея собственных

• Цепочка прототипов –  Каждый прототип это тот же объект –  Который также может иметь прототип –  У прототипа прототипа также может быть прототип

Page 15: JavaScript. OOP (in russian)

Вызывает функцию как конструктор Строит цепочку прототипов

Оператор new

Page 16: JavaScript. OOP (in russian)

16

Работа оператора new

• new(Constructor, arguments):*!• Получает на вход 2 операнда

–  Функция должна иметь свойство prototype

• Создает временный объект (obj) • Добавляет свойство __proto__

–  obj.__proto__ = Constructor.prototype

• Вызывает конструктор над объектом –  Constructor.apply(obj, arguments)

• Конструктор вернул примитив. Результат obj • Иначе то, что вернул конструктор

Page 17: JavaScript. OOP (in russian)

17

new Smth() может вернуть не инстанс Smth!

Page 18: JavaScript. OOP (in russian)

18

function Constructor() { // no body } new Constructor() instanceof Constructor === true; // OK // Подмена результата function Constructor () { return {}; // <<< } new Constructor() instanceof Constructor === false; // <<< // Аналогично function Constructor() {return []} function Constructor() {return function () {}}

Подмена инстанса

Page 19: JavaScript. OOP (in russian)

19

It isn't JavaScript bug, it is feature!

Page 20: JavaScript. OOP (in russian)

20

function myNew(Constructor, args) { if (typeof Constructor !== "function") { throw new TypeError(); } if (typeof Constructor.prototype === "undefined") { throw new TypeError(); } var obj = { __proto__: Constructor.prototype }; var result = Constructor.apply(obj, args); if (typeof result === "object" && result !== null || typeof result === "function") { return result; } return obj; }

Оператор new в коде

Page 21: JavaScript. OOP (in russian)

21

Во многих браузерах __proto__ скрытое свойство. Менять и получать нельзя!

Page 22: JavaScript. OOP (in russian)

22

// Конструктор Grandfather var Grandfather = function () {}; Grandfather.prototype.color = 'green';

Пример new

var gf = new Grandfather(); gf.color; // "green"

// Это аналогично var gf = { __proto__: Grandfather.prototype }; gf.color; // "green"

Page 23: JavaScript. OOP (in russian)

23

Оператор new используется для построения цепочек прототипов

Page 24: JavaScript. OOP (in russian)

Цепочка прототипов это способ наследования в JavaScript

Page 25: JavaScript. OOP (in russian)

25

Цепочка прототипов

// Конструктор Grandfather var Grandfather = function () {}; Grandfather.prototype.color = 'green';

// Конструктор Father var Father = function () {}; typeof Father.prototype === "object";

// Для цепочки нам нужно получить вот это Father.prototype = { __proto__: Grandfather.prototype };

Page 26: JavaScript. OOP (in russian)

26

Строим цепочку прототипов явно

// Конструктор Father var Father = function () {}; Father.prototype = new Grandfather();

// Как помним, это аналогично: Father.prototype = { __proto__: Grandfather.prototype };

Page 27: JavaScript. OOP (in russian)

27

Не забываем! __proto__ лучше установить явно – через оператор new

Page 28: JavaScript. OOP (in russian)

28

var Grandfather = function () {}; // Конструктор Grandfather Grandfather.prototype.color = 'green'; var Father = function () {}; // Конструктор Father Father.prototype = new Grandfather(); // Наследуем var Son = function () {}; // Конструктор Son Son.prototype = new Father(); // Наследуем var g = new Grandfather(); // Экземпляр "класса" Grandfather var f = new Father(); // Экземпляр "класса" Father var s = new Son(); // Экземпляр "класса" Son // Изначально все зеленые console.log([g.color, f.color, s.color]); // ["green", "green", "green"]

Пример с мутантами

Page 29: JavaScript. OOP (in russian)

29

// Дед решил поменять свой цвет и цвет потомства Grandfather.prototype.color = 'blue'; // Все синие console.log([g.color, f.color, s.color]); // ["blue", "blue", "blue"] // Отец решил все вернуть для себя и своего потомства Father.prototype.color = 'green'; // Хотя мог исделать и так: // Grandfather.prototype.color = 'green'; // Цвет вернулся console.log([g.color, f.color, s.color]); // ["blue", "green", "green"]

Пример с мутантами

Page 30: JavaScript. OOP (in russian)

30

// Смысла нет Grandfather.prototype.color = 'blue'; console.log([g.color, f.color, s.color]); // ["blue", "green", "green"] // Сын решил поменял только собственное свойство s.color = 'black'; console.log([g.color, f.color, s.color]); // ["blue", "green", "black"] var SonsSon = function () {}; // Конструктор SonsSon SonsSon.prototype = new Son(); // Наследуем var ss = new SonsSon(); // Экземпляр "класса" SonsSon console.log([g.color, f.color, s.color, ss.color]); // ["blue", "green", "black", "green"]

Пример с мутантами

Page 31: JavaScript. OOP (in russian)

31

Цепочка прототипов: Grandfather

g __proto__ object

Grandfather.prototype color blue __proto__ object

Object.prototype __proto__ null

Page 32: JavaScript. OOP (in russian)

32

В конце экземпляр Son будет таким. Hell Mess…

var s = { color: 'black', // Поменял только собственное свойство __proto__: { // Son.prototype __proto__: { // Father.prototype color: 'green', // Отец решил вернуть цвет __proto__: { // Grandfather.prototype color: 'blue', // Дед решил поменять цвет __proto__: { // Object.prototype // Много разных свойств __proto__: null } } } } };

Цепочка прототипов: Son

Page 33: JavaScript. OOP (in russian)

33

Page 34: JavaScript. OOP (in russian)

34

А что, если в конструкторе alert(), а если он добавляет свойства?

Page 35: JavaScript. OOP (in russian)

35

alert('Mua-ha-ha')

// Конструктор Grandfather var Grandfather = function () { alert('Mua-ha-ha'); return ["Mua-ha-ha!"]; }; Grandfather.prototype.color = 'green';

// Конструктор Father var Father = function () {}; Father.prototype = new Grandfather();

Page 36: JavaScript. OOP (in russian)

36

alert('Mua-ha-ha')

// Конструктор Grandfather var Grandfather = function () { alert('Mua-ha-ha'); return "Mua-ha-ha!"; }; Grandfather.prototype.color = 'green';

// Конструктор Father var Father = function () {}; Father.prototype = new Grandfather();

Page 37: JavaScript. OOP (in russian)

37

Используется для чистого наследования цепочки прототипов new – это просто средство подмешать prototype

function inherits(Constructor, SuperConstructor) { var F = function () {}; // Временный, чистый конструктор // Сохраняем ссылку F.prototype = SuperConstructor.prototype; // Применяем __proto__ = prototype Constructor.prototype = new F(); }

Функция inherits или подобная

var Grandfather = function () {}; // Конструктор Grandfather Grandfather.prototype.color = 'green'; var Father = function () {}; // Конструктор Father // Father.prototype = new Grandfather(); inherits(Father, Grandfather); // Наследуем

Page 38: JavaScript. OOP (in russian)

38

Есть еще один вариант использовать Object.create(); Только ECMAScript 5

var Grandfather = function () {}; // Конструктор Grandfather Grandfather.prototype.color = 'green'; var Father = function () {}; // Конструктор Father // Father.prototype = new Grandfather(); // Что она делает - понятно Father.prototype = Object.create(Grandfather.prototype); // Полная и абсолютно честная версия Father.prototype = Object.create(Grandfather.prototype, { constructor: { value: Father, enumerable: false, writable: true, configurable: true } });

ECMAScript 5 – Object.create()

Page 39: JavaScript. OOP (in russian)

Оператор "точка" и []

Получение свойств

Page 40: JavaScript. OOP (in russian)

Используют цепочку прототипов и собственные свойства

Page 41: JavaScript. OOP (in russian)

41

Оператор "точка" и []

• getProperty(obj, name): *!• Ищет в собственных свойствах • Нет? – ищем в цепочке прототипов • Пока __proto__ !== null • Не нашли – возвращаем undefined

Page 42: JavaScript. OOP (in russian)

42

function getProperty(obj, name) { // Ищем в собственных if (obj.hasOwnProperty(name)) { return obj[name]; } // Ищем рекурсивно в цепочке прототипов else if (obj.__proto__ !== null) { return getProperty(obj.__proto__, name); } // Не нашли else { return undefined; } } // Пример getProperty(s, "color") === s.color;

Оператор "точка" и [] в коде

Page 43: JavaScript. OOP (in russian)

43

Цепочка прототипов объекта s s color black __proto__ object

Son.prototype __proto__ object

Grandfather.prototype color blue __proto__ object

Object.prototype __proto__ null

Участок цепочки Father пропущен

Page 44: JavaScript. OOP (in russian)

44

Храните функции в прототипе, а данные в собственных свойствах

Page 45: JavaScript. OOP (in russian)

Оператор instanceof

Page 46: JavaScript. OOP (in russian)

46

var u = new Grandfather(); var f = new Father(); var s = new Son(); s instanceof Son === true; // OK s instanceof Father === true; // OK? s instanceof Grandfather === true; // OK?? // Неужели множественное наследование??? s instanceof Object === true; // WAT??? s instanceof Array === false; // ОК!

Оператор instanceof

Page 47: JavaScript. OOP (in russian)

47

Оператор instanceof использует цепочку прототипов

Page 48: JavaScript. OOP (in russian)

48

Оператор instanceof

•  instanceof(obj, Constructor):Boolean!• Использует цепочку прототипов • Рекурсивно проверяет равенство Constructor.prototype === obj.__proto__!

•  До того пока __proto__ !== null – вернет false

Page 49: JavaScript. OOP (in russian)

49

function isInstanceOf(obj, Сonstructor) { // Нашли if (obj.__proto__ === Сonstructor.prototype) { return true; } // Ищем дальше рекурсивно else if (obj.__proto__ !== null) { return isInstanceOf(obj.__proto__, Сonstructor); } // Не нашли else { return false; } } // Пример isInstanceOf(s, Father) === s instanceof Father;

Оператор instanceof в коде

Page 50: JavaScript. OOP (in russian)

50

Цепочка прототипов объекта s s color black __proto__ object

Son.prototype __proto__ object

Grandfather.prototype color blue __proto__ object

Object.prototype __proto__ null

Участок цепочки Father пропущен

Page 51: JavaScript. OOP (in russian)

51

var s = new Son(); s instanceof Array === false; // ОК! Grandfather.prototype.__proto__ = Array.prototype; s instanceof Array === true; // WAT???

Оператор instanceof

Page 52: JavaScript. OOP (in russian)

52

var s = new Son(); s instanceof Array === false; // ОК! Grandfather.prototype.__proto__ = Array.prototype; s instanceof Array === true; // WAT???

Оператор instanceof

Page 53: JavaScript. OOP (in russian)

Вызов конструктора родителя Вызов метода родителя

Вызов метода родителя

Page 54: JavaScript. OOP (in russian)

54

var Grandfather = function (name) { this.name; }; Grandfather.prototype.hello = function () { return 'I am ' + this.name; }; var Father = function (name) { Grandfather.call(this, name); }; Father.prototype.hello = function () { return Grandfather.prototype.hello.call(this) + ' – Father'; };

Вызов метода родителя

Page 55: JavaScript. OOP (in russian)

Классы?

ECMAScript 6 class

Библиотеки для эмуляции классов

Трансляция в JavaScript

Page 56: JavaScript. OOP (in russian)

56

Находится в стадии черновика спецификации

class Grandfather { constructor () {} public color 'blue'; } class Father extends Grandfather { constructor () {} public color 'green'; } var f = new Father();

ECMAScript 6 class

Page 57: JavaScript. OOP (in russian)
Page 58: JavaScript. OOP (in russian)

58

В JavaScript нет и не будет классов. Слово class – синтаксический сахар.

Page 59: JavaScript. OOP (in russian)

59

Все это в конечном итоге будет цепочкой прототипов. Вот такой:

var Grandfather = function () {}; // Конструктор Grandfather Grandfather.prototype.color = 'blue'; var Father = function () {}; // Конструктор Father Father.prototype = Object.create(Grandfather.prototype); Father.prototype.color = 'green';

ECMAScript 6 class

Page 60: JavaScript. OOP (in russian)

Есть несколько библиотек…

Библиотеки для классов

Page 61: JavaScript. OOP (in russian)

61

Таких библиотек over 9000

Page 62: JavaScript. OOP (in russian)

62

Каждый программист на JavaScript должен написать свою реализацию классов ©

Page 63: JavaScript. OOP (in russian)

63

Библиотеки для классов

• Mootools • Klass •  JSClas • …

Over 9000 http://habrahabr.ru/post/132698/#comment_4404597

Page 64: JavaScript. OOP (in russian)

64

Mootools

var Grandfather = new Class({ initialize: function () { } }); var Father = new Class({ Extends: Grandfather, initialize: function () { } });

Все они выглядят примерно так

Page 65: JavaScript. OOP (in russian)

65

В JavaScript нет и не будет классов. new Class – для удобства разработчика.

Page 66: JavaScript. OOP (in russian)

Пишем на одном языке, где есть классы, а затем переделываем в JavaScript!

Трансляция в JS

Page 67: JavaScript. OOP (in russian)

67

Много языков транслируется в JS

• CoffeeScript • Dart • TypeScript • Processing • Python, Delphi, Ruby, C++ (LLVM)

Page 68: JavaScript. OOP (in russian)

68

Зачем транслируют?

• Не знают JavaScript и его особенностей • Удобно писать на 1м языке

–  Python, Ruby

• Синтаксический сахар –  CoffeeScript, Dart, TypeScript

• Очень долго переписывать –  Программы на C++

Page 69: JavaScript. OOP (in russian)

69

Проблемы трансляции

• Может быть крайне не оптимальна –  Тормоза и лаги –  Много костылей

• На выходе плохо читаемый код –  Сделан роботами для роботов

• Отлаживать в любом случае JavaScript

Page 70: JavaScript. OOP (in russian)

70

Лучше применять трансляцию в JavaScript только в крайнем случае!

Page 71: JavaScript. OOP (in russian)

Изменение базовых классов

Да, их также можно менять!

String.prototype

Array.prototype

Number.prototype

Page 72: JavaScript. OOP (in russian)

72

// Чтобы не зацепить хорошие браузеры if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (searchElement) { for (var i = 0; i < this.length; i++) { if (this[i] === searchElement) { return i; } } return -1; }; } [1, 2, 3, 4].indexOf(3); // 2

Polyfill для Array#indexOf Внимание! Это не полная реализация – не используйте ее! Все, что влезло в слайд.

Array indexOf method http://clck.ru/3mm5x

Page 73: JavaScript. OOP (in russian)

73

Number.prototype.times = function (callback) { for (var i = 0; i < this; i++) { callback(i); } }; // Пример (10).times(function (index) { console.log(index); }); // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

Number#times

Page 74: JavaScript. OOP (in russian)

74

Прототипы базовых классов изменяем только в крайнем случае или для polyfill!

Page 75: JavaScript. OOP (in russian)

75

JavaScript ООП

• Нет классов – есть прототипы • Прототипное наследование • Цепочка прототипов

–  inherits, Object.create()

• __proto__ и prototype • Оператор new • Оператор instanceof • Оператор точка и [] • Много библиотек для классов • Трансляция в JavaScript – крайний случай

Page 76: JavaScript. OOP (in russian)

Основы и заблуждения насчет JavaScript

http://clck.ru/0zjHr

Page 77: JavaScript. OOP (in russian)

77

Михаил Давыдов

Разработчик JavaScript

[email protected] azproduction

Спасибо