Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 13.07.2013, 15:28
Новичок на форуме
Отправить личное сообщение для krantadan Посмотреть профиль Найти все сообщения от krantadan
 
Регистрация: 13.07.2013
Сообщений: 5

Почему методы массива находятся в прототипе, а не в конструкторе?
Приветствую)
Почему стандартные объекты в JavaScript (вроде строк или массивов) наследуют свои методы из прототипа, вместо того, чтобы задать их присваиваниями в своём конструкторе?

То есть почему в Array делается так:
function Array() {}
Array.prorotype = {
    push: function (...) {...},
    ...
};


Вместо того, чтобы сделать так:
function Array() {
    this.push = function (...) {...};
    ...
}


Ведь именно конструктор - место для задания полей будущего объекта, а прототип - это способ реализовать наследование. Я хочу сказать, что такая архитектура не типична, и если бы вам нужно было реализовать массивы в C++, то вы бы обошлись одним классом, а не двумя с наследованием.
Так что каждый раз при создании своих объектов приходится решать для себя дилемму: следовать этой странной яваскриптовой традиции или сделать "по-нормальному". Может быть я чего-то не понимаю, хочется понять причины такой вот яваскриптовой традиции.
У меня есть некоторые догадки на этот счёт, но мне бы хотелось узнать ваше мнение)
Вот мои мысли:

1) Расширение функциональности.
Чтобы можно было легко расширить функциональность всех строк.
Кроме того можно легко поменять прототип для всех новых объектов.
Это правда считается дурным тоном, нарушением инкапсуляции и вообще ведёт к конфликтам. Так что в нормальных классовых языках этому вообще нет аналога.

2) Производительность.
Немного ускоряется создание новых объектов, так как нет множества присваиваний в конструкторе. Однако также немного замедляется обращение к методам, так как приходится дольше их искать.

3) Красивая структура.
Чтобы получилась более красивая структура объектов. Если унаследоваться от прототипа, а не от полноценного массива, то потомок не будет засоряться локальными для массива данными вроде length. Хотя обратное вовсе не вредит. Напротив, тогда этот length можно не инициализировать руками в своём потомке.

Последний раз редактировалось krantadan, 13.07.2013 в 15:33.
Ответить с цитированием
  #2 (permalink)  
Старый 13.07.2013, 15:42
Аватар для danik.js
Профессор
Отправить личное сообщение для danik.js Посмотреть профиль Найти все сообщения от danik.js
 
Регистрация: 11.09.2010
Сообщений: 8,804

Сообщение от krantadan
если бы вам нужно было реализовать массивы в C++, то вы бы обошлись одним классом, а не двумя с наследованием
В C++ свой способ хранения методов и статичных свойств, в javascript - свой. Если для каждого инстанса создавать весь набор методов то объем потребляемой памяти возрастет в разы. В C++ если не ошибаюсь все методы хрянятся отдельно от инстансов. В JS вот тоже самое. И в чем тогда непонимание?
Ответить с цитированием
  #3 (permalink)  
Старый 13.07.2013, 16:18
Новичок на форуме
Отправить личное сообщение для krantadan Посмотреть профиль Найти все сообщения от krantadan
 
Регистрация: 13.07.2013
Сообщений: 5

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

Было:
function Array() {
    this.push = function (...) {...};
    ...
}


Стало:
function push(...) {...}
function Array() {
    this.push = push;
}


Тогда мы тратим память лишь на хранение ссылок в каждом инстансе, а это совсем не так страшно, так как ссылка мало весит.
Ответить с цитированием
  #4 (permalink)  
Старый 13.07.2013, 16:40
Аватар для nerv_
junior
Отправить личное сообщение для nerv_ Посмотреть профиль Найти все сообщения от nerv_
 
Регистрация: 29.11.2011
Сообщений: 3,924

Сообщение от krantadan
Почему стандартные объекты в JavaScript (вроде строк или массивов) наследуют свои методы из прототипа, вместо того, чтобы задать их присваиваниями в своём конструкторе?
var x = 1 /* и тут ты задаешь присваиванием в своем конструкторе */ + 2 /* тут тоже */ + 3 /* и тут */

т.о. для того, чтобы сложить три числа, тебе надо вручную построить три объекта. Аналогично и со строками. Не бред ли?

var x = 1 + 2 + 3 // ===
var y = 1..valueOf() + 2..valueOf() + 3..valueOf();

alert( x );
alert( y );


хотя, скорее всего, примитивы оборачиватюся в объекты только при необходимости

В целом могу сказать, что: "хотите писать на js, забудьте про классы, привыкайте (наслаждайтесь) прототипами"

Сообщение от krantadan
Значит, чтобы избежать излишнего потребления памяти достаточно вытащить определения методов из конструктора (чтобы эти функции-методы не создавались каждый раз при создании нового объекта).
это встроено в язык - прототипы
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук

Последний раз редактировалось nerv_, 13.07.2013 в 16:49.
Ответить с цитированием
  #5 (permalink)  
Старый 13.07.2013, 17:15
Новичок на форуме
Отправить личное сообщение для krantadan Посмотреть профиль Найти все сообщения от krantadan
 
Регистрация: 13.07.2013
Сообщений: 5

Сообщение от nerv_
т.о. для того, чтобы сложить три числа, тебе надо вручную построить три объекта. Аналогично и со строками. Не бред ли?
Не понял мысль. Числа - элементарный тип. При их сложении вообще не вызываются какие-либо конструкторы.

Сообщение от nerv_
это встроено в язык - прототипы
Нет, не встроено. Прототип - это в первую очередь средство решения архитектурных задач, а не оптимизации.

Например, если вам надо сделать цепочку наследований, такого плана:
{
    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 () {} };

Последний раз редактировалось krantadan, 13.07.2013 в 17:17.
Ответить с цитированием
  #6 (permalink)  
Старый 13.07.2013, 18:08
Аватар для nerv_
junior
Отправить личное сообщение для nerv_ Посмотреть профиль Найти все сообщения от nerv_
 
Регистрация: 29.11.2011
Сообщений: 3,924

Сообщение от krantadan
Числа - элементарный тип. При их сложении вообще не вызываются какие-либо конструкторы
и при этом эти "элементарные типы" имеют методы и свойства

Сообщение от krantadan
То вы не сможете решить прототипами задачу оптимизации
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 () {} };
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук

Последний раз редактировалось nerv_, 13.07.2013 в 18:30.
Ответить с цитированием
  #7 (permalink)  
Старый 13.07.2013, 18:46
Новичок на форуме
Отправить личное сообщение для krantadan Посмотреть профиль Найти все сообщения от krantadan
 
Регистрация: 13.07.2013
Сообщений: 5

Сообщение от nerv_
и при этом эти "элементарные типы" имеют методы и свойства
мм... ну да, при обращении к полям переменной элементарного типа, она неявным образом подменяется соответствующим объектом. Но при сложении обращения к полям нет. Даже если в вашем коде случается вызов конструктора, пара десятков копирований по ссылке замедляют работу конструктора незначительно.

В вашем 1-м варианте вы предварительно сохраняете ссылку на метод в свойство конструктора. Я сохраняю в независимую переменную. Куда бы мы её не сохраняли - принцип один и он не имеет отношения к теме наследований.
А во 2-м предложенном варианте вы создадите объект другой структуры:
{ // A
    __proto__: { // B
        a: function () {},
        b: function () {},
        __proto__: { // C
            c: function () {}
        }
    }
}

Что-то я тут расфлудился) Надеюсь, я никого не утомил своими изысканиями.
Ответить с цитированием
  #8 (permalink)  
Старый 13.07.2013, 22:56
Аватар для nerv_
junior
Отправить личное сообщение для nerv_ Посмотреть профиль Найти все сообщения от nerv_
 
Регистрация: 29.11.2011
Сообщений: 3,924

Сообщение от krantadan
В вашем 1-м варианте вы предварительно сохраняете ссылку на метод в свойство конструктора. Я сохраняю в независимую переменную. Куда бы мы её не сохраняли - принцип один и он не имеет отношения к теме наследований.
Сообщение от krantadan
То вы не сможете решить прототипами задачу оптимизации
решил, без помощи прототипов - не создаю функцию каждый раз при создании экземпляра, а использую одну, заранее созданную

Сообщение от krantadan
А во 2-м предложенном варианте вы создадите объект другой структуры:
нет
__________________
Чебурашка стал символом олимпийских игр. А чего достиг ты?
Тишина - самый громкий звук
Ответить с цитированием
  #9 (permalink)  
Старый 15.07.2013, 14:15
Новичок на форуме
Отправить личное сообщение для krantadan Посмотреть профиль Найти все сообщения от krantadan
 
Регистрация: 13.07.2013
Сообщений: 5

Думаю, что я понял почему так сделано)
Дело не в том, что это какой-то идеально-правильный способ реализовывать ООП в яваскрипте. Всё дело в литералах. 'text' - литерал строки, [ 1, 2, 3 ] - литерал массива, /^nya$/ - литерал регулярки и т.д. Ими все пользуются и никто от них не собирается отказываться. Каждому литералу соответствует конструктор (можно сказать класс). Мы не можем заменить этот класс на другой. Поэтому единственная возможность расширить/изменить функционал классов, связанных с литералами - модифицировать сам класс. И вот нам дали эту единственную возможность - средствами языка через прототип. Между прочим та же проблема есть в других языках (например Ruby) и решается она там точно также, через модификацию стандартного класса. А если же мы строим какие-то свои классы (которым не соответствуют никакие литералы языка), то совершенно не обязательно и не нужно в общем случае городить этот дополнительный слой наследований.

Последний раз редактировалось krantadan, 15.07.2013 в 14:38.
Ответить с цитированием
  #10 (permalink)  
Старый 15.07.2013, 15:21
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 06.04.2009
Сообщений: 2,379

Сообщение от krantadan
то совершенно не обязательно и не нужно в общем случае городить этот дополнительный слой наследований
типа в конструкторе массива вручную заполнить экземпляр методами массива, и вместо наследования от объекта так же вручную добавить методы объекта?
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Нужен цикл для создания огромного массива apish Общие вопросы Javascript 2 20.09.2012 16:10
Почему некоторые операторы возвращают значения, а не ссылки? dump Общие вопросы Javascript 15 25.07.2012 17:28
Сортировка массива по ключу RazZzeR Элементы интерфейса 9 21.07.2012 19:31
Проблема с передачей массива ajax-ом maximale AJAX и COMET 1 11.02.2012 02:20
Свойства объекта, методы и this. Почему свойство вызывается с () ? jsuse Общие вопросы Javascript 2 04.11.2011 20:39