Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Как создать массив с особым прототипом? (https://javascript.ru/forum/misc/36783-kak-sozdat-massiv-s-osobym-prototipom.html)

danik.js 27.03.2013 12:42

Как создать массив с особым прототипом?
 
Нужно создать массив со своим кастомным прототипом.

Если в браузере доступно свойство __proto__, то можно сделать так:

function MyArray () {
    var instance = []
    instance.__proto__ = arguments.callee.prototype
    return instance
}
MyArray.prototype.myFunction = function () {}

var myArray = new MyArray()
alert( typeof myArray.myFunction )


А как сделать такое же без __proto__ ?

Aetae 27.03.2013 13:27

Мб так:
function MyArray () {}
MyArray.prototype = Array.prototype;
MyArray.prototype.myFunction = function () {
	return this[0] + this[1]
}
 
var myArray = new MyArray()
alert([ 
	typeof myArray.myFunction,
	myArray instanceof Array,
	typeof myArray
])

myArray.push(5)
myArray.push(3)
alert([
	myArray.length,
	myArray.myFunction()
])

...но зачем?

danik.js 27.03.2013 13:51

Aetae
function MyArray () {}
MyArray.prototype = Array.prototype;
MyArray.prototype.myFunction = function () {
    return this[0] + this[1]
}
  
var myArray = new MyArray()

// во-первых мы изменили прототип Array, но это решаемо
alert( typeof [].myFunction )

// во-вторых, мы получили таки объект, а не массив
alert( Array.isArray(myArray) )
alert( Object.prototype.toString.call(myArray) )


А должно быть так:
function MyArray () {
    var instance = []
    instance.__proto__ = arguments.callee.prototype
    return instance
}
MyArray.prototype.myFunction = function () {}
 
var myArray = new MyArray()

alert( typeof [].myFunction )
alert( Array.isArray(myArray) )
alert( Object.prototype.toString.call(myArray) )


Зачем это? Пытаюсь заставить библиотеку Zepto работать с IE. А там как раз использется массив, а не объект.
Да и вообще интересно - возможно ли так сделать без __proto__

rgl 27.03.2013 14:11

var MyArray = function() {
  var _myPrototype = {
    joinReverse : function(s) { return this.slice(0).reverse().join(s); }
  }
  return function() {
    var ret = Array.apply( null, arguments );
    for( var i in _myPrototype )
      // if( typeof _myPrototype[i] == "function" )
        ret[i] = _myPrototype[i];
    return ret;
  }
}();

var test = MyArray( 1, "bb", 333 );

alert( test.joinReverse( " - " ) );

Aetae 27.03.2013 14:15

Цитата:

Сообщение от danik.js (Сообщение 242926)
// во-первых мы изменили прототип Array, но это решаемо

Эт да, бывает)
Цитата:

Сообщение от danik.js (Сообщение 242926)
Зачем это? Пытаюсь заставить библиотеку Zepto работать с IE. А там как раз использется массив, а не объект.

Лол, Zepto прямо позиционируется как jquery без ie.) Хочешь поддержку ie - откатись на jquery. (Там так и написано на главной: "If you need to support Internet Explorer, you can fall back on jQuery.")
Цитата:

Сообщение от danik.js (Сообщение 242926)
Да и вообще интересно - возможно ли так сделать без __proto__

Можно сделать без __proto__ - используюя новомодные методы Object(которые в ie конечно не работают). =)
Ну или вручную навешивать на каждый создаваемый массив методы, что, естесно, скажется на производительности и будет вылезать в for in.

...upd: пока писал - последний вариант реализовал rgl.)

megaupload 27.03.2013 16:21

function MEGAArray (){};
MEGAArray.prototype = new Array;

MEGAArray.prototype.godOfJavascript = function(){};


Прототипное наследование не, не слышали?

rgl 27.03.2013 17:40

Цитата:

Сообщение от megaupload (Сообщение 242947)
function MEGAArray (){};
MEGAArray.prototype = new Array;

MEGAArray.prototype.godOfJavascript = function(){};


Прототипное наследование не, не слышали?

Разумеется, это первое, что приходит в голову каждому, знающему JavaScript на более-менее нормальном уровне. Но каждый (м-м-м, почти каждый) прежде чем писать в форуме, проверил, и убедился что это не работает. Дело в том, что у полученного объекта отсутствует свойство length (точнее оно присутствует, но не свое, а наследуемое, а там оно ноль). Из-за этого методы массива не работают. Свойство length можно добавить, но оно не будет автоматически модифицироваться как должно у массива.
function MEGAArray (){};
MEGAArray.prototype = new Array;

var test = new MEGAArray();
test[0] = "a";
test[1] = "bb";
test[2] = "ccc";

alert( test.length );

rgl 27.03.2013 17:48

Цитата:

Сообщение от Aetae (Сообщение 242931)
Ну или вручную навешивать на каждый создаваемый массив методы, что, естесно, скажется на производительности и будет вылезать в for in.

Как это скажется на производительности, не знаю, но думаю что сильно не ухудшит, т.к. добавленные методы будут находиться ближе, прямо тут а не по цепочке. Другое дело, наверно памяти больше займет. А вылезать в for in будет в любом случае, и если в прототип добавить тоже.

danik.js 27.03.2013 18:45

rgl, твой вариант некудышный, ибо методов много, а конструктор вызывается часто. Так что думаю потребление ресурсов возрастет ощутимо. Кроме того, прототипа то и нет получается ). А нужно чтобы был прототип. И добавляя метод в прототип мы автоматом получаем его у уже созданных экземпляров.

megaupload, этот вариант я пробовал. В консоли объект действительно выглядит как [массив], вот только Array.isArray не обманешь. Ну и toString также выдает [object Object]

А то, что length не обновляется - это не беда. Работа с коллекцией всеравно идет через методы, а не напрямую.

Цитата:

Сообщение от Aetae
Лол, Zepto прямо позиционируется как jquery без ie.) Хочешь поддержку ie - откатись на jquery. (Там так и написано на главной: "If you need to support Internet Explorer, you can fall back on jQuery.")

Дело в том, что это по большей части касается IE6-8.
В девятке и уж тем более в десятке есть все, что нужно. Так что отсутствие поддержки IE9-10 - это всего лишь наследие zepto. А корень проблемы в использовании __proto__ . Кстати, кто-то говорил что это свойство добавили в стандарт, это так?

Если заглянуть сюда https://github.com/madrobby/zepto/issues/ то видно, что не я один хочу поддержки IE.
Цитата:

Сообщение от Aetae
используюя новомодные методы Object

Че за методы такие?

megaupload 27.03.2013 19:21

function MyArray() {}
MyArray.prototype = [];
MyArray.prototype.toString  = function(){alert('ну вы нубы')};

var q = new MyArray();

q.push(1);
q.push(1);

alert(q.length);

q+"";

melky 27.03.2013 19:32

придётся заставить себя не использовать сеттеры длины массива, а использовать встроенные методы

function ArrayLike () { /* NULL */}

// Object.create легко эмулируется
//ArrayLike.prototype = Object.create(Array.prototype);

// вот так, например
var noop = function () {};
noop.prototype = Array.prototype;
ArrayLike.prototype = new noop();
ArrayLike.prototype.constructor = ArrayLike;

// расширим
ArrayLike.prototype.TEST = "Hello World!";

var a = new ArrayLike();

// опробуем новый супер-массив
a.push("a");
alert (a.length + '\n' + a);

a.push("b");

a.push("c");
alert (a.length + '\n' + a);

// повыводим типы
alert( "type: " + typeof(a) );
alert( "class: " + Object.prototype.toString.call(a) );

// и проверим, не тронули ли мы пртотип массива
alert( "a.test : " + a.TEST );
alert( "[].test : " + ['нету тут такого'].TEST);
// и тут тоже
alert( "new Array().test : " + new Array().TEST);

megaupload 27.03.2013 19:39

melky,
и зачем в прототип делать Object.create а не просто создать массив? хоть одну причину назови

melky 27.03.2013 19:50

Цитата:

Сообщение от megaupload (Сообщение 242990)
melky,
и зачем в прототип делать Object.create а не просто создать массив? хоть одну причину назови

мне ..... а нет, погоди. так почему всё-таки TypeScript работает быстрее JavaScript?

megaupload 27.03.2013 20:32

ты не ответил на мой вопрос)

danik.js 27.03.2013 22:12

function MyArray() {}
MyArray.prototype = [];
MyArray.prototype.toString  = function(){alert('ну вы нубы')};
 
var q = new MyArray();
 
alert(Object.prototype.toString.call(q));

alert('— мы нубы? \n— ' + Array.isArray(q));

q.push(1);
q.push(1);

alert( 'concat:' + [].concat(q) );
alert( 'а должно быть: ' + [].concat([1,1]) );

danik.js 27.03.2013 22:27

melky, ключевые моменты:

alert( Array.isArray(myArray) ) // true
alert( Object.prototype.toString.call(myArray) ) // [object Array]
myArray[0] = 'x' //
myArray.length // 1
myArray[1] = 'y', [].concat(myArray) // ['x','y']

melky 28.03.2013 00:17

тогда удачи :) не понимаю, зачем это нужно

Aetae 28.03.2013 00:54

http://javascript.ru/forum/css-html-...-ie-8-a-2.html

danik.js 28.03.2013 06:42

Цитата:

Сообщение от melky
не понимаю, зачем это нужно

Я же объяснял: чтобы заставить zepto работать с IE9+ (или хотя бы с 10).
Я так и предполагал что то, что я спросил - невозможно. Хотел удостовериться.
Придется переделывать на объектах. В принципе коллекции и должны представлять из себя объекты, а не массивы. Так что это изначально ошибка архитектуры. Проблема в том, что потеряется обратная совместимость.

Всем спасибо.

megaupload 28.03.2013 11:14

а я вот так и не понял чо ему надо...

rgl 28.03.2013 12:36

Цитата:

Сообщение от danik.js
А то, что length не обновляется - это не беда. Работа с коллекцией всеравно идет через методы, а не напрямую.

Так что все-таки надо, массив? Методы массива? Коллекция массивом не является. А свойство length важно не только само по себе, оно необходимо чтобы правильно работали методы массива:
Цитата:

Сообщение от rgl
Дело в том, что у полученного объекта отсутствует свойство length (точнее оно присутствует, но не свое, а наследуемое, а там оно ноль). Из-за этого методы массива не работают.

Вот, например:
var test = {};
test[0] = "a";
test[1] = "bb";
test[2] = "ccc";
test.length = 2;

alert( Array.prototype.slice.call( test, 0 ) );

Aetae 28.03.2013 13:21

Если уж извращаться - то можно эмулировать геттером, они вроде в ие 9+ поддерживаются уже. Примерно так, если не думать об оптимизации):
var test = {
    get length(){
        var j = 0;
        for(var i in this)if(!isNaN(i) && i > j) j = +i ;
        return j+1
    }

};
test[0] = "a";
test[1] = "bb";
test[25] = "ccc";
alert(test.length)
 
alert( Array.prototype.slice.call( test, 0 ) );

rgl 28.03.2013 14:29

Aetae,
Только функция должна быть чуть посложнее - нужно отсекать дробные числа, слишком большие числа, и много чего еще:
var test = {
    get length(){
        var j = 0;
        for(var i in this)if(!isNaN(i) && i > j) j = +i ;
        return j+1
    }

};
test[0] = "a";
test[1] = "bb";
test[1e100] = "ccc";

var test2 = [];
test2[0] = "a";
test2[1] = "bb";
test2[1e100] = "ccc";
 
alert( test.length + " | " + test2.length );

nerv_ 28.03.2013 21:20

недавно плагин жукверевский к Zepto подключал. Ой намучался :)

по теме: я вижу только такое решение
var box = {
    method: function() {
      alert( 1 )
    }
};

var arr = [];
arr.method = box.method;

arr.method();

В данном случае просто устанавливаются ссылки на функцию. Т.е. функция не создается заново каждый раз.

p.s.: тред особо не читал

nerv_ 28.03.2013 21:48

Цитата:

Сообщение от danik.js
И добавляя метод в прототип мы автоматом получаем его у уже созданных экземпляров

это зло вообще-то :)

могу еще вариант предложить с аппедиксом :D

var arr = [];

arr.some = {}; // тут твой мега объект, в нем ссылка на родителя (arr)


но это все через ж*пу и проще сделать нормальный объект

danik.js 28.03.2013 22:33

nerv_, видимо больше ничего не остается. Тут чел то же самое предлагает: https://github.com/madrobby/zepto/pu...mment-15571557
Цитата:

Сообщение от nerv_
Ой намучался

Впринципе для плагинов нехватает методов (inner|outerWidth, fade* (но есть модуль), slide*). Еще одна серьезная проблема - расширенные селекторы (хотя ведь можно их избегать). Плюс некоторые различия (тип коллекции zepto - массив, а не объект, коллекция не является инстансом $ и т.д.)

danik.js 28.03.2013 22:36

Цитата:

Сообщение от nerv_
это зло вообще-то

Почему зло? Плагины ведь расширяют прототип. Хотя как правило они подключаются в первую очередь. Но ведь никто не запрещает..

<script>
$el = $('.el');
</script>
<script src="plugin.js"></script>
<script>
$el.plugin() // без прототипа  - беда!
</script>

nerv_ 28.03.2013 23:05

Цитата:

Сообщение от danik.js
Почему зло?

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

я не понял зачем тебе тема с прототипом, т.к. плагины для зепто пишутся также как и для жуквери:
$.fn.some = function() {


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

danik.js 28.03.2013 23:40

Нужно пофиксить коллекции zepto и дописать $.prototype = $.fn. Тогда останутся только проблемы отсутствия методов, которые легко решить и проблема расширенных селекторов, от которых народ нужно отучать.
Хотя есть и другие проблемы.. Тот же data(), который полноценен только через модуль. Есть и другие проблемы..

Тут циклическая зависимость - зепто не используют в качестве замены jQuery => его особенности не принимают во внимание => зепто не используют...

nerv_ 28.03.2013 23:50

Цитата:

Сообщение от danik.js
Нужно пофиксить коллекции zepto

не понял, что ты имеешь ввиду

Цитата:

Сообщение от danik.js
и дописать $.prototype = $.fn

зачем?

Цитата:

Сообщение от danik.js
Тот же data()

его я и писал :) если нужно, могу ссылку дать

Цитата:

Сообщение от danik.js
его особенности не принимают во внимание

скорее одни особенности (жуквери) накладываются на другие (зепто) при попытке использовать зепто
Цитата:

Сообщение от danik.js
в качестве замены jQuery

как результат приходиться лезть в исходники обоих библиотек, чтобы понять, как оно работает и фиксить, фиксить, фиксить :)

Octane 29.03.2013 00:04

А че из ифрейма достать другой конструктор не вариант?
(function () {
	var iFrame = document.createElement("iframe"), iFrameDoc;
	document.body.appendChild(iFrame);
	iFrameDoc = iFrame.contentDocument || iFrame.contentWindow.document;
	iFrameDoc.open();
	iFrameDoc.write('<script>window.Array = Array</scr' + 'ipt>');
	iFrameDoc.close();
	window.$Array = (iFrame.contentWindow || iFrameDoc.defaultView).Array;
}());

$Array.prototype.test = function () {};

var array = [], $array = new $Array();

alert([
	"test" in array,
	"test" in $array,
	Array.isArray ? Array.isArray($array) : "",
	Object.prototype.toString.call($array),
	new $Array(1, 2, 3).concat(4)
]);

danik.js 29.03.2013 00:13

Цитата:

Сообщение от nerv_
не понял, что ты имеешь ввиду

Например в fancybox увидел проверку if ($.type($el) == 'object') { .. }
Но для zepto type = 'array'. Проверка не проходит. И это кривизна zepto.
Цитата:

Сообщение от nerv_
зачем?

Там же увидел if ($el instanceof $) { ... }
Опять же, для zepto это не сработает.

Aetae 29.03.2013 00:41

Цитата:

Сообщение от rgl (Сообщение 243117)
Aetae,
Только функция должна быть чуть посложнее - нужно отсекать дробные числа, слишком большие числа, и много чего еще:

Ну я и не претендовал на окончательность - просто пример.)
Кстати по поводу больших чисел - просто наш псеводомассив круче нативного. ))


Цитата:

Сообщение от Octane (Сообщение 243213)
А че из ифрейма достать другой конструктор не вариант?

Ну да, забыли об энтом варианте. Но тут свои подводные камни, ибо энтот $array уже не instance от Array. Да и грязно это, юзать стоит только если задача стоит "добиться N любой ценой".)

nerv_ 29.03.2013 01:27

Цитата:

Сообщение от danik.js
Там же увидел if ($el instanceof $) { ... }
Опять же, для zepto это не сработает

в зепто для этого есть специальный метод
$.zepto.isZ( some );

Цитата:

Сообщение от danik.js
Например в fancybox увидел проверку if ($.type($el) == 'object') { .. }
Но для zepto type = 'array'. Проверка не проходит.

ради интереса проверил
var x = $( 'div' );

alert( $.type( x ) == 'array' ); // true
alert( $.type( x ) === 'array' ); // и так тоже true

danik.js 29.03.2013 10:01

Octane, блин, круто! Это действительно делает то, что мне нужно. Но это конечно очень грязный хак. Я не уверен что его можно использовать.
А есть еще подобные вариации для получения другого Array ?
Цитата:

Сообщение от Aetae
$array уже не instance от Array

Так это понятно. Это и отражает суть задачи - прототипы должны быть разными. Следовательно мы не будем иметь instance от Array.

Цитата:

Сообщение от nerv_
в зепто для этого есть специальный метод

Я вкурсе. Но jquery-плагинам это неизвестно )
Цитата:

Сообщение от nerv_
alert( $.type( x ) == 'array' ); // true

Я о чем и говорю. Коллекция zepto - массив. Коллекция jquery - объект.
В плагинах бывает проверка через $.type и поведение jquery и zepto тут будет разным.

nerv_ 29.03.2013 12:42

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

Цитата:

Сообщение от danik.js
Я вкурсе. Но jquery-плагинам это неизвестно )

не проще поиск и замена по регулярному выражению внутри плагина, чем вся эта акробатика?

megaupload 29.03.2013 13:09

danik.js,
это еще чо , мы тогда пытались функции с другим прототипом получать вот это было весело)

danik.js 29.03.2013 13:49

Цитата:

Сообщение от nerv_
не проще поиск и замена по регулярному выражению внутри плагина, чем вся эта акробатика?

Правки плагинов - это ведь и есть акробатика. Не проще ли раз и навсегда устранить различия между jquery и zepto ? Нет ведь принципиальных проблем сделать это.

nerv_ 29.03.2013 14:17

Цитата:

Сообщение от danik.js
Не проще ли раз и навсегда устранить различия между jquery и zepto ?

проще использовать жуквери. Или жуквери 2.0 :) А если устранить различия (полностью, как ты хочешь), то вместо зепто мы получим жуквери.

Могу еще пример привести: когда пытался подключить жуквери плагин к зепто, оказалось, что в зепто нет метода pushStack.

danik.js 29.03.2013 15:48

Цитата:

Сообщение от nerv_
А если устранить различия (полностью, как ты хочешь)

Пффф. Почему полностью?
Дописать $.prototype = перед $.fn - это всего 12 символов
Ну с коллекциями будет чуть сложнее, но правок будет не так уж и много. На уровне ядра больше ничего править то и не нужно (наверное). С чего ты взял что после этих правок zepto станет jquery?
Это не кучи говна ради совместимости с IE6, не миллионы оберток, исправляющие недочеты быдлокодеров, css-анимация никуда не денется. Selector engine не добавится.
В общем я знаю две вещи:
1) нужно по возможности использовать Zepto. Ибо работает быстрее, грузится быстрее, и не содержит тонны мертвого кода. А ишак( IE8- ) пускай хавает jQuery.
2) при желании можно писать плагины, совместимые как с jQuery, так и с Zepto. И это не так уж и сложно.


Часовой пояс GMT +3, время: 08:32.