Форум dkLab и Denwer
Здесь общаются Web-разработчики.
Генеральный спонсор:
Хостинг «Джино»

40 Наследование в JavaScript (Дмитрий Котеров)
Goto page 1, 2, 3, 4, 5, 6  Next
Author Message
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Thu Dec 02, 2004 9:09 pm (написано за 2 секунды)
   Post subject: 40 Наследование в JavaScript
Reply with quote

dklab.ru/chicken/nablas/40.html
Back to top
View user's profile Send private message Send e-mail
Dwarf
Guest





Карма: 387
   поощрить/наказать


PostPosted: Fri Dec 03, 2004 4:32 pm (спустя 19 часов 22 минуты)
   Post subject: Описки
Reply with quote

Там есть 2 описки:
Quote:
Напоминаем, как работает основная часть данной программы.

   1. Когда мы вызываем vehicle.drive() (по-другому это можно записать как vehicle['drive']), интерпретатор начинает просматривать содержимое хэша vehicle в поисках элемента drive.
После vehicle['drive'] надо ставить (), иначе функция запущена не будет.
Quote:
// Создание объекта стандартным способом.
self.Car = function() { alert("Car") }
var vehicle = new selfCar();
selfCar(); => self.Car();
Back to top
Rumata
Профессионал



Joined: 17 Aug 2003
Posts: 1850
Карма: 185
   поощрить/наказать


PostPosted: Fri Dec 03, 2004 5:02 pm (спустя 30 минут; написано за 1 минуту 5 секунд)
   Post subject:
Reply with quote

Дмитрий, я прошу прощения, но мне последняя набла показалась очень тяжелой для понимания.
Даже учитывая, что я немного смыслю в JavaScript.
Back to top
View user's profile Send private message
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Fri Dec 03, 2004 6:13 pm (спустя 1 час 11 минут; написано за 1 минуту 2 секунды)
   Post subject:
Reply with quote

Dwarf wrote:
selfCar
Большое спасибо, исправил.

Rumata:
Ильдар, а она такая и есть. Материал весьма и весьма сложный, тут ничего не поделать. В принципе, можно конец перенести в начало.

А какие места Вам показались особенно тяжелыми?
Back to top
View user's profile Send private message Send e-mail
Rumata
Профессионал



Joined: 17 Aug 2003
Posts: 1850
Карма: 185
   поощрить/наказать


PostPosted: Fri Dec 03, 2004 7:26 pm (спустя 1 час 12 минут; написано за 18 секунд)
   Post subject:
Reply with quote

Дмитрий. можете не согласиться со мной. но в целом, мне именно целиком статья показалась тяжеловесной..
если сравнивать с предыдущими (38 и 39), то самая легкая (и я бы сказал - кристальная) была - 39.

(это мое скромное имхо) первый листинг и описание запутаны. может пример неудачный?
я долго не мог понять, что же я не понимаю.
видимо этот код (листинги 1 и 2) для меня оказался очевидным

честно говоря именно введение и задало уровень сложности всей статьи
Back to top
View user's profile Send private message
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Fri Dec 03, 2004 7:57 pm (спустя 30 минут; написано за 34 секунды)
   Post subject:
Reply with quote

Немного подправил первый листинг и объяснение.
Back to top
View user's profile Send private message Send e-mail
Rumata
Профессионал



Joined: 17 Aug 2003
Posts: 1850
Карма: 185
   поощрить/наказать


PostPosted: Fri Dec 03, 2004 8:20 pm (спустя 22 минуты; написано за 35 секунд)
   Post subject:
Reply with quote

во...
Quote:
В объектно-ориентированных языках с поддержкой классов (C++, Java, PHP, Perl, Ptyhon и т. д.) конструкторы базовых классов обычно вызываются непосредственно перед конструкторами производных.
почему перед, а не внутри производного класса?
Back to top
View user's profile Send private message
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Sat Dec 04, 2004 1:15 am (спустя 4 часа 54 минуты; написано за 31 секунду)
   Post subject:
Reply with quote

Потому что слово не удалось тогда подобрать. ;-)
Поправил. Внутри, конечно, будет правильнее (это только в C++ вызываются перед).
Back to top
View user's profile Send private message Send e-mail
Rumata
Профессионал



Joined: 17 Aug 2003
Posts: 1850
Карма: 185
   поощрить/наказать


PostPosted: Sat Dec 04, 2004 1:53 am (спустя 38 минут; написано за 2 минуты 52 секунды)
   Post subject:
Reply with quote

Дмитрий Котеров wrote:
это только в C++ вызываются перед
разве??? проиллюстрируете?
Back to top
View user's profile Send private message
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Sat Dec 04, 2004 2:48 am (спустя 54 минуты; написано за 1 минуту 22 секунды)
   Post subject:
Reply with quote

Code (c++): скопировать код в буфер обмена
class Base {
  Base() {
    //
  }
}
class Derive {
  Derive(): Base() {
    //
    //
  }
}
Back to top
View user's profile Send private message Send e-mail
Rumata
Профессионал



Joined: 17 Aug 2003
Posts: 1850
Карма: 185
   поощрить/наказать


PostPosted: Sat Dec 04, 2004 3:00 am (спустя 12 минут; написано за 2 минуты 22 секунды)
   Post subject:
Reply with quote

опять не согласен...

конструктор родителя вызывается в примере после потомка
и (если мне не изменяет память, если изменяет - поправьте) такая запись означает, что метод родителя вызывается, по умолчанию, внутри метода потомка
Back to top
View user's profile Send private message
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Sat Dec 04, 2004 3:28 am (спустя 27 минут; написано за 58 секунд)
   Post subject:
Reply with quote

Конструктор-то вызывается после. Но код конструктора вызывается до.
Rumata wrote:
такая запись означает, что метод родителя вызывается, по умолчанию, внутри метода потомка
Это Вы о чем? О примере из предыдущего постинга? Если да, то - нет, такая запись называется списком инициализаторов.
Back to top
View user's profile Send private message Send e-mail
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Sat Dec 04, 2004 3:40 am (спустя 11 минут; написано за 54 секунды)
   Post subject:
Reply with quote

Нашел баг в коде: если много вложенных наследований, были глюки. Исправил. Код для тестирования:
Code (html): скопировать код в буфер обмена
<script (december.com/html/4/element/script.html) language="JavaScript" src="../Oop.js"> (december.com/html/4/element/.html)</script>
<script> (december.com/html/4/element/script.html)</script>
Раньше на этом коде работало неправильно.
Back to top
View user's profile Send private message Send e-mail
Rumata
Профессионал



Joined: 17 Aug 2003
Posts: 1850
Карма: 185
   поощрить/наказать


PostPosted: Sat Dec 04, 2004 3:43 am (спустя 3 минуты; написано за 3 секунды)
   Post subject:
Reply with quote

Дмитрий Котеров wrote:
Конструктор-то вызывается после. Но код конструктора вызывается до
Дмитрий Котеров wrote:
Если да, то - нет
ничего не понял
Back to top
View user's profile Send private message
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Sat Dec 04, 2004 4:22 am (спустя 38 минут; написано за 2 минуты 44 секунды)
   Post subject:
Reply with quote

А Вы перечитайте.

В C++ конструктор состоит из двух частей:
- список инициализаторов, определяющий параметры, передаваемые базовым классам;
- само тело конструктора.

Соответственно, базовый конструктор вызывается в списке инициализаторов, до вызова тела конструктора.

В то же время, когда мы создаем в программе некоторый объект, мы делаем new ИмяКласса(). При этом вначале вызывается конструктор ИмяКласса(), а уж затем - конструктор ИмяБазовогоКласса() (см. на порядок машинных инструкций CALL, например). Так что - базовый конструктор вызывается после производного.

Таким образом, базовый конструктор вызывается после производного, однако до первого оператора тела производного. Такой вот дзен.
Back to top
View user's profile Send private message Send e-mail
Rumata
Профессионал



Joined: 17 Aug 2003
Posts: 1850
Карма: 185
   поощрить/наказать


PostPosted: Sat Dec 04, 2004 5:16 am (спустя 54 минуты; написано за 5 минут 14 секунд)
   Post subject:
Reply with quote

это все верно. но как-то странно вы сдвигаете рассматриваемую тему под другой угол зрения. отсюда и дзен.
мое: родитель вызывается внутри потомка
Ваше: родитель вызывается после потомка

мы говорим об одном и том же, но по разному, с разных точек зрения

но в Вашем толковании можно понимать и так
Code (any language): скопировать код в буфер обмена
Child::method();
Parent::method();
и так
Code (any language): скопировать код в буфер обмена
Child::method()
{
  Parent::method();
  // child does something
}
Back to top
View user's profile Send private message
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Sat Dec 04, 2004 7:31 am (спустя 2 часа 15 минут; написано за 7 секунд)
   Post subject:
Reply with quote

Я же поправил уже статью.
Back to top
View user's profile Send private message Send e-mail
Dwarf
Guest





Карма: 387
   поощрить/наказать


PostPosted: Wed Dec 08, 2004 6:30 pm (спустя 4 дня 10 часов 58 минут; написано за 1 минуту 3 секунды)
   Post subject: Выделение кода (офф)
Reply with quote

А сожно в форуме сделать так, что бы в коде не было html мнемоник, а?
Читать жутко неудобно. Да и копируеться он вместе с ними.
Back to top
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Wed Dec 08, 2004 6:57 pm (спустя 26 минут; написано за 22 секунды)
   Post subject:
Reply with quote

Их и не должно быть - это глюк очередной. Попробую поправить.
Back to top
View user's profile Send private message Send e-mail
YuppY
Guest





Карма: 387
   поощрить/наказать


PostPosted: Mon Dec 20, 2004 4:16 pm (спустя 11 дней 21 час 19 минут)
   Post subject:
Reply with quote

Можно же делать гораздо проще:
Code (JavaScript): скопировать код в буфер обмена
function inherit(object)
{
    function temp() {}
    temp.prototype = object.prototype

    return (new temp())
}

function A() {} //
A.prototype.x = 10

function B() {} //
B.prototype = inherit(A) //
B.prototype.y = 20 //

var a = A(), b = B()
alert(a.x) // 10
alert(a.y) // undefined
alert(b.x) // 10
alert(b.y) // 20

//
A.prototype.x = 'base value'

alert(a.x) // base value
alert(b.x) // base value

//
B.prototype.x = 'new value'

alert(a.x) // base value
alert(b.x) // new value
 
Кстати, сейчас пытаюсь сделать библиотеку, аналогичную вашей, но с честным наследованием и более удобным интерфейсом. Но пока что застрял на том, что не знаю как создать вызываемый объект :(
Back to top
YuppY
Guest





Карма: 387
   поощрить/наказать


PostPosted: Mon Dec 20, 2004 4:28 pm (спустя 11 минут; написано за 40 секунд)
   Post subject:
Reply with quote

Насчет нечестного наследования - не прав, извините.
Back to top
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Mon Dec 20, 2004 4:53 pm (спустя 25 минут; написано за 53 секунды)
   Post subject:
Reply with quote

Вы, вообще-то, наблу читали?
Где B instanceof A?
Где вызов родительского конструктора?

Наконец, у Вас temp создается как глобальная функция (window.temp)! Надо через var делать хотя бы.
Back to top
View user's profile Send private message Send e-mail
YuppY
Guest





Карма: 387
   поощрить/наказать


PostPosted: Mon Dec 20, 2004 5:40 pm (спустя 46 минут; написано за 10 секунд)
   Post subject:
Reply with quote

Еще раз извините, забыл про new, должно быть так:
Code (JavaScript): скопировать код в буфер обмена
var a = new A(), b = new B()
Quote:
Где B instanceof A?
А с чего так должно быть? instanceof == 'экземпляр класса', с этим все как надо:
Code (JavaScript): скопировать код в буфер обмена
alert(a instanceof A) // true
alert(b instanceof B) // true
alert(b instanceof A) // true
alert(a instanceof B) // false
 
Quote:
Где вызов родительского конструктора?
Этому место в конструкторе производного класса. Если нужно его вызвать, то это так:
Code (JavaScript): скопировать код в буфер обмена
function B() {
    A.apply(this)
}
Или вы про то, что он у меня автоматом не наследуется?
Quote:
у Вас temp создается как глобальная функция (window.temp)
Ну с каких это пор объявление функции внутри функции стало глобальным? Неправда это.
Back to top
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Mon Dec 20, 2004 6:01 pm (спустя 21 минуту; написано за 6 минут 56 секунд)
   Post subject:
Reply with quote

YuppY wrote:
instanceof == 'экземпляр класса',
Ой, виноват! Перепутал с isa.
YuppY wrote:
Или вы про то, что он у меня автоматом не наследуется?
В частности, про это.

Вы обратите внимание: Вам приходится постоянно тянуть за собой названия A и B. Вот захотите изменить имя класса A, придется в ста местах еще менять.
YuppY wrote:
Ну с каких это пор объявление функции внутри функции стало глобальным? Неправда это.
И снова Вы правы. Что-то я совсем плохой стал. ;-)

и еще одно: у Вас прототип B вообще никак не связан с A. Он связан с временной анонимной функцией, а также с прототипом A. Честно говоря, я вообще не понимаю, почему срабатывает alert(b instanceof A)! Ведь A не является конструктором ни для одного подобьекта b. Или instanceof сравнивает прототипы, а не сами имена конструкторов?

А вообще, Ваш код делает практически то же самое, что и мой. Только Вы не заострили внимание на корструкторе. Если взять мой и вырезать комментарии, получится:
Code (JavaScript): скопировать код в буфер обмена
function newClass(parent, prop)
{
  var clazz = function() {
    if (clazz.preparing) return delete(clazz.preparing);
    if (clazz.constr) {
      this.constructor = clazz; // we need it!
      clazz.constr.apply(this, arguments);
    }
  }
  clazz.prototype = {}; // no prototype by default
  if (parent) {
    parent.preparing = true;
    clazz.prototype = new parent;
    clazz.prototype.constructor = parent;
    clazz.constr = parent; // BY DEFAULT - parent constructor
  }
  if (prop) {
    var cname = "constructor";
    for (var k in prop) {
      if (k != cname) clazz.prototype[k] = prop[k];
    }
    if (prop[cname] && prop[cname] != Object)
      clazz.constr = prop[cname];
  }
  return clazz;
}
Как видите, практически то же самое, но только я стараюсь сохранить ссылку на базовый конструктор, чтобы к нему можно было обратиться через прототип (а не явно через имя, как делаете Вы).
Back to top
View user's profile Send private message Send e-mail
YuppY
Guest





Карма: 387
   поощрить/наказать


PostPosted: Tue Dec 21, 2004 12:42 pm (спустя 18 часов 41 минуту)
   Post subject:
Reply with quote

Quote:
Честно говоря, я вообще не понимаю, почему срабатывает alert(b instanceof A)!
Меня тоже поначалу удивило, но все согласно спецификации: V instanceof F - вызывает внутреннюю функцию F.HasInstance(V), которая работает так:
Quote:
When the [[HasInstance]] method of F is called with value V, the following steps are taken:
1. If V is not an object, return false.
2. Call the [[Get]] method of F with property name "prototype".
3. Let O be Result(2).
4. If O is not an object, throw a TypeError exception.
5. Let V be the value of the [[Prototype]] property of V.
6. If V is null, return false.
7. If O and V refer to the same object or if they refer to objects joined to each other (13.1.2), return
true.
8. Go to step 5.
Мне в вашем коде не понравилось то, что он резервирует имена 'constr' и 'preparing', которые кто-нибудь рано или поздно захочет использовать в своих объектах и получит баги. От 'preparing' можно избавиться, используя мою функцию inherit, 'constr' можно обозвать как-нибудь позаковыристей и задокументировать. Я бы его назвал по-питоновски '__init__'.
Back to top
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Tue Dec 21, 2004 2:20 pm (спустя 1 час 37 минут; написано за 3 минуты 21 секунду)
   Post subject:
Reply with quote

Понятно. Значит, instanceof сравнивает прототипы, а не сами объекты. Интересно.
YuppY wrote:
 он резервирует имена 'constr' и 'preparing', которые кто-нибудь рано или поздно захочет использовать в своих объектах
Это не совсем так. Он данные имена резервирует как свойства конструктора, а не как свойства нового объекта. Т.е.

function tmp() {}
tmp.constr = ...
tmp.preparing = ...

Соответственно, побочных эффектов быть не должно.

Ваша функция inherit, как я уже писал, отбрасывает объект A. Соответственно, не получится обращаться к конструктору базового подобъекта через this.constructor.prototype.constructor, и придется тянуть имя A через всю программу (для A.call(this, ...)).
Back to top
View user's profile Send private message Send e-mail
Guest






Карма: 387
   поощрить/наказать


PostPosted: Wed Jun 01, 2005 1:00 pm (спустя 5 месяцев 10 дней 22 часа 40 минут; написано за 3 минуты 22 секунды)
   Post subject: private properties
Reply with quote

Как задать приватные свойства и функции, используя метод newClass? В 39 набле они достаточно удобно описаны и используются. При попытке же переписать определения классов с помощью newClass сталкиваюсь с тем, что все функции становятся public. Это неудобно, т.к. приходится переписывать тела функций.
Back to top
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Wed Jun 01, 2005 2:33 pm (спустя 1 час 33 минуты; написано за 55 секунд)
   Post subject:
Reply with quote

Вот так не получится?
Code (JavaScript): скопировать код в буфер обмена
Car = newClass(null, {
  constructor: function() {
    document.writeln("  Car().");
    var private_var = 1;
    this.drive = function() {
      document.writeln(" Car.drive(): "+private_var);
    }
  }
});
Back to top
View user's profile Send private message Send e-mail
Guest






Карма: 387
   поощрить/наказать


PostPosted: Wed Jun 01, 2005 4:30 pm (спустя 1 час 56 минут; написано за 2 минуты 13 секунд)
   Post subject:
Reply with quote

Изменил базовый класс из примера так:
Code (JavaScript): скопировать код в буфер обмена
//
Car = newClass(null, {
  constructor: function() {
    document.writeln("  Car().");
    var private_var = 1;
    this.drive = function() {
      document.writeln(" Car.drive(): "+private_var);
    }
  }
/*
});
Было:
Code (JavaScript): скопировать код в буфер обмена
 
Стало:
Code (JavaScript): скопировать код в буфер обмена
 
Куда-то делся Zaporojets.drive().
Back to top
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Wed Jun 01, 2005 5:34 pm (спустя 1 час 3 минуты; написано за 42 секунды)
   Post subject:
Reply with quote

Хм. Ну тогда попробуйте не this.drive = ..., а this.constructor.prototype.drive = ...
Экспериментируйте, короче говоря.
Back to top
View user's profile Send private message Send e-mail
Guest






Карма: 387
   поощрить/наказать


PostPosted: Wed Jun 01, 2005 6:06 pm (спустя 32 минуты; написано за 1 минуту 52 секунды)
   Post subject:
Reply with quote

Совместил два подхода - получилось довольно удобно...
Code (JavaScript): скопировать код в буфер обмена
<pre><script>
// inheritance function
function inherit(object)
{
  function temp() {};
  temp.prototype = object.prototype;
  return (new temp());
}

// base class
function Car() {
  this.x = 10; // public var
  var xyx = 20; // private var

  this.drive = function() { // public function
    document.writeln(" Car.drive(): "+[this.x,xyx,hidden()]);
  }

  function hidden() { // private function
    return "WHAT?";
  }

  this.constr = function() {
    document.writeln("  Car().");
  }

  // blocking first new() operator from constructor calling
  if(Car.prototype.preparing) {Car.prototype.preparing = false;}
  else this.constr();
}

Car.prototype.preparing = true;
Car.prototype = new Car(); // for this.* vars/functions working

// you can add vars/functions after class

Car.prototype.x = 11// public var
Car.prototype.constr = function() {
  document.writeln("   Car().");
}

/*

// inherited class
function Zaporojets() {

  this.fun = 10; // public var
  var sysys = 20; // private var

  this.drive = function() { // overridden public function
    document.writeln(" Zaporojets.drive(): "+[this.fun,sysys,show()]);
    document.writeln(" public : "+this.constructor.prototype.x);   // 10
    document.writeln(" private : "+this.constructor.prototype.xyx);// undefined
    return this.constructor.prototype.drive.call(this); // call base
  }
 
  function show() { // private function
    return "U SEE ME";
  }

  this.crack = function() {
    document.writeln(" Zaporojets.crack()");
  }

  this.constr = function() {
    document.writeln("  Zaporojets().");
    this.constructor.prototype.constr.call(this);
  }

  // blocking first new() operator from constructor calling
  if(Zaporojets.prototype.preparing) {Zaporojets.prototype.preparing = false;}
  else this.constr();
}
Zaporojets.prototype = inherit(Car); // inherit Zaporojets

Zaporojets.prototype.y = 20; // additional var
/*

Zaporojets.prototype.crack = function() {
  document.writeln("  Zaporojets.crack()");
}

/*

Zaporojets.prototype.preparing = true;
Zaporojets.prototype = new Zaporojets();

document.writeln(" .");

//
var vehicle = new Zaporojets();
vehicle.drive(); //

//
var other = new Zaporojets();
other.crack(); //

</script></pre>
Вывод:
Code (any language): скопировать код в буфер обмена
Программа запущена.
Вызван конструктор Zaporojets().
Вызван внешний конструктор Car().
Вызван Zaporojets.drive(): 10,20,U SEE ME
Базовая public переменная: 11
Базовая private переменная: undefined
Вызван Car.drive(): 11,20,WHAT?
Вызван конструктор Zaporojets().
Вызван внешний конструктор Car().
Вызван Zaporojets.crack()
Back to top
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Wed Jun 01, 2005 6:24 pm (спустя 18 минут; написано за 32 секунды)
   Post subject:
Reply with quote

Это Вы не "совместили подходы", а воспользовались методом, который в одной из статей упомянут - со всеми его недостатками.
Back to top
View user's profile Send private message Send e-mail
eugene_v
Guest





Карма: 387
   поощрить/наказать


PostPosted: Thu Jun 09, 2005 5:25 pm (спустя 7 дней 23 часа 1 минуту; написано за 5 минут 22 секунды)
   Post subject:
Reply with quote

Я новичок в JavaScript'e, прочитал 38, 39, 40 наблы и "Ядро JavaScript 1.5. Руководство по Использованию."
Экспериментировал -- очень меня заинтересовало наследование на базе прототипов.

Кто-нибудь может обяснить почему в следующем коде оба alert'а выдают true ?

function A() {
}

function B() {
}

b1 = new B();

B.prototype = new A();
b2 = new B();

alert(b1.constructor === B);
alert(b2.constructor === A);


Только поняв то, что b2.constructor === A, я понял почему код типа
b2.constructor.prototype.new_prop = 10 модифицирует прототип A так,
что последующие new A() создают объекты с уже определенным new_prop !!
Для меня это полная неожиданность :)

А вообще, такое ощущение, что для полного понимая нехватает просто понятной картинки.
Я прочитал кучи текстовых описаний, но похоже так чего-то и недопонял...
Заранее спасибо.
Back to top
Андрей Сумин
Участник форума



Joined: 23 Nov 2005
Posts: 60
Карма: 5
   поощрить/наказать

Location: Москва

PostPosted: Thu Feb 02, 2006 8:11 pm (спустя 7 месяцев 23 дня 2 часа 46 минут; написано за 3 минуты 7 секунд)
   Post subject:
Reply with quote

Дальне повторное наследование работает не совсем так как я ожидал:
Code (JavaScript): скопировать код в буфер обмена
//
Car = newClass(null, {
  constructor: function() {
        document.writeln("  Car().");
  },
  drive: function() {
        document.writeln(" Car.drive()");
  }
});

//
Zaporojets = newClass(Car, {
  constructor: function() {
        document.writeln("  Zaporojets().");
        this.constructor.prototype.constructor.call(this);
  },
  crack: function() {
        document.writeln(" Zaporojets.crack()");
  },
  drive: function() {
        document.writeln(" Zaporojets.drive()");
        return this.constructor.prototype.drive.call(this);
  }
});

Niva = newClass(Zaporojets, {
  constructor: function() {
        document.writeln("  Niva().");
        this.constructor.prototype.constructor.call(this);
  },
  drive: function() {
        document.writeln(" Niva.drive()");
        return this.constructor.prototype.drive.call(this);
  }
});

var vehicle = new Niva();
vehicle.drive(); //
 
Выдает следующее:
Code (any language): скопировать код в буфер обмена
Нет ожидаемого вызова Zaporojets.drive();
Back to top
View user's profile Send private message
Zeroglif
Участник форума



Joined: 02 Jan 2006
Posts: 293
Карма: 61
   поощрить/наказать


PostPosted: Fri Feb 03, 2006 12:06 pm (спустя 15 часов 54 минуты; написано за 6 минут 6 секунд)
   Post subject:
Reply with quote

А покритиковать статью можно? Я немножко... ;-) Немного критики 39-40 набл (объединил наблы т.к. касается прототипов):
Quote:
Каждый объект (хэш) имеет ассоциированный с ним хэш-прототип.
Quote:
У каждого объекта есть прототип, который также является объектом.
Quote:
Фактически, класс — это всего лишь функция, с которой ассоциирован отдельный прототип.
Хочу немного придраться к словам, только пользы ради. Использовать слово "ассоциированный" или "есть" применительно к любому объекту (а не только конструктору) не совсем верно, может сложится ложное впечатление, что у 5 объектов (созданных конструктором) 5 прототипов. Согласно ES с каждым конструктором ассоциирован прототип,а каждый созданный этим конструктором объект неявно ссылается на прототип, ассоциированный с конструктором. Это важный текст, т.к. раскрывает суть.
Quote:
Оператор new создает новый объект, присваивает его значение специальной переменной this, а затем вызывает функцию-конструктор и делает доступным this внутри нее.
Оператор new ничего не создаёт на то есть конструкторы. Основная задача оператора new - вызвать внутренний метод (Construct) объекта, предварительно проверив, что справа от него стоит именно объект, и именно имеющий этот самый метод. Далее в рамках уже самого "процесса конструирования" объектом-конструктором создаётся новый объект, он передаётся значением в this... и т.д. и т.п.
Quote:
Кроме того, «просто функций» также не существует: любая функция является в действительности методом — либо указанного вами объекта, либо же объекта window...
К примеру, анонимная функция function(){} не является методом энного объекта и, лишённая идентификатора, никак не может быть досягаемым свойством window, это "просто функция"?
Quote:
Собственно, все «наследование» сводится к присваиванию нового значения прототипу объекта (свойству prototype).
Если прототип не переопределять вовсе, а только добавлять к нему нужные свойства, prototype-based inheritance будет налицо, я это к тому, что наследование не сводится только лишь к присваиванию нового значения прототипу (переопределению самого объекта-прототипа).
Quote:
Какие выводы можно сделать из примера?
1. В самом объекте свойства prototype не имеет никакого особого смысла.
2. К прототипу объекта следует обращаться через служебное свойство constructor, присутствующее в любом хэше.
3. Выражение obj.constructor.prototype (а не obj.prototype! это важно!) означает прототип объекта.
Ложный вывод. Свойство prototype имеет огромный смысл в объекте, являющимся конструктором, в объекте (не конструкторе) его нет, как и смысла тоже. Служебного свойства constructor у объектов тоже нет (кроме самих объектов-прототипов). Выражение obj.constructor.prototype при переопределении свойств obj может означать что угодно.
Quote:
Так вот, после создания объекта интерпретатор присваивает его свойству constructor значение, равное величине, стоящей справа от оператора new. Таким образом, vehicle.constructor == self.Car, а obj.constructor в последнем примере вообще ссылается на функцию, не имеющую отдельного имени в глобальной области видимости (анонимную). Это настолько важно, что я приведу еще один поясняющий пример...
Это совершенно не так. Интерпретатор не присваивает новому объекту свойства constructor, у объекта вообще нет такого собственного свойства. Конструктор только связывает вновь созданный объект с объектом-прототипом, в результате чего неявная ссылка объекта (свойство [[Prototype]]) и явная ссылка конструктора (свойство Prototype) указывают на один и тот же объект. Это может быть как объект, созданный при создании самого конструктора (обычное поведение), так и другой объект (переопределённый), не важно.
Quote:
Итак, вывод: прототипы объектов доступны по цепочке obj.constructor.prototype.constructor.prototype..., а не obj.prototype.prototype, как можно понять из многих руководств по JavaScript в Интернете.
Ещё один ложный вывод. Цепочка obj.constructor.prototype.constructor.prototype (можно продлить её ещё хоть на 100 пар вперёд)- это не цепочка, а кольцевая ссылка на один и тот же объект, а при явном определении свойств объекта или переопределении свойст прототипа может вообще вести куда угодно. Настоящая prototype chain - это цепь неявных ссылок на объекты-прототипы, если исходить из записи, принятой в ES для внутренних свойств, то это obj.[[Prototype]].[[Prototype]].[[Prototype]].
Back to top
View user's profile Send private message
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Fri Feb 03, 2006 12:20 pm (спустя 13 минут; написано за 3 минуты 27 секунд)
   Post subject:
Reply with quote

Андрей Сумин wrote:
Нет ожидаемого вызова Zaporojets.drive();
Хм, и правда - это неверное поведение. Попробую разобраться, как руки дойдут...
Zeroglif wrote:
Цепочка obj.constructor.prototype.constructor.prototype (можно продлить её ещё хоть на 100 пар вперёд)- это не цепочка, а кольцевая ссылка на один и тот же объект
Вы уверены? Если имеет место наследование B > A > Object, и obj=new B, то
obj.constructor == B
obj.constructor.prototype.constructor == A
obj.constructor.prototype.constructor.prototype.constructor == Object

Разве это не так? Где кольцевая цепочка?
Zeroglif wrote:
Оператор new ничего не создаёт на то есть конструкторы. Основная задача оператора new - вызвать внутренний метод (Construct) объекта, предварительно проверив, что справа от него стоит именно объект, и именно имеющий этот самый метод. Далее в рамках уже самого "процесса конструирования" объектом-конструктором создаётся новый объект, он передаётся значением в this... и т.д. и т.п.
Возможно, с точки зрения внутренней архитектуры это и так. Но с точки зрения семантики языка - в момент входа в конструктор мы уже имеем в this пустой хэш. Поэтому я и пишу, что "new создает новый пустой хэш".
Zeroglif wrote:
Согласно ES с каждым конструктором ассоциирован прототип,а каждый созданный этим конструктором объект неявно ссылается на прототип, ассоциированный с конструктором.
...но добраться до этого прототипа синтаксически возможно только через constructor, верно? И переприсвоив свойство constructor, мы также и прототип тоже сменим. Так что, мне кажется, с точки зрения семантики языка имеет смысл все же говорить "с каждым объектом связан конструктор, с каждым конструктором связан прототип".
Back to top
View user's profile Send private message Send e-mail
Zeroglif
Участник форума



Joined: 02 Jan 2006
Posts: 293
Карма: 61
   поощрить/наказать


PostPosted: Fri Feb 03, 2006 12:51 pm (спустя 31 минуту; написано за 3 минуты 50 секунд)
   Post subject:
Reply with quote

Дмитрий Котеров wrote:
obj.constructor == B
obj.constructor.prototype.constructor == A
obj.constructor.prototype.constructor.prototype.constructor == Object
true
false
false

При сравнении только с B всё будет в true.
Дмитрий Котеров wrote:
Возможно, с точки зрения внутренней архитектуры это и так. Но с точки зрения семантики языка - в момент входа в конструктор мы уже имеем в this пустой хэш. Поэтому я и пишу, что "new создает новый пустой хэш".
В общем смысле это верно, но немного художественно, но верно ;-)
Дмитрий Котеров wrote:
...но добраться до этого прототипа синтаксически возможно только через constructor, верно? И переприсвоив свойство constructor, мы также и прототип тоже сменим. Так что, мне кажется, с точки зрения семантики языка имеет смысл все же говорить "с каждым объектом связан конструктор, с каждым конструктором связан прототип".
Не верно. Мы можем явно определить свойство constructor у объекта (повторяю, в явном виде его у объекта нет!), от этого он не потеряет неявную связь с прототипом (прототип не сменится), ассоциированным с его конструктором.
Back to top
View user's profile Send private message
Андрей Сумин
Участник форума



Joined: 23 Nov 2005
Posts: 60
Карма: 5
   поощрить/наказать

Location: Москва

PostPosted: Fri Feb 03, 2006 12:59 pm (спустя 8 минут; написано за 1 минуту 19 секунд)
   Post subject:
Reply with quote

Quote:
При сравнении только с B всё будет в true.
Я думаю именно по этой причине приведенный мною код не работает ижидаемым с точки зрения ОПП образом.
Back to top
View user's profile Send private message
Дмитрий Котеров
Администратор



Joined: 10 Mar 2003
Posts: 13665
Карма: 412
   поощрить/наказать


PostPosted: Fri Feb 03, 2006 1:09 pm (спустя 10 минут; написано за 50 секунд)
   Post subject:
Reply with quote

В таком случае, я вообще что-либо перестал понимать.
Code (JavaScript): скопировать код в буфер обмена
function A() {}

function B() {}
B.prototype = new A();

obj = new B();

alert(obj.constructor);
Выводит "function A() {}". Спрашивается, почему?
Если убрать "B.prototype = new A()", то выводит B, как и положено.
Back to top
View user's profile Send private message Send e-mail
Zeroglif
Участник форума



Joined: 02 Jan 2006
Posts: 293
Карма: 61
   поощрить/наказать


PostPosted: Fri Feb 03, 2006 3:22 pm (спустя 2 часа 12 минут; написано за 3 минуты 23 секунды)
   Post subject:
Reply with quote

Дмитрий Котеров wrote:
Выводит "function A() {}". Спрашивается, почему?
Сейчас попробую написать почему. Расскажу то, как я это себе представляю, не претендуя на истину и не используя слова-паразиты, вроде "класс", "суперкласс" и т.п. Итак, бермудский треугольник "наследования на базе прототипов" лежит между тремя островами:

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

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

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

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

Надо понимать, что свойство constructor появляется при создании объекта-прототипа и не имеет н-и-к-а-к-о-г-о отношения к объекту-экземпляру, его (экземпляра) ещё даже нет в природе. Таким образом, в итоге мы должны получить:
Code (JavaScript): скопировать код в буфер обмена
  A                       //
  A.prototype             //
  A.prototype.consructor  //
 
Теперь запустим сам конструктор и создадим новый объект-экземпляр a с использованием оператора new и конструктора A:
Code (JavaScript): скопировать код в буфер обмена
a = new A()
Что теперь присходит? Интерпретатор доходит до оператора new, проверяет стоит ли справа от него объект, имеет ли этот объект внутреннее свойство [[Construct]] (a он имеет, так как при создании A это поле было заведено) и даёт условную команду "Создать/сконструировать новый объект-экземпляр":

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

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

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

Встаёт вопрос, есть ли явная связь между экземпляром и конструктором или прототипом. Ответ - есть, но эта связь сама по себе уже основана на "наследовании на базе прототипов". Логика такого наследования предполагает, что интерпретатор ищет свойство объекта по цепочке прототипов, если не находит его сразу же. Именно это он и делает в данном случае, пытаясь отловить свойство constructor, т.к. такого поля в объекте-экземпляре a просто нет:
Code (JavaScript): скопировать код в буфер обмена
  a.constructor                //
  a.[[Prototype]].constructor  //
 
Отсюда должно быть понятно, что:
Code (JavaScript): скопировать код в буфер обмена
  A == a.constructor // true
  A.prototype == a.constructor.prototype // true
  A.prototype.constructor == a.constructor.prototype.constructor // true
 
Соответственно, если мы заведём в объекте-экземпляре своё собственное поле с именем constructor, то связь с объектом-прототипом не прервётся, но достать его или объект-конструктор через это свойство уже будет нельзя, т.к. поиск по цепочке прототипов не начнётся (имя-то будет сразу найдено).

Изменим пример и добавим ещё один объект-конструктор с тем, чтобы переопределить прототип. Под "переопределить прототип" я имею в виду навести ссылку A.prototype на другой объект.
Code (JavaScript): скопировать код в буфер обмена
  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. Иначе говоря:
Code (JavaScript): скопировать код в буфер обмена
  /*
Вот как бы вот так, очень надеюсь, что всё более-менее понятно... ибо чукча - не писатель, чукча - читатель ;-)

Last edited by Zeroglif on Fri Feb 03, 2006 8:16 pm; edited 1 time in total
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic All times are GMT + 3 Hours
Goto page 1, 2, 3, 4, 5, 6  Next
Page 1 of 6    Email to a Friend.
Post a reply
Username
Subject
Господа спамеры и оптимизаторы!

Вы можете даже и не пытаться вставлять в текст поста ссылки - они все равно автоматически удаляются (вернее, тэги <a> заменяются на тэги <u>).

Но если не поверите и все же попытаетесь - как только увидите, что все безрезультатно, удалите свой пост, пожалуйста. Модераторы тоже люди, нехорошо, если они погрязнут в тоннах спама.
     

Disable BBCode in this post
Disable Smilies in this post
    HTML is OFF
BBCode is ON
Smilies are ON
You cannot post new topics in this forum. You can reply to topics in this forum. You cannot edit your posts in this forum. You cannot delete your posts in this forum. You cannot vote in polls in this forum. You cannot attach files in this forum. You can download files in this forum.
XML