【问题标题】:How are the attribute names on objects stored in Javascript?对象的属性名称如何存储在 Javascript 中?
【发布时间】:2012-04-02 12:32:18
【问题描述】:

我一直假设对象的键存储为字符串,并且任何非字符串值都会被强制转换。因此,正是在这种假设下,在编写一些必须为数千个键存储小值的代码时,我将所有键都转换为 base 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 团队添加的一些额外技巧?

【问题讨论】:

    标签: javascript google-chrome memory


    【解决方案1】:

    这确实是 V8 的一些额外技巧。

    JSObject(JS Object 的内部 C++ 表示)有两个属性,elementsproperties,其中“元素”是带有数字索引的 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 调用 SetElementSetProperty,具体取决于密钥。不知道为什么在您的测试用例key = i + '0' 中检查失败。

    【讨论】:

      【解决方案2】:

      这是 Chromium 中的优化。我相信它具有启发式 (here's one mention of it) 来确定在内部存储属性的最有效方式。 ECMAScript 规范所规定的只是 JavaScript 和环境之间的接口,并没有说明暴露给 JavaScript 的对象是如何在内部实现的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-25
        • 2020-04-21
        相关资源
        最近更新 更多