Ответ 1
Позднее редактирование:
Недавно я пересмотрел этот вопрос/ответ, и я думаю, что я понял, почему хром, похоже, "держится" за имя Created
. Это не совсем то, что является эксклюзивным для V8, но я думаю, что это результат того, как V8 работает за кулисами (скрытые объекты, которые я объяснил в своем первоначальном ответе), и что V8 требуется сделать (чтобы соответствовать стандарту ECMAScript).
Любая функция, функции конструктора или иначе, по умолчанию используют один и тот же конструктор и прототип-цепочку:
function Created(){};
console.log(Created.constructor);//function Function() { [native code] }
console.log(Object.getPrototypeOf(Created));//function Empty() {}
console.log(Created.__proto__);//same as above
console.log(Created.prototype);//Created {}
Это говорит нам о нескольких вещах: все функции совместно используют собственный конструктор Function
и наследуют от конкретного экземпляра функции (function Empty(){}
), который используется в качестве своего прототипа. Однако свойство функции prototype
требуется как объект, возвращаемый функцией, если он был вызван как конструктор (см. стандарт ECMAScript).
Значение свойства prototype используется для инициализации внутреннего свойства [[Prototype]] для вновь созданного объекта до того, как объект Function вызывается как конструктор для этого вновь созданного объекта. Это свойство имеет атрибут {[[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false}.
Мы можем легко проверить это, посмотрев на Created.prototype.constructor
:
console.log(Created.prototype.constructor);//function Created() {}
Теперь давайте на мгновение перечислим скрытые классы V8 и, вероятно, создадим их, чтобы они соответствовали стандарту:
function Created(){}
Скрытые классы:
-
Object
, конечно: мать всех объектов, из которыхFunction
- конкретный ребенок -
Function
: Этот собственный объект, как мы показали, является конструктором -
function Empty
: прототип, из которого наша функция наследует -
Created
наша пустая функция, которая наследует все вышеперечисленные
На этом этапе ничего необычного не произошло, и само собой разумеется, что когда мы вернем экземпляр этого конструктора Created
, функция Created
будет открыта из-за своего прототипа.
Теперь, поскольку мы переназначаем свойство prototype
, вы можете утверждать, что этот экземпляр будет отброшен и потерян, но из того, что я понимаю, это не то, как V8 будет обрабатывать эту ситуацию. Вместо этого он создаст дополнительный скрытый класс, который просто переопределяет свойство prototype
его родителя после того, как этот оператор встретится:
Created.prototype = proto;
Его внутренняя структура в конечном итоге выглядит примерно так (пронумеровано на этот раз, потому что я вернусь к определенным этапам в этой цепочке наследования дальше):
-
Object
, конечно: мать всех объектов, из которыхFunction
- конкретный ребенок -
Function
: Этот собственный объект, как мы показали, является конструктором -
function Empty
: прототип, из которого наша функция наследует -
Created
наша пустая функция, которая наследует все вышеперечисленные -
Created2
: расширяет предыдущий класс (Created
) и переопределяетprototype
Так почему же еще Created
?
Что вопрос в миллион долларов, на который, я думаю, теперь есть ответ: Оптимизация
V8 просто не может и не должен позволять оптимизировать скрытый класс Created
(этап 4). Зачем? Потому что то, что будет отменено prototype
, является аргументом. Это то, чего нельзя предсказать. Что V8, вероятно, сделает для оптимизации кода, - это хранить скрытый объект 4, и всякий раз, когда вызывается функция create
, он создаст новый скрытый класс, который продолжит этап 4, переопределяя свойство prototype
любым значением передается функции.
Из-за этого Created.prototype
всегда будет существовать где-то внутри внутреннего представления каждого экземпляра. Также важно отметить, что вы можете заменить свойство prototype
на тот, который на самом деле ссылается на экземпляр Created
(с цепочкой прототипов пропущенных пакетов, но все же):
cc = create();
console.log(Object.getPrototypeOf(cc))//Object {}
cc = create(new GlobalCreated);
console.log(Object.getPrototypeOf(cc));//Created {}
Как это для умы? Начальные script -авторы, питаются вашими сердцами...
Во всяком случае, я надеюсь, что вся эта каламбура имела какое-то значение для кого-то здесь, если нет, я отвечаю на комментарии, так что исправления ошибок, которые я, возможно, сделал, или вопросы относительно некоторой части этого обновления, которая немного неясна приветствуются...
Я постараюсь ответить на вопрос вопросом, но, как вы говорите, все они тесно связаны друг с другом, поэтому ответы перекрываются до определенного момента. Читая это, не забывайте, что я написал это за один раз, чувствуя себя немного лихорадочным. Я не эксперт V8, и основал это на воспоминаниях о том, что я некоторое время назад занимался копанием в V8. Ссылка внизу находится в официальных документах и, конечно же, будет содержать более точную и актуальную информацию по этому вопросу.
Что происходит
Что действительно делает хром V8-движок - это создать скрытый класс для каждого объекта, и этот класс отображается на JS-представление объекта.
Или, как говорят сами люди в google:
Чтобы сократить время, необходимое для доступа к свойствам JavaScript, V8 не использует динамический поиск для доступа к свойствам. Вместо этого V8 динамически создает скрытые классы за кулисами.
Что происходит в вашем случае, расширение, создание нового конструктора из определенного экземпляра и переопределение свойства constructor
на самом деле не более того, что вы можете видеть на этом графике:
Если скрытый класс C0 можно рассматривать как стандартный класс Object
. В принципе, V8 интерпретирует ваш код, строит набор классов, подобных С++, и при необходимости создает экземпляр. У вас есть объекты JS, указывающие на разные экземпляры всякий раз, когда вы меняете/добавляете свойство.
В вашей функции create
это очень вероятно - что происходит:
function create(proto)
{//^ creates a new instance of the Function class -> cf 1 in list below
function Created(){};//<- new instance of Created hidden class, which extends Function cf 2
function Created.prototype = proto;//<- assigns property to Created instance
return new Created;//<- create new instance, cf 3 for details
}
- Правильно:
Function
- это собственная конструкция. Способ работы V8 означает, что существует класс Function, на который ссылаются все функции. Они косвенно ссылаются на этот класс, поскольку каждая функция имеет свои собственные спецификации, которые указаны в производном скрытом классе.create
, то следует рассматривать как ссылку на классcreate extends HiddenFunction
.
Или, если хотите, в синтаксисе С++:class create : public Hidden::Function{/*specifics here*/}
- Функция
create
ссылается на скрытую функцию, идентичнуюcreate
. Однако после объявления этого класса класс получает 1 свойство правдоподобия, называемоеprototype
, поэтому создается другой скрытый класс с указанием этого свойства. Это основа вашего конструктора. Поскольку тело функцииcreate
, где все это происходит, это заданное значение, и V8, вероятно, будет достаточно умным, чтобы заранее создать эти классы, так или иначе: в псевдокоде С++ он будет похож на код 1 ниже.
Каждый вызов функции назначает ссылку на новый экземпляр скрытого класса, описанный выше, на имяCreated
, которое является локальным для областиcreate
. Конечно, возвращаемый экземплярcreate
все еще сохраняет ссылку на этот экземпляр, но то, как работают области JS, и поэтому это относится ко всем моделям... подумайте о закрытии, и вы получите то, что я имею в виду (I ' m действительно борется с этой мерзкой лихорадкой... извините, что нагнал это) - На этом этапе
create
указывает на экземпляр этого скрытого класса, который расширяет класс, который расширяет класс (как я пытался объяснить в пункте 2). Использование поведения тэговnew
триггеров, определенных классомFunction
, конечно (как это построение языка JS). Это приводит к созданию скрытого класса, который, вероятно, одинаковый для всех экземпляров: он расширяет собственный объект и имеет свойствоconstructor
, которое ссылается на экземплярCreated
, который мы только что создали. Экземпляры, возвращаемыеcreate
, хотя и похожи. Уверены, что их конструкторы могут иметь другое свойствоprototype
, но объекты, которые они производят, выглядят одинаково. Я уверен, что V8 создаст только 1 скрытый класс для объектовcreate
. Я не понимаю, почему экземпляры должны требовать разные скрытые классы: их имена и количество свойств одинаковы, но каждый экземпляр ссылается на другой экземпляр, но для каких классов для
В любом случае: список кодов для пункта 2, представление псевдокода того, что Created
может выглядеть в терминах скрытого класса:
//What a basic Function implementation might look like
namespace Hidden
{//"native" JS types
class Function : public Object
{
//implement new keyword for constructors, differs from Object
public:
Function(...);//constructor, function body etc...
Object * operator new ( const Function &);//JS references are more like pointers
int length;//functions have a magic length property
std::string name;
}
}
namespace Script
{//here we create classes for current script
class H_create : public Hidden::Function
{};
class H_Created : public Hidden::Function
{};//just a function
class H_Created_with_prototype : public H_Created
{//after declaring/creating a Created function, we add a property
//so V8 will create a hidden class. Optimizations may result in this class
// being the only one created, leaving out the H_Created class
public:
Hidden::Object prototype;
}
class H_create_returnVal : public Hidden::Object
{
public:
//the constructor receives the instance used as constructor
//which may be different for each instance of this value
H_create_returnVal(H_Created_with_prototype &use_proto);
}
}
Игнорировать любые (вероятные) синтаксические нечеткости (прошло более года с тех пор, как я написал строку С++), и игнорируя пространства имен и неряшливые имена. Перечисленные классы, кроме Hidden::Function
, эффективно всех скрытых классов, которые будут когда-либо необходимо создать для запуска вашего кода. Тогда весь ваш код присваивает ссылки на экземпляры этих классов. Сами классы не занимают много места в памяти. И любой другой движок создаст столько же объектов, потому что они тоже должны соответствовать спецификациям ECMAScript.
Поэтому я думаю, что, глядя на это так, такие ответы отвечают на все ваши вопросы: не все двигатели работают так, но этот подход не приведет к использованию огромного объема памяти. Да, это означает много информации/данные/ссылки на все объекты сохраняются, но это всего лишь неизбежный, а в некоторых случаях и счастливый побочный эффект этого подхода.
Обновление. Я немного поработал и нашел пример того, как вы можете добавлять JS-функции в V8 с использованием шаблонов, это иллюстрирует, как V8 переводит объекты/функции JS на классы С++, см. пример здесь
Это только я размышляю, но я бы совсем не удивился, узнав, что способ V8 работает, и этот бизнес удержания широко используется в сборке мусора и управлении памятью вообще (ЭГ: удаление смены свойств скрыто классы и т.п.)
Например:
var foo = {};//foo points to hidden class Object instance (call id C0)
foo.bar = 123;//foo points to child of Object, which has a property bar (C1)
foo.zar = 'new';//foo points to child of C1, with property zar (C2)
delete foo.zar;//C2 level is no longer required, foo points to C1 again
Этот последний бит - это то, что я угадываю, но GC мог бы сделать это.
Что это за сохранение, используемое для
Как я уже сказал, в V8 объект JS фактически является своего рода указателем на класс С++. Доступ к свойствам (и это включает в себя магические свойства массивов тоже!), Быстро. Действительно, очень быстро. Теоретически доступ к свойству является операцией O (1).
Вот почему, на IE:
var i,j;
for(i=0,j=arr.length;i<j;++i) arr[i] += j;
Быстрее, чем:
for (i=0;i<arr.length;++i) arr[i] += arr.length;
Пока на хроме, arr.length
быстрее как показано ей. Я также ответил на этот вопрос, и он также содержит некоторые сведения о V8, которые вы можете проверить. Может быть, мой ответ там больше не применяется (полностью), потому что браузеры и их двигатели быстро меняются...
Как насчет памяти
Не большая проблема. Да, Chrome иногда может быть немного ресурсоемкой, но JS не всегда виноват. Напишите чистый код, а объем памяти не будет слишком разным в большинстве браузеров.
Если вы создадите огромный конструктор, то V8 создаст более крупный скрытый класс, но если этот класс задает много свойств уже, то шансы на их потребность в дополнительных скрытых классах меньше.
И, конечно, каждая функция является экземпляром класса Function
. В любом случае, это родной (и очень важный) тип функционального языка, скорее всего, будет очень оптимизированным.
Во всяком случае: в отношении использования памяти: V8 отлично справляется с управлением памятью. Например, гораздо лучше, чем IE. Настолько, что двигатель V8 используется для серверной JS (как в node.js), если память действительно была проблемой, тогда вы не мечтали запускать V8 на сервере, который должен быть запущен и запущен как насколько это возможно, теперь вы?
Это просто Chrome
Да, в некотором смысле. У V8 есть особый подход к тому, как он потребляет и запускает JS. Вместо JIT-компиляции вашего кода для байт-кода и его выполнения он компилирует AST прямо в машинный код. Опять же, как и трюки скрытых классов, это повышает производительность.
Я знаю, что я включил этот график в свой ответ на CR, но только для полноты: здесь график, который показывает различия между хромированием (внизу) и другими двигателями JS (сверху)
Обратите внимание, что ниже инструкций байт-кода и процессора есть (оранжевый) слой интерпретатора. То, что V8 не нуждается, из-за того, что JS переводится непосредственно в машинный код.
Недостатком является то, что это затрудняет выполнение определенных оптимизаций, особенно в отношении тех, где DOM-данные и пользовательский ввод используются в коде (например: someObject[document.getElementById('inputField').value]
) и что начальная обработка кода сложнее в CPU.
Верх: когда код скомпилирован в машинный код, он быстрее всего выйдет, а запуск кода, скорее всего, вызовет меньше накладных расходов. В большинстве случаев интерпретатор байт-кода более тяжелый на процессоре, поэтому, когда петли занятости в FF и IE могут заставить браузер предупреждать пользователя "running script", спрашивая их, хотят ли они его остановить.