Почему методы массива находятся в прототипе, а не в конструкторе?
Приветствую)
Почему стандартные объекты в JavaScript (вроде строк или массивов) наследуют свои методы из прототипа, вместо того, чтобы задать их присваиваниями в своём конструкторе? То есть почему в Array делается так:
function Array() {}
Array.prorotype = {
push: function (...) {...},
...
};
Вместо того, чтобы сделать так:
function Array() {
this.push = function (...) {...};
...
}
Ведь именно конструктор - место для задания полей будущего объекта, а прототип - это способ реализовать наследование. Я хочу сказать, что такая архитектура не типична, и если бы вам нужно было реализовать массивы в C++, то вы бы обошлись одним классом, а не двумя с наследованием. Так что каждый раз при создании своих объектов приходится решать для себя дилемму: следовать этой странной яваскриптовой традиции или сделать "по-нормальному". Может быть я чего-то не понимаю, хочется понять причины такой вот яваскриптовой традиции. У меня есть некоторые догадки на этот счёт, но мне бы хотелось узнать ваше мнение) Вот мои мысли: 1) Расширение функциональности. Чтобы можно было легко расширить функциональность всех строк. Кроме того можно легко поменять прототип для всех новых объектов. Это правда считается дурным тоном, нарушением инкапсуляции и вообще ведёт к конфликтам. Так что в нормальных классовых языках этому вообще нет аналога. 2) Производительность. Немного ускоряется создание новых объектов, так как нет множества присваиваний в конструкторе. Однако также немного замедляется обращение к методам, так как приходится дольше их искать. 3) Красивая структура. Чтобы получилась более красивая структура объектов. Если унаследоваться от прототипа, а не от полноценного массива, то потомок не будет засоряться локальными для массива данными вроде length. Хотя обратное вовсе не вредит. Напротив, тогда этот length можно не инициализировать руками в своём потомке. |
Цитата:
|
Вряд ли так уж в разы. Функция - это особый вид объекта. Объекты копируются по ссылке, а не по значению. Значит, чтобы избежать излишнего потребления памяти достаточно вытащить определения методов из конструктора (чтобы эти функции-методы не создавались каждый раз при создании нового объекта).
Было:
function Array() {
this.push = function (...) {...};
...
}
Стало:
function push(...) {...}
function Array() {
this.push = push;
}
Тогда мы тратим память лишь на хранение ссылок в каждом инстансе, а это совсем не так страшно, так как ссылка мало весит. |
Цитата:
var x = 1 /* и тут ты задаешь присваиванием в своем конструкторе */ + 2 /* тут тоже */ + 3 /* и тут */ т.о. для того, чтобы сложить три числа, тебе надо вручную построить три объекта. Аналогично и со строками. Не бред ли? var x = 1 + 2 + 3 // === var y = 1..valueOf() + 2..valueOf() + 3..valueOf(); alert( x ); alert( y ); хотя, скорее всего, примитивы оборачиватюся в объекты только при необходимости В целом могу сказать, что: "хотите писать на js, забудьте про классы, привыкайте (наслаждайтесь) прототипами" Цитата:
|
Цитата:
Цитата:
Например, если вам надо сделать цепочку наследований, такого плана:
{
a: function () {},
__proto__: {
b: function () {},
__proto__: {
c: function () {}
}
}
}
То вы не сможете решить прототипами задачу оптимизации, так как прототип уже использован под наследование:
function A() { this.a = function () {}; }
A.prototype = new B(); // Сюда не получится запихнуть реализацию метода a.
function B() { this.b = function() {}; }
B.prototype = { c: function () {} };
|
Цитата:
Цитата:
function A() { this.a = A.a }
A.prototype = new B(); // Сюда не получится запихнуть реализацию метода a.
A.a = function () {};
function B() { this.b = function() {}; }
B.prototype = { c: function () {} };
? (один из вариантов) забыли про
function A() { }
A.prototype = new B(); // Сюда не получится запихнуть реализацию метода a.
A.prototype.a = function () {};
function B() { this.b = function() {}; }
B.prototype = { c: function () {} };
|
Цитата:
В вашем 1-м варианте вы предварительно сохраняете ссылку на метод в свойство конструктора. Я сохраняю в независимую переменную. Куда бы мы её не сохраняли - принцип один и он не имеет отношения к теме наследований. А во 2-м предложенном варианте вы создадите объект другой структуры:
{ // A
__proto__: { // B
a: function () {},
b: function () {},
__proto__: { // C
c: function () {}
}
}
}
Что-то я тут расфлудился) Надеюсь, я никого не утомил своими изысканиями. |
Цитата:
Цитата:
Цитата:
|
Думаю, что я понял почему так сделано)
Дело не в том, что это какой-то идеально-правильный способ реализовывать ООП в яваскрипте. Всё дело в литералах. 'text' - литерал строки, [ 1, 2, 3 ] - литерал массива, /^nya$/ - литерал регулярки и т.д. Ими все пользуются и никто от них не собирается отказываться. Каждому литералу соответствует конструктор (можно сказать класс). Мы не можем заменить этот класс на другой. Поэтому единственная возможность расширить/изменить функционал классов, связанных с литералами - модифицировать сам класс. И вот нам дали эту единственную возможность - средствами языка через прототип. Между прочим та же проблема есть в других языках (например Ruby) и решается она там точно также, через модификацию стандартного класса. А если же мы строим какие-то свои классы (которым не соответствуют никакие литералы языка), то совершенно не обязательно и не нужно в общем случае городить этот дополнительный слой наследований. |
Цитата:
|
| Часовой пояс GMT +3, время: 04:21. |