КРиПИ - JavaScript ООП

КРиПИ - JavaScript ООП

B827d6cfdfbcfce33700b0e6cc03e344?s=128

Mikhail Davydov

November 02, 2012
Tweet

Transcript

  1. None
  2. Михаил Давыдов Разработчик JavaScript JavaScript ООП

  3. 3 JavaScript ООП •  Нет классов –  Но можно эмулировать

    их •  Есть прототипы •  Есть наследование на прототипах –  Делегирующее прототипное наследование •  Все можно менять во время работы –  Цепочку наследования можно менять –  Прототипы можно менять –  На классах так сделать нельзя •  Можно изменить прототипы базовых "классов"
  4. 4 Сказка о мутантах

  5. 5 Сказка о мутантах •  В далекой-далекой галактике •  Нет

    привычного нам наследования •  Есть телепатическое наследование –  "Телегенез" •  Действующие лица: –  Дедушка –  Отец –  Сын
  6. 6 Структура мутанта Мутант "Телепатические" гены Собственные гены Движение генов

  7. 7 Все зеленые Color   Дед Отец Сын

  8. 8 Дед: хочу стать синим! Color   Дед Отец Сын

  9. 9 Все посинели Color   Дед Отец Сын

  10. 10 Отец: верну-ка я цвет Color   Color   Дед

    Отец Сын
  11. 11 Дед синий, отец и сын зеленые Color   Color

      Дед Отец Сын
  12. 12 Сын: хочу быть черным Color   Color   Color

      Дед Отец Сын
  13. 13 Мутанты и JavaScript Size,   Age   Color  

    Объект Свойства прототипа Собственные свойства Делегирование Цепочка прототипов
  14. 14 Собственные свойства и прототип •  Собственные свойства •  Свойства

    прототипа •  Любой объект имеет ссылку на прототип –  И примитив также* –  Имеет с рождения –  По умолчанию – Object.prototype •  Делегирование –  Мы можем пользоваться функциями прототипа не имея собственных •  Цепочка прототипов –  Каждый прототип это тот же объект –  Который также может иметь прототип –  У прототипа прототипа также может быть прототип
  15. Вызывает функцию как конструктор Строит цепочку прототипов Оператор new

  16. 16 Работа оператора new •  new(Constructor, arguments):*! •  Получает на

    вход 2 операнда –  Функция должна иметь свойство prototype •  Создает временный объект (obj) •  Добавляет свойство __proto__ –  obj.__proto__ = Constructor.prototype •  Вызывает конструктор над объектом –  Constructor.apply(obj, arguments) •  Конструктор вернул примитив. Результат obj •  Иначе то, что вернул конструктор
  17. 17 new Smth() может вернуть не инстанс Smth!

  18. 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 () {}} Подмена инстанса
  19. 19 It isn't JavaScript bug, it is feature!

  20. 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 в коде
  21. 21 Во многих браузерах __proto__ скрытое свойство. Менять и получать

    нельзя!
  22. 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"
  23. 23 Оператор new используется для построения цепочек прототипов

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

  25. 25 Цепочка прототипов // Конструктор Grandfather var Grandfather = function

    () {}; Grandfather.prototype.color = 'green'; // Конструктор Father var Father = function () {}; typeof Father.prototype === "object"; // Для цепочки нам нужно получить вот это Father.prototype = { __proto__: Grandfather.prototype };
  26. 26 Строим цепочку прототипов явно // Конструктор Father var Father

    = function () {}; Father.prototype = new Grandfather(); // Как помним, это аналогично: Father.prototype = { __proto__: Grandfather.prototype };
  27. 27 Не забываем! __proto__ лучше установить явно – через оператор

    new
  28. 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"] Пример с мутантами
  29. 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"] Пример с мутантами
  30. 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"] Пример с мутантами
  31. 31 Цепочка прототипов: Grandfather g __proto__ object Grandfather.prototype color blue

    __proto__ object Object.prototype __proto__ null
  32. 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
  33. 33

  34. 34 А что, если в конструкторе alert(), а если он

    добавляет свойства?
  35. 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();
  36. 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();
  37. 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); // Наследуем
  38. 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()
  39. Оператор "точка" и [] Получение свойств

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

  41. 41 Оператор "точка" и [] •  getProperty(obj, name): *! • 

    Ищет в собственных свойствах •  Нет? – ищем в цепочке прототипов •  Пока __proto__ !== null •  Не нашли – возвращаем undefined
  42. 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; Оператор "точка" и [] в коде
  43. 43 Цепочка прототипов объекта s s color black __proto__ object

    Son.prototype __proto__ object Grandfather.prototype color blue __proto__ object Object.prototype __proto__ null Участок цепочки Father пропущен
  44. 44 Храните функции в прототипе, а данные в собственных свойствах

  45. Оператор instanceof

  46. 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
  47. 47 Оператор instanceof использует цепочку прототипов

  48. 48 Оператор instanceof •  instanceof(obj, Constructor):Boolean! •  Использует цепочку прототипов

    •  Рекурсивно проверяет равенство Constructor.prototype === obj.__proto__! •  До того пока __proto__ !== null – вернет false
  49. 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 в коде
  50. 50 Цепочка прототипов объекта s s color black __proto__ object

    Son.prototype __proto__ object Grandfather.prototype color blue __proto__ object Object.prototype __proto__ null Участок цепочки Father пропущен
  51. 51 var s = new Son(); s instanceof Array ===

    false; // ОК! Grandfather.prototype.__proto__ = Array.prototype; s instanceof Array === true; // WAT??? Оператор instanceof
  52. 52 var s = new Son(); s instanceof Array ===

    false; // ОК! Grandfather.prototype.__proto__ = Array.prototype; s instanceof Array === true; // WAT??? Оператор instanceof
  53. Вызов конструктора родителя Вызов метода родителя Вызов метода родителя

  54. 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'; }; Вызов метода родителя
  55. Классы? ECMAScript 6 class Библиотеки для эмуляции классов Трансляция в

    JavaScript
  56. 56 Находится в стадии черновика спецификации class Grandfather { constructor

    () {} public color 'blue'; } class Father extends Grandfather { constructor () {} public color 'green'; } var f = new Father(); ECMAScript 6 class
  57. None
  58. 58 В JavaScript нет и не будет классов. Слово class

    – синтаксический сахар.
  59. 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
  60. Есть несколько библиотек… Библиотеки для классов

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

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

    ©
  63. 63 Библиотеки для классов •  Mootools •  Klass •  JSClas

    •  … Over 9000 http://habrahabr.ru/post/132698/#comment_4404597
  64. 64 Mootools var Grandfather = new Class({ initialize: function ()

    { } }); var Father = new Class({ Extends: Grandfather, initialize: function () { } }); Все они выглядят примерно так
  65. 65 В JavaScript нет и не будет классов. new Class

    – для удобства разработчика.
  66. Пишем на одном языке, где есть классы, а затем переделываем

    в JavaScript! Трансляция в JS
  67. 67 Много языков транслируется в JS •  CoffeeScript •  Dart

    •  TypeScript •  Processing •  Python, Delphi, Ruby, C++ (LLVM)
  68. 68 Зачем транслируют? •  Не знают JavaScript и его особенностей

    •  Удобно писать на 1м языке –  Python, Ruby •  Синтаксический сахар –  CoffeeScript, Dart, TypeScript •  Очень долго переписывать –  Программы на C++
  69. 69 Проблемы трансляции •  Может быть крайне не оптимальна – 

    Тормоза и лаги –  Много костылей •  На выходе плохо читаемый код –  Сделан роботами для роботов •  Отлаживать в любом случае JavaScript
  70. 70 Лучше применять трансляцию в JavaScript только в крайнем случае!

  71. Изменение базовых классов Да, их также можно менять! String.prototype Array.prototype

    Number.prototype …
  72. 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
  73. 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
  74. 74 Прототипы базовых классов изменяем только в крайнем случае или

    для polyfill!
  75. 75 JavaScript ООП •  Нет классов – есть прототипы • 

    Прототипное наследование •  Цепочка прототипов –  inherits, Object.create() •  __proto__ и prototype •  Оператор new •  Оператор instanceof •  Оператор точка и [] •  Много библиотек для классов •  Трансляция в JavaScript – крайний случай
  76. Основы и заблуждения насчет JavaScript http://clck.ru/0zjHr

  77. 77 Михаил Давыдов Разработчик JavaScript azproduction@yandex-team.ru azproduction Спасибо