Подтвердить что ты не робот

Добавление методов в пользовательские объекты в Javascript

Возможный дубликат:
Использование "прототипа" против "этого" в Javascript?

Я пошел на разные сайты, но не смог понять разницу между следующими способами добавления методов к пользовательским объектам:

Способ 1:

function circle(radius){
     this.radius = radius;
     this.area = function(){ return 3.14*this.radius*this.radius;}
}

Способ 2:

function circle(radius){
     this.radius = radius;
}
circle.prototype.area = function(){ return 3.14*this.radius*this.radius; }

Есть ли проблемы с производительностью или дизайном, которые имеет один из методов, а другой нет?

4b9b3361

Ответ 1

Вот один из способов увидеть разницу:

var circle1 = circle(1);
var circle2 = circle(1);
alert(circle1.area == circle2.area);

Для метода 1 вы увидите false, а Method2 - в true. Это потому, что в первом случае вы назначаете новую функцию закрытия каждому созданному объекту, два объекта имеют разные функции area, хотя оба делают то же самое. Во втором случае объекты имеют один и тот же прототипный объект с его методом area, этот метод, конечно, одинаковый в обоих случаях.

Обычно метод 2 предпочтительнее по нескольким причинам:

  • Создание объекта происходит быстрее, потому что необходимо инициализировать только настраиваемые свойства.
  • Вы не теряете память при создании нескольких копий одной и той же вещи.
  • Вы можете легко узнать, какие из свойств объекта имеют пользовательские значения, вызывая Object.hasOwnProperty.

Есть и некоторые недостатки, которые нужно помнить.

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

Вот пример распространенной ошибки:

function NumberCollection()
{
}
NumberCollection.prototype = {
    numbers: [],
    sum: function()
    {
        var result = 0;
        for (var i = 0; i < this.numbers.length; i++)
            result += this.numbers[i];
        return result;
    }
}

var c1 = new NumberCollection();
c1.numbers.push(5);
alert(c1.sum());   // Shows 5
var c2 = new NumberCollection();
c2.numbers.push(6);
alert(c2.sum());   // Oops, shows 11 because c1.numbers and c2.numbers is the same

Правильный подход здесь:

function NumberCollection()
{
    this.numbers = [];
}
NumberCollection.prototype = {
    numbers: null,
    sum: function()
    {
        var result = 0;
        for (var i = 0; i < this.numbers.length; i++)
            result += this.numbers[i];
        return result;
    }
}

Ответ 2

Первый метод (пусть он называет тип 1) добавляет функцию area() к самому объекту. Каждый раз, когда вы создаете объект circle с new, его тело будет скопировано (!) В новый экземпляр.

Второй метод (тип 2) добавляет функцию area() к прототипу объекта (прототип находится на уровне "один уровень вверх" в иерархии). Каждый раз, когда вы создаете новый экземпляр circle, будет скопировано только свойство radius.

Теперь, когда вы вызываете area() в экземпляр второго объекта, JavaScript не может найти эту функцию для самого объекта. Теперь он "поднимается" по цепочке прототипов и использует первую функцию этого имени, которое она находит.

Теперь это имеет несколько последствий:

  • Новые экземпляры объектов будут меньше (меньше физического кода)
  • Вы можете изменить реализацию area для всех существующих объектов типа 2 одновременно, и пока они живут (путем изменения прототипа), вы не можете сделать это для типа 1
  • Вы можете переопределить функциональность area в одном экземпляре типа 2, сохраняя "исходную реализацию" функции вокруг