2012-04-02 79 views
5

我總是假設一個對象的鍵被存儲爲字符串,並且任何非字符串值都會被轉換。所以,正是這種假設下,一邊寫一些代碼,必須存儲數千鍵的值小,我轉換所有鑰匙,基底36:Javascript中存儲的對象屬性名稱如何?

// 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團隊添加的一些額外技巧?

回答

0

這是Chromium的優化。我相信它有啓發式(here's one mention of it)來確定內部存儲屬性的最有效方法。 ECMAScript規範規定的所有內容都是JavaScript和環境之間的接口,並且沒有提及如何在內部實現暴露給JavaScript的對象。

3

這確實是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'中檢查失敗。

相關問題