Upgrade to Pro — share decks privately, control downloads, hide ads and more …

КРиПИ - JavaScript ООП

КРиПИ - JavaScript ООП

Mikhail Davydov

November 02, 2012
Tweet

More Decks by Mikhail Davydov

Other Decks in Education

Transcript

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

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

    привычного нам наследования •  Есть телепатическое наследование –  "Телегенез" •  Действующие лица: –  Дедушка –  Отец –  Сын
  3. 13 Мутанты и JavaScript Size,   Age   Color  

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

    прототипа •  Любой объект имеет ссылку на прототип –  И примитив также* –  Имеет с рождения –  По умолчанию – Object.prototype •  Делегирование –  Мы можем пользоваться функциями прототипа не имея собственных •  Цепочка прототипов –  Каждый прототип это тот же объект –  Который также может иметь прототип –  У прототипа прототипа также может быть прототип
  5. 16 Работа оператора new •  new(Constructor, arguments):*! •  Получает на

    вход 2 операнда –  Функция должна иметь свойство prototype •  Создает временный объект (obj) •  Добавляет свойство __proto__ –  obj.__proto__ = Constructor.prototype •  Вызывает конструктор над объектом –  Constructor.apply(obj, arguments) •  Конструктор вернул примитив. Результат obj •  Иначе то, что вернул конструктор
  6. 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 () {}} Подмена инстанса
  7. 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 в коде
  8. 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"
  9. 25 Цепочка прототипов // Конструктор Grandfather var Grandfather = function

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

    = function () {}; Father.prototype = new Grandfather(); // Как помним, это аналогично: Father.prototype = { __proto__: Grandfather.prototype };
  11. 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"] Пример с мутантами
  12. 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"] Пример с мутантами
  13. 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"] Пример с мутантами
  14. 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
  15. 33

  16. 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();
  17. 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();
  18. 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); // Наследуем
  19. 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()
  20. 41 Оператор "точка" и [] •  getProperty(obj, name): *! • 

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

    Son.prototype __proto__ object Grandfather.prototype color blue __proto__ object Object.prototype __proto__ null Участок цепочки Father пропущен
  23. 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
  24. 48 Оператор instanceof •  instanceof(obj, Constructor):Boolean! •  Использует цепочку прототипов

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

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

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

    false; // ОК! Grandfather.prototype.__proto__ = Array.prototype; s instanceof Array === true; // WAT??? Оператор instanceof
  29. 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'; }; Вызов метода родителя
  30. 56 Находится в стадии черновика спецификации class Grandfather { constructor

    () {} public color 'blue'; } class Father extends Grandfather { constructor () {} public color 'green'; } var f = new Father(); ECMAScript 6 class
  31. 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
  32. 63 Библиотеки для классов •  Mootools •  Klass •  JSClas

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

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

    – для удобства разработчика.
  35. 67 Много языков транслируется в JS •  CoffeeScript •  Dart

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

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

    Тормоза и лаги –  Много костылей •  На выходе плохо читаемый код –  Сделан роботами для роботов •  Отлаживать в любом случае JavaScript
  38. 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
  39. 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
  40. 75 JavaScript ООП •  Нет классов – есть прототипы • 

    Прототипное наследование •  Цепочка прототипов –  inherits, Object.create() •  __proto__ и prototype •  Оператор new •  Оператор instanceof •  Оператор точка и [] •  Много библиотек для классов •  Трансляция в JavaScript – крайний случай