Javascript, когда использовать прототипы - PullRequest
87 голосов
/ 19 января 2011

Я хотел бы понять, когда уместно использовать методы-прототипы в js. Должны ли они всегда использоваться? Или есть случаи, когда их использование не является предпочтительным и / или приводит к снижению производительности?

При поиске вокруг этого сайта общих методов для пространств имен в js кажется, что большинство использует реализацию, не основанную на прототипах: просто используя объект или объект функции для инкапсуляции пространства имен.

Исходя из языка, основанного на классах, трудно не пытаться проводить параллели и думать, что прототипы похожи на "классы", а реализации пространства имен, которые я упомянул, похожи на статические методы.

Ответы [ 5 ]

128 голосов
/ 19 января 2011

Прототипы - это оптимизация .

Отличным примером их правильного использования является библиотека jQuery.Каждый раз, когда вы получаете объект jQuery с помощью $('.someClass'), этот объект имеет десятки «методов».Библиотека может достичь этого, возвращая объект:

return {
   show: function() { ... },
   hide: function() { ... },
   css: function() { ... },
   animate: function() { ... },
   // etc...
};

Но это будет означать, что каждый объект jQuery в памяти будет иметь десятки именованных слотов, содержащих одни и те же методы, много раз.

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

Проблема с JavaScript состоит в том, что функции голого конструктора требуют, чтобы вызывающая программа не забывала префиксировать их с new, иначе оникак правило, не работают.Для этого нет веских причин.jQuery делает это правильно, скрывая эту ерунду за обычной функцией $, поэтому вам не нужно заботиться о том, как реализованы объекты.

Чтобы вы могли удобно создать объект с указанным прототипом,ECMAScript 5 включает в себя стандартную функцию Object.create.Очень упрощенная версия выглядела бы так:

Object.create = function(prototype) {
    var Type = function () {};
    Type.prototype = prototype;
    return new Type();
};

Это просто избавляет от боли написания функции конструктора и последующего вызова с помощью new.

Когда бы вы избежали прототипов?

Полезное сравнение с популярными ОО-языками, такими как Java и C #.Они поддерживают два вида наследования:

  • интерфейс наследование, где вы implement и interface так, что класс обеспечивает свою собственную уникальную реализацию для каждого члена интерфейса.
  • реализация наследование, где вы extend a class, который обеспечивает реализации некоторых методов по умолчанию.

В JavaScript прототипическое наследование является своего рода реализация наследование.Таким образом, в тех ситуациях, когда (в C # или Java) вы должны были бы использовать базовый класс для получения поведения по умолчанию, которое затем вносилось бы небольшими изменениями с помощью переопределений, тогда в JavaScript прототипическое наследование имеет смысл.

ОднакоЕсли вы находитесь в ситуации, когда вы использовали бы интерфейсы в C # или Java, то вам не нужны какие-либо особые языковые функции в JavaScript.Нет необходимости явно объявлять что-то, представляющее интерфейс, и не нужно помечать объекты как «реализующие» этот интерфейс:

var duck = {
    quack: function() { ... }
};

duck.quack(); // we're satisfied it's a duck!

Другими словами, если каждый «тип» объекта имеет свои собственные определения«методов», то нет смысла наследовать от прототипа.После этого это зависит от того, сколько экземпляров вы выделите для каждого типа.Но во многих модульных конструкциях есть только один экземпляр данного типа.

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

44 голосов
/ 19 января 2011

Вы должны использовать прототипы, если хотите объявить «нестатический» метод объекта.

var myObject = function () {

};

myObject.prototype.getA = function (){
  alert("A");
};

myObject.getB = function (){
  alert("B");
};

myObject.getB();  // This works fine

myObject.getA();  // Error!

var myPrototypeCopy = new myObject();
myPrototypeCopy.getA();  // This works, too.
16 голосов
/ 19 января 2011

Одна из причин использования встроенного объекта prototype заключается в том, что вы будете дублировать объект несколько раз, чтобы иметь общую функциональность. Прикрепляя методы к прототипу, вы можете сэкономить на дублирующих методах, создаваемых для каждого экземпляра new. Но когда вы присоединяете метод к prototype, все экземпляры будут иметь доступ к этим методам.

Скажем, у вас есть базовый Car() класс / объект.

function Car() {
    // do some car stuff
}

затем вы создаете несколько Car() экземпляров.

var volvo = new Car(),
    saab = new Car();

Теперь вы знаете, что каждая машина должна будет двигаться, включаться и т. Д. Вместо того, чтобы прикреплять метод непосредственно к классу Car() (который занимает память для каждого созданного экземпляра), вы можете прикрепить методы к прототипу. вместо этого (создавая методы только один раз), предоставляя доступ к этим методам как новым volvo, так и saab.

// just mapping for less typing
Car.fn = Car.prototype;

Car.fn.drive = function () {
    console.log("they see me rollin'");
};
Car.fn.honk = function () {
    console.log("HONK!!!");
}

volvo.honk();
// => HONK!!!
saab.drive();
// => they see me rollin'
12 голосов
/ 19 января 2011

Помещайте функции в объект-прототип, когда вы собираетесь создавать множество копий объекта определенного типа, и все они должны иметь общее поведение.Таким образом, вы сэкономите немного памяти, имея только одну копию каждой функции, но это только самое простое преимущество.

Изменение методов на объектах-прототипах или добавление методов мгновенно изменяет природу всех экземпляров.соответствующего типа (ов).

Теперь точно , почему вы бы все это делали, это в основном функция вашего собственного дизайна приложения, и то, что вам нужно делать вкод на стороне клиента.(Совсем другая история - это код внутри сервера; гораздо проще представить более крупномасштабный «OO-код» там.)

2 голосов
/ 19 января 2011

Если я объясняю в терминах, основанных на классе, то Person - это класс, walk () - метод Prototype. Поэтому walk () будет существовать только после того, как вы создадите новый объект с помощью этого.

Таким образом, если вы хотите создать копии объекта, например Person u, можете создать много пользователей. Прототип - это хорошее решение, поскольку он экономит память, разделяя / наследуя одну и ту же копию функции для каждого объекта в памяти.

Принимая во внимание, что static не очень помогает в таком сценарии.

function Person(){
this.name = "anonymous";
}

// its instance method and can access objects data data 
Person.prototype.walk = function(){
alert("person has started walking.");
}
// its like static method
Person.ProcessPerson = function(Person p){
alert("Persons name is = " + p.name);
}

var userOne = new Person();
var userTwo = new Person();

//Call instance methods
userOne.walk();

//Call static methods
Person.ProcessPerson(userTwo);

Так что это более похоже на метод экземпляра. Подход объекта подобен статическим методам.

https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript

...