2012-09-30 16 views
10

Mam dość często opcjonalne argumenty w funkcjach, ale niektóre testy pokazują ogromną skuteczność w firefoxie i safari (70-95%). O dziwo, jeśli przejdę do wartości literalnej undefined, wówczas nie ma kary. Co może się tutaj wydarzyć? Nie sądziłbym, że jest to kwestia łańcucha zasięgu, ponieważ są one z natury lokalne dla funkcji. Czy mogę zacząć przekazywać undefined do każdego opcjonalnego argumentu?Kara umowna za niezdefiniowane argumenty

jsPerf: http://jsperf.com/function-undefined-args/2

+5

@MattWhipple - To interesujący post, ale co to ma wspólnego z pytaniem OP? (Dobre pytanie, przy okazji, @robC.) –

+0

To interesujące znalezisko.Zanim zmienisz cały kod, aby wypełnić wszystkie argumenty, sprawdziłbym, czy istnieje otwarty raport o błędzie dla tych implementacji JS, a jeśli nie, to go. –

+0

Zdecydowanie podejrzewam, że środowisko wykonawcze ustawia funkcje, gdy jest dopasowany parametr licznika, ale działa funkcja jako faktyczne wywołanie funkcji w inny sposób. Fakt, że nic nie wykorzystuje żadnej wartości zwracanej przez funkcję, może oznaczać, że środowisko wykonawcze * nie robi nic * po pełnym wprowadzeniu parametrów. – Pointy

Odpowiedz

5

Dla funkcji takich jak to:

function threeArgs(x, y, z) { 
    return x + y + z; 
} 

że nazywa się tak:

threeArgs(1, 2, 3); 

optymalizator może swobodnie dokonać wyboru, do generowania kodu w ogóle nie ma. Dość łatwo jest stwierdzić, że nie ma żadnych skutków ubocznych, ponieważ funkcja po prostu odwołuje się do wartości parametrów i zwraca wynik prostego wyrażenia. Ponieważ zwracana wartość jest ignorowana, nie ma powodu, aby środowisko wykonawcze cokolwiek robiło.

poza tym, jeśli kod był:

something += threeArgs(1, 2, 3); 

optymalizator może zdecydować, aby wygenerować kod grubsza odpowiada:

something += 6; 

Dlaczego? Ponieważ wywołanie zostało wykonane z liczbowych stałych i może bezpiecznie spasować je w czasie generowania kodu. Może to być konserwatywne, ponieważ liczby są dziwne, ale tutaj wszystkie są liczbami całkowitymi, więc może to zrobić. Nawet jeśli nie, to może bezpiecznie inline funkcję:

something += 1 + 2 + 3; 

Kiedy pojawia się parametr brakuje, jednak może się okazać, że optymalizujące wyskoczyć i generują prawdziwe wywołanie funkcji. W przypadku takiej prostej funkcji obciążenie związane z wywołaniem funkcji może łatwo uwzględniać dużą różnicę w wydajności.

Używając zmiennych zamiast stałych w teście, i faktycznie używając zwracanej wartości funkcji, można "pomylić" optymalizator i powstrzymać go od pominięcia połączenia lub wstępnego obliczenia wyniku, ale można " t nie umieszczaj go w pozycji inline. Nadal uważam, że twój wynik jest interesujący z tego powodu: ujawnia to, że (od dzisiaj) ci optymiści są wrażliwi na sposób wywoływania funkcji.

+2

To wydaje się wiarygodne wyjaśnienie, ale skąd wiemy, że tak naprawdę to się dzieje? – jfriend00

+0

Rezultaty dla FF i najnowszego Chrome są tak daleko przed konkurencją, że tylko w rzeczywistości można ją ograniczyć do optymalizacji. Wywołanie funkcji, które niesie karę, jest w rzeczywistości równe tej samej wydajności, co inne niezoptymalizowane przeglądarki. – robC

+0

"Jeśli brakuje jakiegoś parametru, możliwe, że optymalizatorzy dostaną pieniądze" - czy mógłbyś wyjaśnić, dlaczego tak się dzieje? Jeśli brakuje argumentu, to jest on niezdefiniowany, więc wydaje mi się, że po prostu wstawił "1 + 2 + niezdefiniowany". – pimvdb

2

I pomyśleć, co mogłoby wyjaśnić różnica wydajności jest sposób argumenty są przekazywane do obiektu funkcji: za pośrednictwem obiektu arguments. Gdy nie przekazujemy żadnych argumentów, JS rozpocznie skanowanie obiektu argumentów dla dowolnego z podanych argumentów, gdy te są niezdefiniowane, łańcuch prototypów zostanie przeskanowany, aż do Object.prototype. Jeśli te wszystkie nie mają pożądanej właściwości, JS zwróci undefined. Zważywszy, przechodząc nieokreślone wyraźnie, ustawia go jako właściwość bezpośrednio na argumentach obiektu:

function foo(arg) 
{ 
    console.log(arguments.hasOwnProperty('0')); 
} 
foo();//false' 
foo('bar');//true 
foo(undefined);//true 

wnoszę, że jest powód, dlaczego przechodząc nieokreślone wyraźnie wydaje się być szybsze.

+4

To interesujący pomysł, ale wydaje mi się, że ponieważ symbol "arg" jest zadeklarowany na liście parametrów formalnych, zawsze * będzie lokalnym odnośnikiem. – Pointy

+0

Być może, ale zmiana funkcji na 'console.log (arg === argumenty [0]);' i wywołanie 'foo (new Date())' loguje true, więc oba odnoszą się dokładnie do tej samej rzeczy. Powiedziałbym, że args jest lokalnym odwołaniem do właściwości lokalnego obiektu 'arguments'. Odsuwając skanowanie na bok, prototypowe argumenty wciąż istnieją, prawda? –

+2

O tak, zgadzam się z tym - obiekt 'arguments' jest dziwaczny. Nie jestem pewien, jak dokładnie wpłynęłoby to na to, o co mi chodziło. Innymi słowy, ponieważ "arg" jest parametrem formalnym, to jego wartością jest * zawsze * wartość 'argumentów [0]', niezależnie od tego, czy jest to wartość 'niezdefiniowana' czy nie. – Pointy

Powiązane problemy