Автор Тема: ООП в Javascript  (Прочитано 8849 раз)

admin

  • Administrator
  • Newbie
  • *****
  • Сообщений: 47
    • Просмотр профиля
    • Email
ООП в Javascript
« : Ноябрь 09, 2011, 00:33:31 »
Цитата: Zeroglif
Цитата: Дмитрий Котеров
Выводит "function A() {}". Спрашивается, почему?
Сейчас попробую написать почему. Расскажу то, как я это себе представляю, не претендуя на истину и не используя слова-паразиты, вроде "класс", "суперкласс" и т.п. Итак, бермудский треугольник "наследования на базе прототипов" лежит между тремя островами:

  - объект-конструктор;
  - объект-прототип;
  - объект-экземпляр;

Разберём на простейшем примере процесс их создания и взаимодействия, например, для начала просто объявим в коде новую функцию A:

  function A() {};
Что присходит? Интерпретатор доходит до этой строки, обнаруживает блатное словечко function и проч. элементы конструкции объявленной функции, и совершает ряд заданных действий, среди которых выделим только нас интересующие:

  - создаётся новый объект (Function object);
  - этот объект обвешивается внутренними свойствами ([[Class]], [[Scope]], [[Call]], [[Construct]]...)
 
И на выходе мы должны получить на блюдечке объект-конструктор A со всеми присущими ему свойствами (по классу это функция, может конструировать и проч.) Но на этом интерпретатор ещё не закончит свою работу, он автоматом начинает создавать ассоциированный с объектом-конструктором объект-прототип:

  - создаётся новый объект;
  - значением свойства constructor этого объекта становится создавший его объект-конструктор;
  - значением свойства prototype объекта-конструктора становится вновь созданный объект;

Надо понимать, что свойство constructor появляется при создании объекта-прототипа и не имеет н-и-к-а-к-о-г-о отношения к объекту-экземпляру, его (экземпляра) ещё даже нет в природе. Таким образом, в итоге мы должны получить:

  A                       //ссылку на объект-конструктор;
  A.prototype             //ссылку на объект-прототип;
  A.prototype.consructor  //обратную ссылку на объект-конструктор;

Теперь запустим сам конструктор и создадим новый объект-экземпляр a с использованием оператора new и конструктора A:

  a = new A()
Что теперь присходит? Интерпретатор доходит до оператора new, проверяет стоит ли справа от него объект, имеет ли этот объект внутреннее свойство [[Construct]] (a он имеет, так как при создании A это поле было заведено) и даёт условную команду "Создать/сконструировать новый объект-экземпляр":

  - создаётся новый объект;
  - этому объекту навешивается внутреннее свойство [[Prototype]];
  - значением этого свойства становится значение свойства prototype объекта-конструктора;

Очень важно понять, откуда экземпляр берёт ассоциированный с конструктором прототип, и как он с ним связан. Берёт элементарно из свойства prototype конструктора на момент создания. Связь держит неявную, порвать её после создания экземпляра уже нельзя. Иными словами, конструктор ссылается на ассоциированный ему объект-прототип явно через ссылку A.prototype, а объект-экземпляр ссылается на тот же самый объект неявно через своё внутреннее свойство a.[[Prototype]], значение которого он получил от конструктора, то есть один смотрит на прототип, условно говоря, слева, а другой глазеет в то же самое место, но справа:

  A.prototype -----> объект-прототип <----- a.[[Prototype]]

Встаёт вопрос, есть ли явная связь между экземпляром и конструктором или прототипом. Ответ - есть, но эта связь сама по себе уже основана на  "наследовании на базе прототипов". Логика такого наследования предполагает, что интерпретатор ищет свойство объекта по цепочке прототипов, если не находит его сразу же. Именно это он и делает в данном случае, пытаясь отловить свойство constructor, т.к. такого поля в объекте-экземпляре a просто нет:

  a.constructor                // ищем свойство, не находим;
  a.[[Prototype]].constructor  // находим существующее свойство в цепи прототипов;

Отсюда должно быть понятно, что:
 
  A == a.constructor // true
  A.prototype == a.constructor.prototype // true
  A.prototype.constructor == a.constructor.prototype.constructor // true

Соответственно, если мы заведём в объекте-экземпляре своё собственное поле с именем constructor, то связь с объектом-прототипом не прервётся, но достать его или объект-конструктор через это свойство уже будет нельзя, т.к. поиск по цепочке прототипов не начнётся (имя-то будет сразу найдено).

Изменим пример и добавим ещё один объект-конструктор с тем, чтобы переопределить прототип. Под "переопределить прототип" я имею в виду навести ссылку A.prototype на другой объект.

  function A() {}; //первый конструктор
  function B() {}; //второй конструктор

  b = new B(); // создаём объект-экземпляр с помощью B;

  A.prototype = b; // переопределяем объект-прототип

  a = new A(); // создаём объект-экземпляр с помощью A;

  alert(a.constructor) // function B() {}

Почему же так получается, что a.constructor ведёт к объекту-конструктору B, а не ведёт к создавшему его A. Bсё просто. Интерпретатор не находит этого свойства непосредственно в a (его там нет и не было) и обращается к цепочке прототипов. Объектом-прототипом для a является b, т.к. именно на этот объект ссылалось свойство A.prototype в момент создания a (как я уже говорил раньше, неявное свойство a.[[Prototype]] в момент создания должно вести туда же, к b). Но и в этом объекте интерпретатор не находит свойство constructor и обращается к уже его собственному объекту-прототипу b, то есть объекту-прототипу, ассоциированному с его объектом-конструктором B. Иначе говоря:

  /*
  a.constructor                             - ищем свойство, не находим;
  a.[[Prototype]].constructor               - не находим в прототипе, продолжаем искать в прототипе прототипа
  a.[[Prototype]].[[Prototype]].constructor - упс, нашлось, это B
*/

Вот как бы вот так, очень надеюсь, что всё более-менее понятно... ибо чукча - не писатель, чукча - читатель ;-)
« Последнее редактирование: Январь 12, 2014, 23:05:34 от admin »

admin

  • Administrator
  • Newbie
  • *****
  • Сообщений: 47
    • Просмотр профиля
    • Email
Re: ООП в Javascript
« Ответ #1 : Ноябрь 09, 2011, 00:35:27 »
Перепечатка с форума dklab. Очень вумный дядька описывает. Раз в год перечитываю, до конца просветление не наступает.
Копирую сюда, чтобы потом не искать, а было под рукой, когда год опять пройдет.


 

Rambler's Top100