Прототип JavaScript ограничен функциями? - PullRequest
7 голосов
/ 21 января 2010

o.prototype = {...} работает, только если o является функцией.Предположим, у меня следующий код

 conf = {
  a: 2,
  b: 4
 };
 conf.prototype = {
  d: 16
 }

conf.a и conf.b в порядке и возвращает правильные значенияНо conf.d не возвращает 16, а остается неопределенным.Есть ли какое-то решение, что обобщение на основе прототипов также может быть применено к этим типам объектов.

Ответы [ 2 ]

21 голосов
/ 21 января 2010

Вы путаете свойство prototype, которое можно использовать в функциях конструктора и внутреннем [[Prototype]] свойстве.

Все объекты имеют это внутреннее свойство [[Prototype]], и только оператор new, когда вы вызываете его с помощью функции конструктора, может установить его (через [[Construct]] внутренняя операция).

Если вы хотите иметь наследование прототипа с экземплярами объекта (без использования конструкторов), вам нужна техника Object.create Крокфорда (этот метод теперь является частью недавно утвержденного ECMAScript 5-го издания):

// Check if native implementation available
if (typeof Object.create !== 'function') {
  Object.create = function (o) {
    function F() {}  // empty constructor
    F.prototype = o; // set base object as prototype
    return new F();  // return empty object with right [[Prototype]]
  };
}

var confProto = {
  d: 16
};
var conf = Object.create(confProto);
conf.a = 2;
conf.b = 4;

В приведенном выше коде conf будет иметь трех членов, но физически на нем будут существовать только a и b:

conf.hasOwnProperty('a'); // true 
conf.hasOwnProperty('b'); // true
conf.hasOwnProperty('d'); // false

Потому что d существует на conf [[Prototype]] (confProto).

Средства доступа к свойствам , . и [] отвечают за разрешение поиска свойств при необходимости в цепочке прототипов (с помощью внутреннего метода [[Get]]).

6 голосов
/ 27 января 2010

На самом деле в JavaScript существует два типа прототипа:

  1. Одна из них - это «скрытая» ссылка, которую имеет каждый объект (давайте используем [[Prototype]] для представления этой скрытой ссылки). по умолчанию для литералов объектов их скрытые ссылки указывают на Object.prototype, для объектов-функций их скрытые ссылки указывают на Function.prototype, а для массивов - на Array.prototype. Эти скрытые ссылки на прототип не связаны со свойствами с именем "prototype". Вы не можете изменить эти скрытые ссылки, добавив или изменив o.prototype.
  2. Еще один момент заключается в том, что все функциональные объекты автоматически имеют специальное свойство с именем "prototype". Это в основном для использования шаблона вызова конструктора.

[[Prototype]] используется для поиска свойств (как родитель в классической иерархии), когда свойство не может быть найдено в объекте, вместо него ищется его [[Prototype]]. Один сценарий использования: скажем, вы хотите добавить свойство ко всем объектам, вы можете просто добавить его к Object.prototype, который будет автоматически применяться ко всем объектам, поскольку все объекты так или иначе имеют Object.prototype в качестве [[Prototype]] корня цепи.

Вернемся к свойству функциональных объектов "prototype". Это полезно только при использовании с оператором new. Возьмем следующий фрагмент кода в качестве примера:

function F() {} // function declaration
// F now has a property named "prototype"

var f = new F(); // use "new" operator to create a new function object based on F

То, что new F() делает выше, - это сначала создать новый функциональный объект, установить [[Prototype]] (скрытая ссылка) этого вновь созданного функционального объекта равным F.prototype, а затем вернуть новый функциональный объект. Это, вероятно, то, что вы уже поняли, что работает для функциональных объектов.

Помните, что я сказал, что мы не можем изменить объекты [[Prototype]]? Ну, по крайней мере, не напрямую. Функция Крокфорда Object.create делает именно это, используя тот факт, что оператор new может помочь установить для нас [[Prototype]]. Таким образом, используя Object.create, вы сознательно указываете, куда должна указывать скрытая ссылка вашего нового объекта. (что-то вроде указания, кто ваш родительский класс)

В вашем примере conf - это литерал объекта, а conf.prototype не очень полезен. Вот еще одна версия с использованием классического стиля:

function ConfBase() {}
ConfBase.prototype.d = 16;
var conf = new ConfBase();
conf.a = 2;
conf.b = 4;

document.writeln(conf.a);
document.writeln(conf.b);
document.writeln(conf.d);

По сравнению с ответом @CMS, я предпочитаю использовать Object.create. Но, по сути, два стиля используют один и тот же базовый механизм, просто Object.create помогает привести его в порядок.

...