JavaScript. OOP (in russian)

535 views
429 views

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
535
On SlideShare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
7
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

JavaScript. OOP (in russian)

  1. 1. Михаил Давыдов Разработчик JavaScript JavaScript ООП
  2. 2. 3 JavaScript ООП •  Нет классов –  Но можно эмулировать их •  Есть прототипы •  Есть наследование на прототипах –  Делегирующее прототипное наследование •  Все можно менять во время работы –  Цепочку наследования можно менять –  Прототипы можно менять –  На классах так сделать нельзя •  Можно изменить прототипы базовых "классов"
  3. 3. 4 Сказка о мутантах
  4. 4. 5 Сказка о мутантах •  В далекой-далекой галактике •  Нет привычного нам наследования •  Есть телепатическое наследование –  "Телегенез" •  Действующие лица: –  Дедушка –  Отец –  Сын
  5. 5. 6 Структура мутанта Мутант "Телепатические" гены Собственные гены Движение генов
  6. 6. 7 Все зеленые Color   Дед Отец Сын
  7. 7. 8 Дед: хочу стать синим! Color   Дед Отец Сын
  8. 8. 9 Все посинели Color   Дед Отец Сын
  9. 9. 10 Отец: верну-ка я цвет Color   Color   Дед Отец Сын
  10. 10. 11 Дед синий, отец и сын зеленые Color   Color   Дед Отец Сын
  11. 11. 12 Сын: хочу быть черным Color   Color   Color   Дед Отец Сын
  12. 12. 13 Мутанты и JavaScript Size,   Age   Color   Объект Свойства прототипа Собственные свойства Делегирование Цепочка прототипов
  13. 13. 14 Собственные свойства и прототип •  Собственные свойства •  Свойства прототипа •  Любой объект имеет ссылку на прототип –  И примитив также* –  Имеет с рождения –  По умолчанию – Object.prototype •  Делегирование –  Мы можем пользоваться функциями прототипа не имея собственных •  Цепочка прототипов –  Каждый прототип это тот же объект –  Который также может иметь прототип –  У прототипа прототипа также может быть прототип
  14. 14. Вызывает функцию как конструктор Строит цепочку прототипов Оператор new
  15. 15. 16 Работа оператора new •  new(Constructor, arguments):*! •  Получает на вход 2 операнда –  Функция должна иметь свойство prototype •  Создает временный объект (obj) •  Добавляет свойство __proto__ –  obj.__proto__ = Constructor.prototype •  Вызывает конструктор над объектом –  Constructor.apply(obj, arguments) •  Конструктор вернул примитив. Результат obj •  Иначе то, что вернул конструктор
  16. 16. 17 new Smth() может вернуть не инстанс Smth!
  17. 17. 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 () {}} Подмена инстанса
  18. 18. 19 It isn't JavaScript bug, it is feature!
  19. 19. 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 в коде
  20. 20. 21 Во многих браузерах __proto__ скрытое свойство. Менять и получать нельзя!
  21. 21. 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"
  22. 22. 23 Оператор new используется для построения цепочек прототипов
  23. 23. Цепочка прототипов это способ наследования в JavaScript
  24. 24. 25 Цепочка прототипов // Конструктор Grandfather var Grandfather = function () {}; Grandfather.prototype.color = 'green'; // Конструктор Father var Father = function () {}; typeof Father.prototype === "object"; // Для цепочки нам нужно получить вот это Father.prototype = { __proto__: Grandfather.prototype };
  25. 25. 26 Строим цепочку прототипов явно // Конструктор Father var Father = function () {}; Father.prototype = new Grandfather(); // Как помним, это аналогично: Father.prototype = { __proto__: Grandfather.prototype };
  26. 26. 27 Не забываем! __proto__ лучше установить явно – через оператор new
  27. 27. 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"] Пример с мутантами
  28. 28. 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"] Пример с мутантами
  29. 29. 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"] Пример с мутантами
  30. 30. 31 Цепочка прототипов: Grandfather g __proto__ object Grandfather.prototype color blue __proto__ object Object.prototype __proto__ null
  31. 31. 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
  32. 32. 33
  33. 33. 34 А что, если в конструкторе alert(), а если он добавляет свойства?
  34. 34. 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();
  35. 35. 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();
  36. 36. 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); // Наследуем
  37. 37. 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()
  38. 38. Оператор "точка" и [] Получение свойств
  39. 39. Используют цепочку прототипов и собственные свойства
  40. 40. 41 Оператор "точка" и [] •  getProperty(obj, name): *! •  Ищет в собственных свойствах •  Нет? – ищем в цепочке прототипов •  Пока __proto__ !== null •  Не нашли – возвращаем undefined
  41. 41. 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; Оператор "точка" и [] в коде
  42. 42. 43 Цепочка прототипов объекта s s color black __proto__ object Son.prototype __proto__ object Grandfather.prototype color blue __proto__ object Object.prototype __proto__ null Участок цепочки Father пропущен
  43. 43. 44 Храните функции в прототипе, а данные в собственных свойствах
  44. 44. Оператор instanceof
  45. 45. 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
  46. 46. 47 Оператор instanceof использует цепочку прототипов
  47. 47. 48 Оператор instanceof •  instanceof(obj, Constructor):Boolean! •  Использует цепочку прототипов •  Рекурсивно проверяет равенство Constructor.prototype === obj.__proto__! •  До того пока __proto__ !== null – вернет false
  48. 48. 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 в коде
  49. 49. 50 Цепочка прототипов объекта s s color black __proto__ object Son.prototype __proto__ object Grandfather.prototype color blue __proto__ object Object.prototype __proto__ null Участок цепочки Father пропущен
  50. 50. 51 var s = new Son(); s instanceof Array === false; // ОК! Grandfather.prototype.__proto__ = Array.prototype; s instanceof Array === true; // WAT??? Оператор instanceof
  51. 51. 52 var s = new Son(); s instanceof Array === false; // ОК! Grandfather.prototype.__proto__ = Array.prototype; s instanceof Array === true; // WAT??? Оператор instanceof
  52. 52. Вызов конструктора родителя Вызов метода родителя Вызов метода родителя
  53. 53. 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'; }; Вызов метода родителя
  54. 54. Классы? ECMAScript 6 class Библиотеки для эмуляции классов Трансляция в JavaScript
  55. 55. 56 Находится в стадии черновика спецификации class Grandfather { constructor () {} public color 'blue'; } class Father extends Grandfather { constructor () {} public color 'green'; } var f = new Father(); ECMAScript 6 class
  56. 56. 58 В JavaScript нет и не будет классов. Слово class – синтаксический сахар.
  57. 57. 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
  58. 58. Есть несколько библиотек… Библиотеки для классов
  59. 59. 61 Таких библиотек over 9000
  60. 60. 62 Каждый программист на JavaScript должен написать свою реализацию классов ©
  61. 61. 63 Библиотеки для классов •  Mootools •  Klass •  JSClas •  … Over 9000 http://habrahabr.ru/post/132698/#comment_4404597
  62. 62. 64 Mootools var Grandfather = new Class({ initialize: function () { } }); var Father = new Class({ Extends: Grandfather, initialize: function () { } }); Все они выглядят примерно так
  63. 63. 65 В JavaScript нет и не будет классов. new Class – для удобства разработчика.
  64. 64. Пишем на одном языке, где есть классы, а затем переделываем в JavaScript! Трансляция в JS
  65. 65. 67 Много языков транслируется в JS •  CoffeeScript •  Dart •  TypeScript •  Processing •  Python, Delphi, Ruby, C++ (LLVM)
  66. 66. 68 Зачем транслируют? •  Не знают JavaScript и его особенностей •  Удобно писать на 1м языке –  Python, Ruby •  Синтаксический сахар –  CoffeeScript, Dart, TypeScript •  Очень долго переписывать –  Программы на C++
  67. 67. 69 Проблемы трансляции •  Может быть крайне не оптимальна –  Тормоза и лаги –  Много костылей •  На выходе плохо читаемый код –  Сделан роботами для роботов •  Отлаживать в любом случае JavaScript
  68. 68. 70 Лучше применять трансляцию в JavaScript только в крайнем случае!
  69. 69. Изменение базовых классов Да, их также можно менять! String.prototype Array.prototype Number.prototype …
  70. 70. 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
  71. 71. 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
  72. 72. 74 Прототипы базовых классов изменяем только в крайнем случае или для polyfill!
  73. 73. 75 JavaScript ООП •  Нет классов – есть прототипы •  Прототипное наследование •  Цепочка прототипов –  inherits, Object.create() •  __proto__ и prototype •  Оператор new •  Оператор instanceof •  Оператор точка и [] •  Много библиотек для классов •  Трансляция в JavaScript – крайний случай
  74. 74. Основы и заблуждения насчет JavaScript http://clck.ru/0zjHr
  75. 75. 77 Михаил Давыдов Разработчик JavaScript azproduction@yandex-team.ru azproduction Спасибо

×