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

Зачем объявлять свойства прототипа для переменных экземпляра в JavaScript

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

Я (думаю, я) понимаю разницу между использованием прототипа для функций или свойств, которые являются переменными класса и использованием this.someProp для переменных "instance" (да, я понимаю, что в JavaScript нет классов).

Код, на который я смотрел, и использую в качестве шаблонов для своего собственного кода, объявляю переменные прототипа и затем ссылаюсь на них с помощью этого i.e.

В конструкторе: this.name = name;

Затем объявление: Object.prototype.name;

И позже, this.name = "Freddy";

Это внутри функций, называемых "new", поэтому в этом случае, как я понимаю, 'this' относится к текущему объекту. Что меня озадачивает, что делает объявление прототипа, и почему мы используем его, например, переменные?

Спасибо, Дэйв

ОК, немного разъяснений. В следующем коде я не вижу того, чего добивается объявление радиуса прототипа:

(function(){
    // constructor
    function MyCircle(radius){
        this.radius = radius;
    }
    MyCircle.prototype.radius;
    this.area = function(){
        return 3.14*this.radius*this.radius;
    };
    window.MyCircle = MyCircle;
}());
4b9b3361

Ответ 1

Значение в прототипе имеет ключевое поведение, отличное от свойства, установленного непосредственно на экземпляре. Попробуйте следующее:

// Create a constructor
function A() {}

// Add a prototype property
A.prototype.name = "Freddy";

// Create two object instances from
// the constructor
var a = new A();
var b = new A();

// Both instances have the property
// that we created on the prototype
console.log(a.name); // Freddy
console.log(b.name); // Freddy

// Now change the property on the
// prototype
A.prototype.name = "George";

// Both instances inherit the change.
// Really they are just reading the
// same property from the prototype
// rather than their own property
console.log(a.name); // George
console.log(b.name); // George

Это невозможно без прототипического наследования.

Вы можете проверить, является ли свойство свойством instance или свойством prototype с помощью метода hasOwnProperty.

console.log(a.hasOwnProperty("name")); // false

Экземпляр может переопределить значение prototype.

b.name = "Chris";
console.log(b.hasOwnProperty("name")); // true
console.log(a.name); // George
console.log(b.name); // Chris

И вернитесь к значению prototype.

delete b.name;
console.log(b.hasOwnProperty("name")); // false
console.log(b.name); // George

Это мощная часть прототипического наследования.

В другом шаблоне:

function A() {
  this.name = "George";
}

Переменная this.name объявляется снова с каждым новым экземпляром.

Имеет смысл иметь методы как функции, объявленные на прототипе. Вместо того, чтобы определение функции повторно декларировалось в каждом экземпляре, все экземпляры могут использовать одну функцию.

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

Код в скрипке

Ответ 2

Значение, сохраненное в прототипе, предоставляет значение по умолчанию для этого свойства.

Если вы впоследствии напишите значение этому свойству, экземпляр получит это новое значение, скрывая значение, которое на прототипе будет оставлено без изменений.

В контексте кода, который вы добавили к вопросу:

MyCircle.prototype.radius;

ничего не делает. Это no-op - он пытается прочитать это свойство и затем отбрасывает результат.

Ответ 3

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

function Person( name, age ) {
    this.name = name;

    if ( age ) {
        this.age = age;
    }
}

Person.prototype.sayHello = function() {
    console.log( 'My name is ' + this.name + '.' );
};

Person.prototype.sayAge = function() {
    if ( this.age ) {
        console.log( 'I am ' + this.age + ' yrs old!' ); 
    } else {
        console.log( 'I do not know my age!' );
    }
};

Person.prototype.age = 0.7;

//-----------

var person = new Person( 'Lucy' );
console.log( 'person.name', person.name ); // Lucy
console.log( 'person.age', person.age );   // 0.7
person.sayAge();                           // I am 0.7 yrs old!

Посмотрите, как Lucy age условно объявлена ​​и инициализирована.

Ответ 4

Другие ответы уже объяснили разницу между свойствами прототипа vs экземпляра.

Но просто чтобы добавить к ответу, позвольте сломать фрагмент кода:

(function(){                         // <------- 1
   // constructor
   function MyCircle(radius){        // <------- 2
       this.radius = radius;         // <------- 2.1
   }
   MyCircle.prototype.radius;        // <------- 3
   this.area = function(){           // <------- 4
       return 3.14*this.radius*this.radius;
   };
   window.MyCircle = MyCircle;       // <------- 5
}());
  • Создание IIFE, который выступает в качестве контейнера области для внутреннего кода
  • Объявление функции с именем MyCircle с использованием шаблона конструктора (но обратите внимание, что оно никогда не получается "построено", поэтому, вероятно, следует избавиться от заглавной буквы, поскольку оно вводит в заблуждение)
    • при вызове создает свойство экземпляра radius для вызываемого объекта
  • Попытка получить доступ к свойству radius в MyCircle функции prototype, которая не существует, поэтому оценивается как undefined
  • Создание свойства экземпляра area для объекта глобального окна и назначение ему выражения функции
  • Создание свойства экземпляра MyCircle объекта window и назначение ему функции MyCircle

Резюме: Кажется, что он создает свойства area и MyCircle для глобального объекта window, а при вызове MyCircle он создает дополнительное свойство radius.

Использование: MyCircle следует вызывать перед областью, так как область использует MyCircle, инициализируя радиус:

window.MyCircle(10);
window.area(); // evaluates to 314