2016-05-05 34 views
7

Studiuję algorytmy JavaScript i Big O w wywiadach. Powiedziano mi, że ważna jest znajomość runtime wbudowanych metod, takich jak Object.prototype.hasOwnProperty i Array.prototype.map.Gdzie mogę znaleźć kod źródłowy dla metod JavaScript, takich jak hasOwnProperty, w Node.js?

Jaki jest prosty sposób wyświetlenia kodu źródłowego tych funkcji w pliku node.js? Mam lokalną kopię pliku node.js i próbowałem wyszukać te metody w edytorze tekstu, ale nie jest to tak proste, jak myślałem.

+1

Czy to nie jest zabawne, że musisz badać bezużyteczne śmieci, których NIGDY nie potrzebujesz w praktyce, aby móc przeprowadzić wywiad? – Hill

+0

Zobacz także [Jak wyświetlić źródło wbudowanych funkcji javascript?] (Https://stackoverflow.com/q/22300206/1048572) – Bergi

Odpowiedz

10

Object.prototype.hasOwnProperty()

Z Javascript punktu widzenia wywiadu, to myślę, że wystarczy, aby w pełni zrozumieć, co obj.hasOwnProperty() robi na poziomie JavaScript, a nie jak to jest realizowane wewnątrz V8.

Aby to zrobić, należy w pełni zrozumieć ten mały fragment:

function MyConstructor() { 
    this.methodB = function() {} 
} 

MyConstructor.prototype = { 
    methodA: function() {} 
}; 

var o = new MyConstructor(); 
log(o.hasOwnProperty("methodA")); // false 
log(o.hasOwnProperty("methodB")); // true 

o.methodA = function() {};   // assign "own" property, overrides prototype 
log(o.hasOwnProperty("methodA")); // true 

To dlatego .hasOwnProperty() wygląda tylko na samego obiektu, a nie na łańcuchu prototypów. Więc właściwości, które są tylko w łańcuchu prototypów lub w ogóle nie istnieją, zwrócą false, a właściwości bezpośrednio na obiekcie zwrócą true.


Array.prototype.map()

polyfill w JavaScript dla Array.prototype.map() jest here on MDN który pokaże Ci dokładnie jak to działa. Możesz oczywiście zrobić to samo, co powyżej, w repozytorium Githuba, aby znaleźć implementację .map(), jeśli chcesz.

Array.prototype.map() jest naprawdę bardzo prosty. Iteruj po tablicy, wywołując funkcję dla każdego elementu w tablicy. Każda zwracana wartość tej funkcji zostanie użyta do skonstruowania nowej tablicy, która zostanie zwrócona z połączenia do .map(). Więc, koncepcyjnie, jest używany do "mapowania" jednej tablicy do drugiej przez wywołanie funkcji transformacji na każdym elemencie oryginalnej tablicy.

W najprostszym wcieleniu dodać 1 do każdego elementu tablicy:

var origArray = [1,2,3]; 

var newArray = origArray.map(function(item, index, array) { 
    return item + 1; 
}); 

console.log(newArray); // [2,3,4] 

Rzeczywisty kod źródłowy V8:

Jeśli naprawdę chcesz zobaczyć, jak to jest realizowane wewnątrz V8, tutaj znajdują się fragmenty kodu i odnośniki do odpowiednich rzeczywistych plików kodu. Jak widać, większość z nich znajduje się w C++ i aby ją zrozumieć, musisz zrozumieć, jak obiekty są uporządkowane w pamięci i jakie metody C++ mają wewnętrznie w V8. Jest to bardzo specyficzna dla V8, a nie ogólna wiedza JavaScript.

Dołączyłem także linki do odpowiednich plików źródłowych, więc jeśli chcesz zobaczyć inny kontekst w tych plikach, możesz kliknąć linki, aby to zobaczyć.

W v8.h:

V8_DEPRECATED("Use maybe version", bool HasOwnProperty(Local<String> key)); 
V8_WARN_UNUSED_RESULT Maybe<bool> HasOwnProperty(Local<Context> context, Local<Name> key); 

W api.cc:

Maybe<bool> v8::Object::HasOwnProperty(Local<Context> context, 
             Local<Name> key) { 
    PREPARE_FOR_EXECUTION_PRIMITIVE(context, "v8::Object::HasOwnProperty()", 
            bool); 
    auto self = Utils::OpenHandle(this); 
    auto key_val = Utils::OpenHandle(*key); 
    auto result = i::JSReceiver::HasOwnProperty(self, key_val); 
    has_pending_exception = result.IsNothing(); 
    RETURN_ON_FAILED_EXECUTION_PRIMITIVE(bool); 
    return result; 
} 

bool v8::Object::HasOwnProperty(Local<String> key) { 
    auto context = ContextFromHeapObject(Utils::OpenHandle(this)); 
    return HasOwnProperty(context, key).FromMaybe(false); 
} 

W v8natives.js:

// ES6 7.3.11 
function ObjectHasOwnProperty(value) { 
    var name = TO_NAME(value); 
    var object = TO_OBJECT(this); 
    return %HasOwnProperty(object, name); 
} 

W objects-inl.h:

Maybe<bool> JSReceiver::HasOwnProperty(Handle<JSReceiver> object, 
             Handle<Name> name) { 
    if (object->IsJSObject()) { // Shortcut 
    LookupIterator it = LookupIterator::PropertyOrElement(
     object->GetIsolate(), object, name, LookupIterator::HIDDEN); 
    return HasProperty(&it); 
    } 

    Maybe<PropertyAttributes> attributes = 
     JSReceiver::GetOwnPropertyAttributes(object, name); 
    MAYBE_RETURN(attributes, Nothing<bool>()); 
    return Just(attributes.FromJust() != ABSENT); 
} 

W runtime-object.cc:

static Object* HasOwnPropertyImplementation(Isolate* isolate, 
              Handle<JSObject> object, 
              Handle<Name> key) { 
    Maybe<bool> maybe = JSReceiver::HasOwnProperty(object, key); 
    if (!maybe.IsJust()) return isolate->heap()->exception(); 
    if (maybe.FromJust()) return isolate->heap()->true_value(); 
    // Handle hidden prototypes. If there's a hidden prototype above this thing 
    // then we have to check it for properties, because they are supposed to 
    // look like they are on this object. 
    if (object->map()->has_hidden_prototype()) { 
    PrototypeIterator iter(isolate, object); 
    DCHECK(!iter.IsAtEnd()); 

    // TODO(verwaest): The recursion is not necessary for keys that are array 
    // indices. Removing this. 
    // Casting to JSObject is fine because JSProxies are never used as 
    // hidden prototypes. 
    return HasOwnPropertyImplementation(
     isolate, PrototypeIterator::GetCurrent<JSObject>(iter), key); 
    } 
    RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); 
    return isolate->heap()->false_value(); 
} 


RUNTIME_FUNCTION(Runtime_HasOwnProperty) { 
    HandleScope scope(isolate); 
    DCHECK(args.length() == 2); 
    CONVERT_ARG_HANDLE_CHECKED(Object, object, 0) 
    CONVERT_ARG_HANDLE_CHECKED(Name, key, 1); 

    uint32_t index; 
    const bool key_is_array_index = key->AsArrayIndex(&index); 

    // Only JS objects can have properties. 
    if (object->IsJSObject()) { 
    Handle<JSObject> js_obj = Handle<JSObject>::cast(object); 
    // Fast case: either the key is a real named property or it is not 
    // an array index and there are no interceptors or hidden 
    // prototypes. 
    // TODO(jkummerow): Make JSReceiver::HasOwnProperty fast enough to 
    // handle all cases directly (without this custom fast path). 
    Maybe<bool> maybe = Nothing<bool>(); 
    if (key_is_array_index) { 
     LookupIterator it(js_obj->GetIsolate(), js_obj, index, 
         LookupIterator::HIDDEN); 
     maybe = JSReceiver::HasProperty(&it); 
    } else { 
     maybe = JSObject::HasRealNamedProperty(js_obj, key); 
    } 
    if (!maybe.IsJust()) return isolate->heap()->exception(); 
    DCHECK(!isolate->has_pending_exception()); 
    if (maybe.FromJust()) { 
     return isolate->heap()->true_value(); 
    } 
    Map* map = js_obj->map(); 
    if (!key_is_array_index && !map->has_named_interceptor() && 
     !map->has_hidden_prototype()) { 
     return isolate->heap()->false_value(); 
    } 
    // Slow case. 
    return HasOwnPropertyImplementation(isolate, Handle<JSObject>(js_obj), 
             Handle<Name>(key)); 
    } else if (object->IsString() && key_is_array_index) { 
    // Well, there is one exception: Handle [] on strings. 
    Handle<String> string = Handle<String>::cast(object); 
    if (index < static_cast<uint32_t>(string->length())) { 
     return isolate->heap()->true_value(); 
    } 
    } else if (object->IsJSProxy()) { 
    Maybe<bool> result = 
     JSReceiver::HasOwnProperty(Handle<JSProxy>::cast(object), key); 
    if (!result.IsJust()) return isolate->heap()->exception(); 
    return isolate->heap()->ToBoolean(result.FromJust()); 
    } 
    return isolate->heap()->false_value(); 
} 

To node.js Github repository. Jeśli wiesz, czego szukać i masz dość cierpliwości, by przebić się przez wszystkie trafienia wyszukiwania, możesz zazwyczaj znaleźć wszystko, czego potrzebujesz. Niefortunną rzeczą przy wyszukiwaniu w Githubie jest to, że nie znalazłem żadnego sposobu na usunięcie wszystkich podkatalogów testowych z wyszukiwania, więc skończysz z 95% wyników wyszukiwania w kodzie testowym, a nie w samym kodzie implementacji. Ale z wystarczającą wytrwałością możesz w końcu znaleźć to, czego potrzebujesz.

+0

Przydatne badania. Dzięki za linki. – TGrif

+0

Dodano rzeczywiste fragmenty kodu z V8 do odpowiedzi. – jfriend00

+0

To powinno być oznaczone jako "właściwa" odpowiedź. Ponad i poza... –