Беларусь, рогачёв |
Классы
Статический метод
Аппетит приходит во время еды; теперь мы можем вспомнить о том, что в C++ и Java бывают также и статические методы. Им разрешен доступ только к статическим полям и методам класса, но вызывать их можно как через класс, так и через объект. Раз уж поля мы сделали статическими, никто не мешает сделать статическими и методы. К статическим свойствам эти методы будут обращаться через класс. Более того, и вызваны эти методы должны быть как методы класса (то есть как если бы они были полями объекта -функции конструктора ). Тогда this в этих методах будет указывать на класс, а не на конкретный объект. Иначе статические методы получат доступ и к обычным (не статическим) полям и методам, чего мы как раз хотим избежать. Таким образом, для заводимых в прототипе ссылок на нужные нам статические методы мы должны будем где-то сохранить ссылку на класс (то есть его конструктор ). Далее мы будем следовать методике Тимоти Гролео и хранить все нужные вещи в контексте вызова функции addStaticMethod, которая приведена ниже.
// Для тестирования нам надо добавлять не только статические // методы, но и свойства. Здесь используем более простой // вариант кода, без пользовательских getter'ов setter'ов Function.prototype.addStaticProperty = function(name, propVal){ // Храним значение прямо в контексте вызова // addStaticProperty, прямо в аргументе ее. var getter = function() { return propVal; } var setter = function(newVal) { propVal = newVal; } // Как сам конструктор, так и прототип должны получить // это свойство (ссылающееся на одни и те же данные). this.addProperty(name, getter, setter); this.prototype.addProperty(name, getter, setter); } // Прячем метод от for...in, наподобие системных методов // В принципе, этого можно и не делать. ASSetPropFlags(Function.prototype, "addStaticProperty", 1); // Простейший вариант добавления статического метода Function.prototype.addStaticMethod = function(name, staticMethodEngene) { var theClass = this; var staticMethod = function() { return staticMethodEngene.apply(theClass, arguments); }; this[name] = staticMethod; this.prototype[name] = staticMethod; } // Прячем метод от for...in, наподобие системных методов // В принципе, этого можно и не делать. ASSetPropFlags(Function.prototype, "addStaticMethod", 1); // Тестируем статическое свойство _global.SomeClass = function(){} SomeClass.addStaticProperty("testProp", "Тестовое значение"); a = new SomeClass(); b = new SomeClass(); trace(a.testProp); SomeClass.testProp = "Второе тестовое значение"; trace(b.testProp) b.testProp = "Третье тестовое значение"; trace(SomeClass.testProp); // Тестируем статический метод b.someStr = "Некоторая строка"; f = function(){ trace("this.testProp.toLowerCase = " + this.testProp.toLowerCase()); trace("this.someStr = " + this.someStr); } SomeClass.addStaticMethod("printTest", f); trace(""); trace("--- b.printTest() ----"); b.printTest(); trace("--------------"); trace(""); trace("--- SomeClass.printTest() ----"); SomeClass.printTest(); trace("------------------"); trace(""); trace("b.someStr = " + b.someStr);
Выводит этот код, как и ожидалось,
Тестовое значение Второе тестовое значение Третье тестовое значение --- b.printTest() ---- this.testProp.toLowerCase = третье тестовое значение this.someStr = -------------- --- SomeClass.printTest() ---- this.testProp.toLowerCase = третье тестовое значение this.someStr = ------------------ b.someStr = Некоторая строка
Вы видите, что к полю someStr у нашего статического метода нет доступа. В то же время статическое свойство testProp ему замечательно видно.
Для более глубокого понимания происходящих здесь вещей попробуйте угадать, что выведет вызов b.printTest() в том случае, когда мы добавим f не с помощью addStaticMethod, а с помощью addStaticProperty. Для того чтобы побольше запутать вас, приведем сначала неправильный способ рассуждений. Он состоит в том, что getter возвращает объект -функцию, который хранится в контексте вызова addStaticProperty. Тогда this указывает на этот контекст, в котором отсутствуют как testProp, так и someStr. Таким образом, вместо их значений мы получим пустоту. Следующая мысль, которая приходит в голову, - это то, что getter, возвращая ссылку на объект -функцию, сохраняет ее в неком временном хранилище, к которому и будет относиться this. (Для сравнения загляните в лекцию 5, параграф "Функция как объект " и еще раз просмотрите пример, иллюстрирующий использование возвращаемых значений типа Function.) Вывод тот же самый - мы увидим пустое место вместо значений строк. Однако запустите наш код, заменив вызов addStaticMethod на addStaticProperty. И вы увидите, что будет выведен, в частности, следующий фрагмент:
--- b.printTest() ---- this.testProp.toLowerCase = третье тестовое значение this.someStr = Некоторая строка --------------
Правильное же решение, которое полностью избавляет от всех сомнений, состоит в том, что Flash MX делает поведение свойств максимально похожим на поведение обычных полей. Так что раз уж мы написали b.printTest(), то в функцию printTest в качестве this будет передана именно ссылка на b. А то, что ссылка на printTest хранится где-то в другом месте (не в b ) и нам ее возвращает getter - это нас волновать уже не должно.