XDR Class — реализация классов с аксессорами для IE8
Всем привет. Решил показать очередную имплементацию классов для JS, но с поддержкой геттеров и сеттеров с помощью Object.defineProperty, работающую в IE8. Идея очень проста: для нормальных браузеров использовать тривиальное наследование, которое можно найти в любом учебнике по JS, а для IE8 возвращать экземпляр XDomainRequest (или просто XDR), расшрияя нужными свойствами (то есть протоип менять не рекомендуется, список свойств должен быть статичным, как в работе devote).
Для тех, кто не в курсе, IE8 поддерживает Object.defineProperty для DOM объектов, XDR, в этом случае, наиболее привлекателен, так как его экземпляр имеет всего 7 свойств + 3 у прототипа. Object.defineProperty для IE8 поддерживается кое-как. Более подробно можно прочесть в этой теме: http://javascript.ru/forum/offtopic/...rty-v-ie8.html Поэтому я настойчиво рекомендую использовать в качестве элементов дескриптора только get и set. Документации никакой нет, но появится после успешного завершения работы над фреймворком (превью которого я покажу чуть позже). Код выложил в репозиторий: https://github.com/finom/xdr-class/b...r/xdr-class.js (для того, чтоб проверить, просто выкачайте репозиторий и запустите index.html, сюда и на jsFiddle вставить код не могу из-за политик безопасности в IE) Функция требует underscore (хотя его можно легко убрать). A = Class({ a1: 'Aa1', a2: 'Aa2', aMethod1: function(arg) { Object.defineProperty( this, 'x', { get: function() { return 'X Property' }, set: function( v ) { console.log( 'Message from "x" setter, value is: ', v ); } }); console.log( 'A.Method1 call, arg is: ', arg ) } }); B = Class({ 'extends': A, // кавычки обязательны (для подсветки и чтоб не вызвать ошибки в IE), но 'extends' можно заменить на extend без кавычек b1: 'Bb1', b2: 'Bb2', a2: 'Ba2', constructor: function() { console.log( 'B.constructor call' ); } }); C = Class({ 'extends': B, c1: 'Cc1', b1: 'Cb1', constructor: function() { this.aMethod1( 'BLAH' ); this.x = 'New X Value'; B.call( this, arguments ); console.log( 'Properties: ', this.c1, this.b1, this.b2, this.a1, this.a2 ); }, aMethod1: function() { console.log( 'C.method call' ); B.prototype.aMethod1.apply( this, arguments ); } }); new C; Из todo: добавить метод .instanceOf и что-то придумать с вызовом родительских методов (B.prototype.aMethod1.call( this ) немного надоел). |
Цитата:
|
Цитата:
|
Сделал небольшую пробу по памяти: перед запуском ИЕ отжирал 30 МБ, на пике съедал 120 МБ, после окончания работы скрипта таск менеджер показал 100МБ. То есть миллион инстанцов отжирают 70—90МБ. Да, опять же, это не глубокое тестирование:
console.log = function() {}; console.time( '1' ); for(var i = 0; i < 1000000; i++) new C; console.timeEnd('1'); Небольшой замер скорости: В Хроме 26195.000ms (просто для сравнения) В ИЕ 10 15683ms В ИЕ 8 112826ms (всего лишь в ~4 раза медленнее Хрома. Еще раз отмечу, в Хроме используется тривиальное наследование). |
Дальше можно делать всё, что душе угодно, например:
MainClass = Class({ define: function( key, descriptor ) { Object.defineProperty( this, key, descriptor ); } }); MyClass = Class({ 'extend': MainClass, constructor: function() { this.define( 'prop', { get: function() { return 'bla'; }, set: function(v) {alert( v )}} ); } }); |
Цитата:
а если собрать экземпляры в массив? |
почему нет стирания XDR свойств?
|
Цитата:
|
ну так через defineProperty не работает?
|
Цитата:
|
а, не думал, что там про это, тогда вопрос исчерпан ^^'
|
Запилил метод .instsanceOf
a.instanceOf( B );(думаю, можно не описывать, что он делает). Сделал небольшую замену для MyClass.prototype.method.apply( this, arguments ); Она не так хорошо выглядит, как у devote (я так понимаю, ты как-то декорировал методы?), зато реализация выглядит сильно проще. Моего ума пока не хватило сделать нормальный, не зацикливающийся parent (this.parent.method()). A = Class({ method: function() {} }); B = Class({ method: function() { B.parent.method( this ); } }); Чтобы вызвать метод родительского класса пишем имя текущего класса (указывать в методах название родительского класса — совсем некрасиво, но своего — уже лучше), свойство parent, имя метода, аргументы: 1. this - обязательно 2. arguments или любые аргументы Можно написать так: B.parent.method( this, arguments ); а можно и так: B.parent.method( this, 1, 2, 3, 4, 5 ); |
Цитата:
console.log = function() {}; x = {}; console.time( '1' ); for( var i = 0; i < 100000; i++ ) x[ i ] = new C; console.timeEnd( '1' )Миллион инстанцов сожрали около гига, сто тысяч сожрали, ожидаемо, 100 мегабайт. Не знаю, хорошо это или плохо, но совет очевиден: не создавайте больше 100 тысяч инстанцов, 100 мегабайт для осла не так уж и много :) |
Цитата:
|
FINoM, ну, лучше уж только перечисляться, чем еще и что-то возвращать
как тебе такой вариант? var B= Class(function( proto ){ proto.define( 'foo', function( ){ return 1 } ) proto.override( 'bar', function( return 2 } ) }) define - создаёт свойство, но бросает исключение, если такое уже есть override - перегружает свойство предка, но бросает исключение, если такого нет ну и расширять тут можно: defineGetter, overrideGetter defineSetter, overrideSetter и так далее |
Цитата:
MainClass = Class({ define: function() {/* ... */}, override: function() {/* ... */} /* ... */ }); A = Class({ 'extends': MainClass /* ... */ }); То же самое касается и этого: Цитата:
MainClass = Class({ initMainClass: function() { // можно перечислить все семь свойств и вернуть в геттере undefined, // а можно и перечислить всего три (которые не null) и вернуть null var badProps = [ 'contentType', 'timeout', 'responseText' ]; for(var i = 0; i < badProps.length; i++ ) { Object.defineProperty( this, badProps[ i ], { get: function() { return null; }}); } } }) |
не, ты не понял, у меня эти методы расширяют прототип, а не экземпляр
а пример, не о том, вот правильный: var A= Class(function( proto ){ proto.define( 'foo', function( ){ return 1 } ) }) var B= Class(function( proto ){ proto.mixin( A ) proto.override( 'foo', function( base, 1 ){ return base() + 1 } ) }) |
Цитата:
|
вот это:
proto.override( 'foo', function( base, 1 ){ return base() + 1 } ) перегруженный метод передаётся в качестве первого параметра |
Цитата:
|
именно
|
Часовой пояс GMT +3, время: 15:54. |