![]() |
||||||
|---|---|---|---|---|---|---|
Наследование во Flash MX Мы уже познакомились с процессом создания объектов во Флэш МХ и теперь знаем, что любой объект легко поддается модификации. Но ведь прототипом объекта всегда тоже является какой-то объект! Значит и прототип можно модифицировать. К чему это приводит, мы сейчас увидим. "вручную". Предположим однако, что мы написали возникает структура, подобная изображенной. что все условия, необходимые для наследования, в данном случае выполнены. значение этого поля? Сначала, разумеется, он посмотрит среди полей, которые мы непосредственно завели в объекте obj. Если ничего не найдено, то, как мы знаем, Флэш принимается за поиски в прототипе, который он находит с помощью ссылки __proto__. Но поиски в прототипе идут по той же самой схеме, то есть если непосре дственно в прототипе искомая ссылка не нашлась, то мы идем в прототип прототипа (относительно исходного объекта это __proto__.__proto__) и так далее вплоть до прототипа типа Object (который автоматически попадает в "корень" этой цепочки, даже если у самого базового класса прототип был вовсе не указан). Очевидно, что такой механизм полностью соответствует наследованию в обычном понимании этого слова. Более т ого, если в производном классе создана какая-то функция с тем же именем, что и в базовом, то при поиске по цепочке прототипов будет сначала найдена функция из производного класса (и на этом поиск прекратится). То есть таким образом реализовано переопределение виртуальных функций. Мы видим, что все функции являются виртуальными. Более того, виртуальными являются также и поля. (Временами случается пожалеть об отсутствии возможности делать виртуальные поля при работе на С++ или Java.) где-то в цепочке __proto__? Оказывается, можно, хотя и приходится для этого использовать недокументированную функцию Object.hasOwnProperty. Вот код, который демонстрирует все, что нам нужно. типа constr. Собственно говоря, тут еще не было наследования как такового, ибо prot - это единичный объект, не описывающий тип. Если бы мы непременно хотели устроить наследование, мы могли бы сделать этот объект прототипом некоего класса Base, а затем объект этого класса использовать в качестве прототипа класса constr. Впрочем, мы пока что иллюстрируем не наследование, а цепочку поиска. Поэтому мы нарочно подчеркнули в названии constr тот факт, что мы используем эту функцию в качестве конструктора, а то, что э та функция-объект задает класс, нам не очень важно. В конце концов, мы могли обойтись вовсе без конструктора, а написать obj.__proto__ = prot. Можете убедиться, что этот вариант тоже работает; более подробно мы его разберем, когда будем говорить об альтернативном наследовании (в предпоследнем параграфе этой лекции). Мы видим, что функция Object.hasOwnProperty работает так, как ожидалось. Заодно убеждаемся в том, что уничтожить поле и присвоить ему значение undefined - это разные вещи. В последнем случае значения из прототипа скрываются свежеприсвоенным значением undefined.
Вывод полного содержимого объектаВ этом небольшом разделе мы поговорим о том, какие есть удобные способы вывести полную таблицу полей и методов объекта - таблицу, в которой все переменные и функции рассортированы по принадлежности к одному из прототипов в цепочке __proto__. Вот код, который сию замечательную методику реализует. // Печатаем имя и значение поля, но не смешиваем // пустую строку и undefined _global.printField = function(name, value){
if (value == undefined) value = "undefined"; trace(name + ": " + value) } // Печатаем все, что доступно функции for...in // Чтобы увидеть скрытые поля, снимаем с них защиту // Если не предпринять никаких мер, эта функция // выведет как непосредственные поля и методы объекта, // так и открытое содержимое его прототипов всех уровней. _global.printFieldsByForIn = function(obj, str, tempProto){
trace("::::::::::: " + str + " ::::::::::::");
trace(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
// Снимаем "защиту" со скрытых полей. ASSetPropFlags(obj, null, 0, 1); for(var name in obj){
// Принимаем меры для того, чтобы наши действия // с обнулением __proto__ не отражались на выводимой // информации. if (name == "__proto__") printField(name, tempProto); else printField(name, obj[name]); } trace(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::");
} // В этой рекурсивной функции мы, собственно, и реализуем // вышеописанный фокус с "отцеплением цепочки" __proto__ . _global.printAllFields = function(obj, name){
var tempProto = obj.__proto__; obj.__proto__ = null; printFieldsByForIn(obj, name, tempProto); obj.__proto__ = tempProto; // Проверка на null не нужна: null == undefined, хотя // отличить их и можно при помощи оператора ===. if (obj.__proto__ != undefined) printAllFields(obj.__proto__, name + ".__proto__"); } // А эта функция просто вызывает основную рабочую функцию и // добавляет "элементы оформления" (в текстовом виде, разумеется). _global.dumpObj = function(obj, name){
trace("================================================");
if (name == undefined) name = "<Dumped object>"; printAllFields(obj, name); trace("================================================");
trace("");
} // Теперь тестируем функцию dumpObj на разных объектах. // Сначала на простейшем объекте с одним полем. myObject = new Object(); myObject.a = 10; dumpObj(myObject, "myObject"); // Затем тестируем на массиве. someArray = [1, 2, 4, 7]; dumpObj(someArray, "someArray"); // Далее тестируем на примитивной сроке. a = "Примитивная строка"; dumpObj(a, "Примитивная строка"); // И, наконец, на системном объекте _root, который // представляет из себя корневой MovieClip. dumpObj(_root, "_root"); Выводит этот код очень большое количество текста, но текст этот того стоит - ведь это внутренности разных системных объектов, что может быть интереснее? Вот что выводится в консоль: ========================================================== ::::::::::: myObject :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: a: 10 __constructor__: [type Function] constructor: [type Function] __proto__: [object Object] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: myObject.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __proto__: undefined toLocaleString: [type Function] isPropertyEnumerable: [type Function] isPrototypeOf: [type Function] hasOwnProperty: [type Function] toString: [type Function] valueOf: [type Function] addProperty: [type Function] unwatch: [type Function] watch: [type Function] constructor: [type Function] ========================================================== ::::::::::: someArray :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 3: 7 2: 4 1: 2 0: 1 __proto__: constructor: [type Function] length: 4 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: someArray.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: sortOn: [type Function] reverse: [type Function] sort: [type Function] toString: [type Function] splice: [type Function] join: [type Function] slice: [type Function] unshift: [type Function] shift: [type Function] concat: [type Function] pop: [type Function] push: [type Function] __proto__: [object Object] constructor: [type Function] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: someArray.__proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __proto__: undefined toLocaleString: [type Function] isPropertyEnumerable: [type Function] isPrototypeOf: [type Function] hasOwnProperty: [type Function] toString: [type Function] valueOf: [type Function] addProperty: [type Function] unwatch: [type Function] watch: [type Function] constructor: [type Function] ========================================================== ::::::::::: Примитивная строка :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: Примитивная строка.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: substr: [type Function] split: [type Function] substring: [type Function] slice: [type Function] lastIndexOf: [type Function] indexOf: [type Function] concat: [type Function] charCodeAt: [type Function] charAt: [type Function] toLowerCase: [type Function] toUpperCase: [type Function] toString: [type Function] valueOf: [type Function] __proto__: [object Object] constructor: [type Function] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: Примитивная строка.__proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __proto__: undefined toLocaleString: [type Function] isPropertyEnumerable: [type Function] isPrototypeOf: [type Function] hasOwnProperty: [type Function] toString: [type Function] valueOf: [type Function] addProperty: [type Function] unwatch: [type Function] watch: [type Function] constructor: [type Function] ========================================================== ::::::::::: _root :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: toLocaleString: [type Function] isPropertyEnumerable: [type Function] isPrototypeOf: [type Function] hasOwnProperty: [type Function] toString: [type Function] valueOf: [type Function] addProperty: [type Function] unwatch: [type Function] watch: [type Function] a: Примитивная строка someArray: 1,2,4,7 myObject: [object Object] __proto__: [object Object] constructor: [type Function] $appPath: file:///K|/Program%20Files/Macromedia/Flash%20MX/ $version: WIN 6,0,21,0 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: _root.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: createTextField: [type Function] clear: [type Function] endFill: [type Function] lineStyle: [type Function] curveTo: [type Function] lineTo: [type Function] moveTo: [type Function] beginGradientFill: [type Function] beginFill: [type Function] createEmptyMovieClip: [type Function] stopDrag: [type Function] startDrag: [type Function] removeMovieClip: [type Function] duplicateMovieClip: [type Function] gotoAndStop: [type Function] gotoAndPlay: [type Function] prevFrame: [type Function] nextFrame: [type Function] stop: [type Function] play: [type Function] setMask: [type Function] getDepth: [type Function] attachVideo: [type Function] attachAudio: [type Function] getBytesLoaded: [type Function] getBytesTotal: [type Function] getBounds: [type Function] hitTest: [type Function] globalToLocal: [type Function] localToGlobal: [type Function] swapDepths: [type Function] attachMovie: [type Function] loadMovie: [type Function] loadVariables: [type Function] unloadMovie: [type Function] getURL: [type Function] meth: [type Function] tabIndex: undefined enabled: true useHandCursor: true __proto__: [object Object] constructor: [type Function] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::: _root.__proto__.__proto__ :::::::::::: ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: __proto__: undefined toLocaleString: [type Function] isPropertyEnumerable: [type Function] isPrototypeOf: [type Function] hasOwnProperty: [type Function] toString: [type Function] valueOf: [type Function] addProperty: [type Function] unwatch: [type Function] watch: [type Function] constructor: [type Function] ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Предоставив вам самостоятельно разглядывать разнообразные недокументированные функции, названия которых у нас сейчас распечатались (наряду со всем остальным), мы дадим все же нес колько комментариев. Во-первых, разумеется, необязательно было снимать защиту со всех полей и методов. Если вы будете использовать подобную функцию для распечатки содержимого собственных классов, мы рекомендуем вам закомментировать соответствующую строчку. ot - в него все равно попали методы из класса Object. (Почему только из Object? Дело в том, что непосредственный прототип объекта _root - это MovieClip, но снять защиту с его методов мы к моменту вызова dumpObj для _root еще не успели. А с Object защита уже была снята.) В остальных случаях такого не произошло. Отсюда вывод: все дело в том, что _root - слишком важный системный объект, чтобы позволять его поведению зависеть от того, что лежит у него в __proto__. (А, может, дело в том, как именно было удобнее всего реализовать корневой клип.) Во всяком случае, о том, что за объект является прототипом клипа _root, система Flash MX догадывается без обращения к полю _root.__proto__ и поэтому изменение его значения ничего не дает. О том, что это п оведение не является общим для других клипов, можно узнать, вызвав функцию dumpObj для произвольного клипа. Для этого можно в конец вышеприведенного кода вставить такие строчки: _root.createEmptyMovieClip("newClip", 1);
dumpObj(newClip, "mc"); Только что раскрытые функции Object и MovieClip в секции, где выводятся собственные поля и методы клипа newClip, не обнаруживаются. Доступ к базовому классу: ключевое слово superВы уже знаете два способа обратиться из производного класса к базовому. Первый - с помощью apply или call (при этом используется явное имя класса и ссылка prototype). Можно также добраться до нужных функций прямо из объекта через this.__proto__.__proto__ (а потом опять использовать apply). Этот способ хуже, поскольку при вызове функции, содержащей такой код, из классов, производных от нашего, значение ссылки this.__proto__.__proto__ будет другим. рассматриваемой задачи. Этот механизм программистам, применяющим в своей работе язык Java, прекрасно известен. Основан он на использовании ключевого слова super. С помощью этого ключевого слова можно вызвать конструктор базового класса - пишем super(arg1, arg2, ...). Список аргументов ничем не отличается от списка аргументов любой другой функции. При этом конструктор вызывается не как обычная функция (в которой this означает объект, в котором лежит ссылка на нее), а именно как конструктор (this указывает на вновь созданный объект). Если же нужно обратиться к какому-либо полю или методу базового класса, используется конструкция вида super.a или super .func() (что, как правило, применяется при вызове базового варианта переопределяемой виртуальной функции). значение в полиморфных классах. Ведь super, использованный в методе некоторого класса, всегда будет означать ссылку на его непосредственного предка, даже когда вызов произошел из далекого производного класса (в методах которого super имеет совсем другое значение). Вы можете представлять себе это так: компилятор Флэш, видя в коде ключевое слово super, вставляет на его место нечто вроде ссылки непосредственно на базовый класс (его прототип). В случае вызова конструктора эта псевдоссылка будет указывать на сам конструктор базового класса. Когда нужно применять явное указание базового классаМы теперь знаем, что обратиться к методу базового класса можно, используя ключевое слово super. Но в длинных иерархиях иногда полезно вызвать метод суперкласса более высокого уровня (так что наш класс является его весьма дальним наследником). Как это сделать? Цепочки типа super.super не только неудобны - они еще и не работают, поскольку super не является полем. Вот пример, доказывающий это. superclass = function(){}
superclass.prototype = new Object(); superclass.prototype.say = function(text){trace(text +
"superclass")} subclass1 = function(){super.say();}
subclass1.prototype = new superclass(); subclass1.prototype.say = function(text){trace(text +
"subclass1")} subclass2 = function(){super.say();}
subclass2.prototype = new subclass1(); subclass2.prototype.say = function(text){trace(text +
"subclass2")} subclass3 = function(){super.say();}
subclass3.prototype = new subclass2(); subclass3.prototype.say = function(text){trace(text +
"subclass3")} subclass3.prototype.say2 = function(text){super.say(text);}
subclass3.prototype.say3 = function(text){super.super.say(text);}
subclass3.prototype.say4 = function(text){super.super.super.say(text);}
a = new subclass3(); trace("---------------");
a.say("type = ");
a.say2("type = ");
a.say3("type = ");
a.say4("type = ");
Эта программа выводит следующее: superclass subclass1 subclass2 --------------- type = subclass3 type = subclass2 Мы видим, что все цепочки типа super.super не сработали. Что же делать? В С++ в таких случаях можно было явно указать имя базового класса. Во ФлэшМХ такого механизма нет, но мы знаем, что средствами Флэш разрешима более общая задача - можно вызывать функцию, методом класса вовсе не являющуюся так, как если бы она этим методом была. Как вы, наверное, помните, для этого используются методы класса Function по имени call и apply. В случае, когда мы переопределяем функцию базового класса, бывает удобнее пользоваться методом apply, где аргументы не перечисляются друг за другом, а передаются целиком в объекте arguments.В примере, приведенном в начале параграфа, описание к ласса subclass3 можно сделать таким образом: subclass3 = function(){super.say();}
subclass3.prototype = new subclass2(); subclass3.prototype.say = function(text){trace(text +
"subclass3")} subclass3.prototype.say2 = function(text){subclass2.
prototype.say.apply(this, arguments);} subclass3.prototype.say3 = function(text){subclass1.
prototype.say.apply(this, arguments);} subclass3.prototype.say4 = function(text){superclass.
prototype.say.apply(this, arguments);} На сей раз вывод программы будет таким: superclass subclass1 subclass2 --------------- type = subclass3 type = subclass2 type = subclass1 type = superclass То есть теперь все работает как надо. они почему-то не были вызваны своими непосредственными потомками) вам тоже придется производить, пользуясь вышеописанным способом. "Лишние" вызовы конструкторовПри анализе двух предыдущих примеров вы могли заметить, что на печать выводится нескольком начале. Можно ли обойтись без вызова конструкторов при наследовании? Оказывается, есть обходные пути. Мы обсудим их в параграфе, посвященном альтернативному наследованию и "альянсу мятежников". Проверка типаВ некоторых случаях бывает необходимо проверить, является ли данный объект наследником некоторого класса. Хотя в грамотно спроектированной иерархии классов подобная надобность возникает нечасто. Но иногда вы пользуетесь чужими классами, менять которые нет ни желания, ни необходимости. Тогда на помощь приходит оператор instanceof. Пример: if (arg instanceof MovieClip) arg.x = 10; способ - попробовать завести у объекта данного типа поля). Рассмотрим такой код: a = new Number(5); b = 5; a.x = 3; b.x = 4; trace(a instanceof Number); trace(b instanceof Number); trace(a.x); trace(b.x); То есть мы производим одни и те же действия над объектами а и b, причем первый из них - объектного типа, а второй - примитивного. А вот и результат: true false 3 undefined Мы видим, что объект b к типу Number не принадлежит и попытка завести у него поля хотя и не приводит к ошибке, но и результата никакого не дает. мы можем проверить, на что указывает поле __proto__ нашего объекта. В случае Number оно указывает на Number.prototype, в противном случае его значением будет undefined. Эти рассуждения наводят на мысль о том, что функцию, подобную instanceof, можно сделать самостоятельно. И действительно, в онлайн-документации Флэш МХ приведен следующий пример (мы меняем только название функции, чтобы оно не совпадало с ключевым словом): function emulated_instanceof (theObject, theClass){
while ((theObject = theObject.__proto__) != null) {
if (theObject == theClass.prototype) {
return true; } } return false; } Давайте разберем этот пример подробнее, поскольку на нем вполне можно проверить, прави следует проверить не сам текущий объект, а уже его прототип, и так далее, пока цепочка не приведет нас к Object'у. Таким образом, рассматриваемая функция действительно полностью идентична оператору instanceof. Изменение базовых классовПрототипная модель наследования (вкупе с интерпретируемым языком) имеет одно неожиданное свойство. Мы можем вносить какие-то изменения в базовые классы уже после того, как объекты производных классов созданы и используются. После того, как эти изменения будут внесены, поведение всех объектов производных классов сразу же поменяется. Такую интересную возможность грех не использовать. И сейчас мы покажем, как это делается. (Хотя самые яркие примеры вы увидите позже в лекции об эмуляции множественного наследования). Пример изменения работы иерархииВот совсем простой пример изменения работы иерархии классов. Мы создаем два класса (один - наследник другого). И заводим в базовом классе функцию, которая выводит в консоль все поля объекта. Затем, испробовав, как эта функция работает, заменяем ее другой. И снова смотрим, каков результат ее вызова из объекта базового и производного классов. Вот код, который реализует эти простые идеи. _global.Base = function(a, b){
this.a = a; this.b = b; } _global.Base.prototype.printAll = function(){
for (var name in this){
trace("this." + name + " = " + this[name]);
} } _global.Derived = function(a, b, c, d){
super(a, b); this.c = c; this.d = d; } _global.Derived.prototype = new Base(); b = new Base(10, 20); d = new Derived(15, 25, 35, 45); trace("================ b ===============");
b.printAll(); trace("==================================");
trace("================ d ===============");
d.printAll(); trace("==================================");
_global.Base.prototype.printAll = function(){
trace("Function is obsolete!");
} trace("================ b ===============");
b.printAll(); trace("==================================");
trace("================ d ===============");
d.printAll(); trace("==================================");
На выходе получаем: ================ b =============== this.printAll = [type Function] this.b = 20 this.a = 10 ================================== ================ d =============== this.printAll = [type Function] this.d = 45 this.c = 35 this.b = 25 this.a = 15 ================================== ================ b =============== Function is obsolete! ================================== ================ d =============== Function is obsolete! ================================== Функция действительно заменилась - как в базовом классе, так и в производном. В следующем подпараграфе мы приведем уже более близкие к практике примеры использования этой методики.
Альтернативное наследование ("альянс мятежников")Мы познакомились с тем интересным способом (называемым "прототипным наследованием"), с помощью которого делается наследование во Флэш МХ. Однако вы уже, видимо, заметили в этом способе некоторое неудобство. Состоит оно в том, что при создании объекта базового класса, записываемого в прототип класса производного, неминуемо вызывается конструктор. Сейчас мы приведем слегка модифицированный (и укороченный) пример наследования, уже встречавшийся в этой лекции, и затем попытаемся его исправить. superclass = function(){
trace("constructor of superclass");
} superclass.prototype.say = function(text){trace(text +
"superclass")} subclass1 = function(){
super(); super.say(); } // Вот обычное наследование subclass1.prototype = new superclass(); subclass1.prototype.say = function(text){trace(text +
"subclass1")} a = new subclass1(); trace("---------------");
a.say("type = ");
Запустим это код и получим: constructor of superclass constructor of superclass superclass --------------- type = subclass1 Мы еще раз убедились, что обычное наследование неудобно из-за двойного вызова конструкторов. Но есть ряд вещей, которые нам помогут избежать этого! Во-первых, учтем, что при создании функции сразу же создается дополнительный пустой объект, на который указывает ссылка prototype. То есть при создании самого базового класса вовсе необязательно писать superclass.prototype = new Object() (и этим фактом мы регулярно пользовались). Во-вторых, нам надо добиться, чтобы объект, на который указывает prototype, воспринимался как объект определенного класса (базового); этот объект мы будем затем модифицировать. Мы знаем, что для этого нужна запись наподобие subclass1.prototype.__proto__ = superclass.prototype. Наконец, мы говорили, что выполнение оператора new не только создает новый объект и устанавливает ему свойство __proto__, но и инициализирует новому объекту еще два свойства по имени constructor и __constructor__. Свойство constructor нам сейчас не понадобится, а вот без __constructor__ не работает вызов базового конструктора через super(). Соответственно, именно на конструктор базового класса и должен указывать __constructor__. Итак, модифицируем вышеприведенный код следующим образом: superclass = function(){
trace("constructor of superclass");
} superclass.prototype.say = function(text){trace(text +
"superclass")} subclass1 = function(){
super(); super.say(); } // Следующие две строки - это и есть альтернативное наследование subclass1.prototype.__proto__ = superclass.prototype; subclass1.prototype.__constructor__ = superclass; subclass1.prototype.say = function(text){trace(text +
"subclass1")} a = new subclass1(); trace("---------------");
a.say("type = ");
Запускаем этот код и получаем: constructor of superclass superclass --------------- type = subclass1 Этот способ наследования действительно помог разрешить нам проблему двойного вызова конструкторов. Если он вам понравился, вы, возможно, захотите записывать наследование при помощи одного оператора вместо двух. В таком случае можете сделать специальную функцию (мы по традиции, принятой в среди сторонников такого наследования, называем ее cExtends, что является сокращением от custom extends), которая будет делать то, что мы написали выше. Эту функцию можно положить прямо в прототип класса Function, чтобы его можно было вызвать из любого объекта-функции. Вот как это делается: Function.prototype.cExtends = function(base){
this.prototype.__proto__ = base.prototype; this.prototype.__constructor__ = base; } superclass = function(){
trace("constructor of superclass");
} superclass.prototype.say = function(text){trace(text +
"superclass")} subclass1 = function(){
super(); super.say(); } // Сокращенная форма альтернативного наследования subclass1.cExtends(superclass); subclass1.prototype.say = function(text){trace(text +
"subclass1")} a = new subclass1(); trace("---------------");
a.say("type = ");
Запустив этот код, мы получим то же самое, что и в прошлый раз - функция cExtends замечательным образом сработала. Но нет предела совершенству - можно еще улучшить нашу замечательную функцию cExtends. Во-первых, неплохо бы сделать, чтобы поле __constructor__ было скрыто - так же, как и в том случае, когда его создает оператор new. Для этого надо в cExtends добавить строчку ASSetPropFlags(this.prototype, "__constructor__", 1); Для __proto__ ничего такого делать не надо - это свойство уже присутствует (и является скрытым) в prototype с самого начала. Во-вторых, вы, наверное, помните, что в прошлой лекции мы обсуждали, как сделать статические (принадлежащие целому классу) свойства. При этом свойства у нас хранились в полях функции-конструктора класса. Можно ли сделать эти статические свойства наследуемыми? Оказывается, да, и для этого функцию cExtends нужно модифицировать следующим образом: Function.prototype.cExtends = function(base){
this.prototype.__proto__ = base.prototype; this.prototype.__constructor__ = base; ASSetPropFlags(this.prototype, "__constructor__", 1); this.__proto__ = base; } В самом деле, после этой модификации функция-объект, для которой был вызван метод cExtends, становится как бы наследником функции-объекта, являющейся конструктором базового класса (и в полях которой лежат статические свойства). То есть для того, чтобы можно было наследовать статические свойства, мы связали отношением наследования не только прототипы, но и объекты-конструкторы!
|
||||||
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2009 г. |
||||||