Что происходит в технике создания объектов Крокфорда?

Есть только 3 строки кода, и все же у меня возникают проблемы с полным пониманием этого:

Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
};
newObject = Object.create(oldObject);

(из прототипного наследования)

  1. Object.create() начинается с создания пустой функции с именем F. Я думаю, что функция - это своего рода объект. Где хранится этот объект F? Глобально, я думаю.

  2. Затем наша oldObject, переданная как o, становится прототипом функции F. Функция (т. е. объект) F теперь «наследует» от нашего oldObject в том смысле, что разрешение имен будет проходить через него. Хорошо, но мне любопытно, какой у объекта прототип по умолчанию, Object? Верно ли это и для объекта-функции?

  3. Наконец, F создается и возвращается, становясь нашим newObject. Здесь строго необходима операция new? Разве F уже не обеспечивает то, что нам нужно, или существует критическая разница между функциональными объектами и нефункциональными объектами? Ясно, что с помощью этой техники невозможно создать функцию-конструктор.

Что произойдет в следующий раз, когда Object.create() вызовут? Глобальная функция F перезаписана? Конечно, он не используется повторно, потому что это изменило бы ранее настроенные объекты. И что произойдет, если несколько потоков вызовут Object.create(), есть ли какая-либо синхронизация для предотвращения условий гонки на F?


person Chris Noe    schedule 04.05.2010    source источник


Ответы (4)


1) Object.create() начинается с создания пустой функции с именем F. Я думаю, что функция — это своего рода объект. Где хранится этот F-объект? Глобально, я думаю.

Нет, он хранится в локальной области видимости функции Object.create, каждый раз, когда вы вызываете Object.create, эта функция F будет создаваться заново.

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

if (typeof Object.create !== "function") {
  Object.create = (function () {
    function F() {} // created only once
    return function (o) {
      F.prototype = o; // reused on each invocation
      return new F();
    };
  })();
}

2) Затем наш oldObject, переданный как o, становится прототипом функции F. Функция (т. е. объект) F теперь «наследуется» от нашего oldObject в том смысле, что разрешение имен будет проходить через него. Хорошо, но мне любопытно, какой у объекта прототип по умолчанию, Object? Верно ли это и для объекта-функции?

У всех объектов есть внутреннее свойство, которое строит цепочку прототипов, это свойство известно как [[Prototype]], это внутреннее свойство, хотя некоторые реализации позволяют вам получить к нему доступ, например, в Mozilla, с помощью свойства obj.__proto__.

Значение по умолчанию [[Prototype]] при создании нового объекта, т. е. var obj = {}; равно Object.prototype.

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

Новый экземпляр объекта создается за кулисами, и этот объект [[Prototype]] устанавливается в свойство prototype конструктора.

3) Наконец, F создается и возвращается, становясь нашим newObject. Является ли «новая» операция строго необходимой здесь? Разве F уже не предоставляет то, что нам нужно, или существует критическая разница между функциональными объектами и нефункциональными объектами? Ясно, что с помощью этой техники невозможно создать функцию-конструктор.

Да, в этом методе необходим оператор new.

Оператор new — это единственный стандартный способ установить внутреннее свойство [[Prototype]] объекта. Если вам интересно, как это работает, вы можете взглянуть на [[Construct]] внутренняя операция.

Что произойдет при следующем вызове Object.create()? Глобальная функция F перезаписывается? Конечно, он не используется повторно, потому что это изменило бы ранее настроенные объекты. И что произойдет, если несколько потоков вызовут Object.create(), есть ли какая-то синхронизация для предотвращения условий гонки на F?

При следующем вызове Object.create новая локальная функция F создается только в рамках вызова метода, вам не нужно беспокоиться об состояниях гонки.

Обратите внимание, что эта реализация вряд ли соответствует Object.create описанной в спецификации ECMAScript 5th Edition, в этом методе вы можете передать дескриптор свойства для инициализации объекта.

Его реализуют все поставщики браузеров (уже доступно в альфа-версиях Firefox 3.7, последних сборках Wekit Nightly и бета-версии Chrome 5), поэтому я бы порекомендовал вам хотя бы проверить, существует ли нативная реализация, прежде чем переопределять ее.

person Christian C. Salvadó    schedule 04.05.2010
comment
Хм, для Крокфорда нет условий гонки из-за локального масштаба, но как насчет вашей версии для повторного использования? - person Chris Noe; 05.05.2010
comment
@Chris, также нет условий гонки, природа JavaScript однопоточна, даже таймеры и другие асинхронные события, такие как взаимодействие с пользователем, выполняются последовательно в блокирующем одиночном потоке. - person Christian C. Salvadó; 05.05.2010
comment
Подобные разговоры не дадут мне слишком легкомысленно относиться к предположениям о многопоточности: гарантированно однопоточный"> stackoverflow.com/questions/2734025/, oreillynet.com/cs/user/view/cs_msg/81559 - person Chris Noe; 10.05.2010
comment
Это правда, что ваша реализация создает только один F. Но она деструктивно изменяет F.prototype для каждого нового объекта, который вы создаете. Между тем, все новые объекты имеют F в качестве .constructor. Таким образом, в любой момент времени у них может быть много разных прототипов, но все их .constructor.prototype будут последними прототипами, вставленными в F. - person FutureNerd; 29.03.2014
comment
У меня была та же проблема, что и у автора вопроса, и следующая статья действительно помогла мне понять, что свойство прототипа в функции javascript на самом деле не является прототипом функции, а является ОБЪЕКТОМ, который СТАНЕТ прототипом нового созданного ОБЪЕКТА: sporto.github.io /блог/22/02/2013/ - person Soferio; 21.11.2016

1) Функция действительно является своего рода объектом. Функциональный объект с идентификатором F создается каждый раз, когда вызывается Object.create, и доступен только с этим идентификатором в этом выполнении Object.create. Поэтому каждый раз, когда вызывается Object.create, вы получаете другой функциональный объект F. Этот функциональный объект продолжает существовать как свойство constructor объекта, возвращаемого Object.create.

2)

F теперь "наследуется" от нашего oldObject, в том смысле, что разрешение имени будет маршрутизироваться через него.

Это не совсем правильно. Присвоение объекта someObject свойству prototype функции просто означает, что прототипом любого будущего объекта, созданного вызовом этой функции в качестве конструктора, будет someObject.

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

Наконец, JavaScript в браузерах является однопоточным, поэтому такие условия гонки, как вы описываете, невозможны.

person Tim Down    schedule 04.05.2010
comment
Условия гонки в любом случае не будут применяться, поскольку F является локальным для функции Object.create, а не глобальным. - person Matthew Crumley; 04.05.2010

Ваше главное недоразумение здесь заключается в том, что F имеет глобальную область видимости. Он объявлен в теле Object.create и, следовательно, находится в области видимости только в этом блоке метода.

person spender    schedule 04.05.2010
comment
и, к удивлению C++, он не уничтожается при выходе из блока. (подсказка: созданный объект сохраняет ссылку, поэтому GC не убивает его, даже если он недоступен напрямую) - person Javier; 04.05.2010

> Ясно, что с помощью этой техники невозможно будет создать функцию-конструктор.

Этот метод уже является конструктором объекта, поскольку он возвращает новый F(), но никакие значения свойств не могут быть установлены, например, для нового человека ('Джон', 'Смит'). Однако, если код Object.create изменен, создание экземпляра возможно. Например, приведенный ниже объект sarah может быть создан и создан с помощью Object.creator, и он унаследует метод getName.

var girl = {
   name: '',
   traits: {},
   getName: function(){return this.name}
}

var sarah = Object.creator(girl, 'Sarah', {age:29,weight:90})

Затем объект sarah будет состоять из собственных свойств {name:'Sarah', traits:{age:9,weight:49} }, а унаследованный прототип sarah.getName() создаст 'Sarah'.

Следующий метод основан на собственных свойствах, перечисляемых с помощью «for (prop in o)» в порядке создания. Хотя это и не гарантируется спецификациями ECMA, этот пример (и несколько более сложных) работал для всех основных протестированных браузеров (4), при условии, что использовалась функция hasOwnProperty(), иначе нет.

Object.creator = function(o) {
   var makeArgs = arguments 
   function F() {
      var prop, i=1, arg, val
      for(prop in o) {
         if(!o.hasOwnProperty(prop)) continue
         val = o[prop]
         arg = makeArgs[i++]
         if(typeof arg === 'undefined') break
         this[prop] = arg
      }
   }
   F.prototype = o
   return new F()
}

Официальный ECMA Object.create имеет необязательный второй параметр, propertiesObject, который может создавать экземпляры значений свойств, но это объект, а не обычный список, и он выглядит неудобным в использовании. Например. Я считаю:-

o2 = Object.create({}, { p: { value: 42, writable: true, enumerable: true, configurable: true } });

эквивалентен гораздо более простому старому способу: -

o2 = new function(p) { this.p=p }(42)

и

o2 = Object.creator({p:''}, 42)
person John    schedule 04.07.2011