Как имена атрибутов на объектах хранятся в Javascript? - PullRequest
5 голосов
/ 02 апреля 2012

Я всегда предполагал, что ключи объекта хранятся в виде строк и что любое нестроковое значение будет приведено.Таким образом, именно в этом предположении при написании кода, который должен был хранить небольшое значение для многих тысяч ключей, я преобразовал все ключи в базу 36:

// theKey is an integer
myMap[theKey.toString(36)] = theValue;

Затем я решил посмотреть,мое предположение было на самом деле правильно, и использовал профилировщик Chrome, чтобы проверить использование памяти.Примерно вот тесты, которые я провел, и использование памяти:

window.objIntegers = {};
for (i = 100000; i--) window.objIntegers[i] = 'a';
// 786kb

window.objStrings = {};
for (i = 100000; i--) window.objStrings[i.toString(36)] = 'a';
// 16.7mb!

// and the same pattern but with:
key = i + .5;  // 16.7mb
key = i + '';  // 786kb
key = '0' + i; // 16.7mb
key = i + '0'; // 16.7mb

Очевидно, мои предположения были неверны.Что мне интересно, так это то, как они хранятся и является ли это поведение стандартным, или просто какая-то дополнительная хитрость, добавленная командой Chromium / WebKit?

Ответы [ 2 ]

3 голосов
/ 02 апреля 2012

Это действительно дополнительная хитрость V8.

A JSObject (внутреннее представление JS Object в C ++) имеет два атрибута, elements и properties, где «элементы» - это атрибуты JS с числовыми индексами, а «свойства» - это атрибуты JS со строковыми индексами.

Очевидно, что числовые индексы занимают здесь гораздо меньше памяти, поскольку имена свойств не нужно хранить.

http://code.google.com/intl/de-DE/chrome/devtools/docs/memory-analysis-101.html#primitive_objects

Типичный объект JavaScript имеет два массива: один для хранения именованных свойств, другой для хранения числовых элементов.

Это видно из исходного кода v8:

http://code.google.com/p/v8/source/browse/trunk/src/objects.h#1483

// [properties]: Backing storage for properties.
...
// [elements]: The elements (properties with names that are integers).

http://code.google.com/p/v8/source/browse/trunk/src/runtime.cc#4462

MaybeObject* Runtime::SetObjectProperty(Isolate* isolate,
                                        Handle<Object> object,
                                        Handle<Object> key,
                                        Handle<Object> value,
                                        PropertyAttributes attr,
                                        StrictModeFlag strict_mode) {
  ...

  // Check if the given key is an array index.
  uint32_t index;
  if (key->ToArrayIndex(&index)) {
    // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
    // of a string using [] notation.  We need to support this too in
    // JavaScript.
    // In the case of a String object we just need to redirect the assignment to
    // the underlying string if the index is in range.  Since the underlying
    // string does nothing with the assignment then we can ignore such
    // assignments.
    if (js_object->IsStringObjectWithCharacterAt(index)) {
      return *value;
    }

    Handle<Object> result = JSObject::SetElement(
        js_object, index, value, attr, strict_mode, set_mode);
    if (result.is_null()) return Failure::Exception();
    return *value;
  }

  if (key->IsString()) {
    Handle<Object> result;
    if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
      result = JSObject::SetElement(
          js_object, index, value, attr, strict_mode, set_mode);
    } else {
      Handle<String> key_string = Handle<String>::cast(key);
      key_string->TryFlatten();
      result = JSReceiver::SetProperty(
          js_object, key_string, value, attr, strict_mode);
    }
    if (result.is_null()) return Failure::Exception();
    return *value;
  }

  // Call-back into JavaScript to convert the key to a string.
  bool has_pending_exception = false;
  Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
  if (has_pending_exception) return Failure::Exception();
  Handle<String> name = Handle<String>::cast(converted);

  if (name->AsArrayIndex(&index)) {
    return js_object->SetElement(
        index, *value, attr, strict_mode, true, set_mode);
  } else {
    return js_object->SetProperty(*name, *value, attr, strict_mode);
  }
}

Я не буду вдаваться в подробности, но учтите, что SetObjectProperty вызывает либо SetElement, либо SetProperty, в зависимости от ключа. Не уверен, почему проверка не проходит в вашем тестовом случае key = i + '0' хотя.

0 голосов
/ 02 апреля 2012

Это оптимизация в Chromium.Я считаю, что у него есть эвристика (, вот одно упоминание об этом ), чтобы определить наиболее эффективный способ хранения свойств внутри.Все, что диктует спецификация ECMAScript, это интерфейс между JavaScript и средой и ничего не говорит о том, как объекты, представленные в JavaScript, реализованы внутри.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...