Почему методы массива находятся в прототипе, а не в конструкторе?
Приветствую)
Почему стандартные объекты в 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, время: 21:22. |