2010-04-11 8 views
7

Chciałem, aby moje instrukcje rejestrowania były jak najkrótsze, jednocześnie uniemożliwiając dostęp do konsoli, gdy ta nie istnieje; I wymyślił następujące rozwiązanie:Przypisywanie pliku console.log do innego obiektu (wydanie Webkit)

var _ = {}; 
if (console) { 
    _.log = console.debug; 
} else { 
    _.log = function() { } 
} 

Dla mnie to wydaje się dość elegancki i działa doskonale w Firefoksie 3.6 (w tym zachowania numery linii, które czynią console.debug bardziej użyteczne niż console.log). Ale nie działa w Safari 4. [Aktualizacja: lub w Chrome. Tak więc problem wydaje się być różnica między Firebug i konsoli Webkit] Jeśli zastosujemy się do powyższych z

console.debug('A') 
_.log('B'); 

pierwsza wypowiedź działa dobrze w obu przeglądarkach, ale drugi generuje. „Błąd typu: typ błędu” w Safari. Czy to tylko różnica między tym, jak Firebug i Safari Web Developer Tools implementują konsolę? Jeśli tak, to jest BARDZO denerwujące na części Apple's Webkit's. Powiązanie funkcji konsoli z prototypem, a następnie tworzenie instancji zamiast wiązania jej bezpośrednio z obiektem, nie pomaga.

Mogę, oczywiście, zadzwonić pod numer console.debug z anonimowej funkcji przypisanej do _.log, ale wtedy stracę numery linii. Jakieś inne pomysły?

+0

Jest to cecha WebKit, a nie bug ;-) https://bugs.webkit.org/show_bug.cgi?id=20141 –

+0

Powiązane: http://stackoverflow.com/questions/14146316/why-does-scope-reduction-in-safari-break-existing-code – MvG

Odpowiedz

8

Po pierwsze, jeśli console jest rzeczywiście niezdefiniowany (jak to jest w przeglądarkach takich jak IE), pojawi się błąd. Powinieneś to sprawdzić jako właściwość obiektu globalnego, który w przeglądarkach jest window. Dobrym pomysłem jest także przetestowanie funkcji przed jej użyciem, dlatego dodałem test dla metody debug.

Prawdopodobnie realizacja console.debug w Safari podnosi jego wartość this jest odniesienie do console, który nie będzie w przypadku, jeśli nazywać go używając _.log (this będzie zamiast być odniesienie do _). Zrobiwszy szybki test, to wydaje się być przypadek i następujące rozwiązuje problem:

var _ = {}; 
if (typeof window.console != "undefined" 
     && typeof window.console.debug == "function") { 
    _.log = function() { 
     window.console.debug.apply(window.console, arguments); 
    } 
} else { 
    _.log = function() { } 
} 
+3

Tak, to podejście konwencjonalne, ale ma poważną wadę: traci linię liczby. Jeśli wywołasz funkcję _.log() z dowolnego miejsca w aplikacji, konsola zgłosi to wyjście jako generowane z funkcji _.log(), a nie z oryginalnego źródła. Tak więc traci się korzyści z używania konsoli console.debug() zamiast console.log(). –

+2

W takim przypadku, czy nie byłoby łatwiej po prostu użyć "konsoli" bez żadnego aliasu lub opakowania, i po prostu zdefiniuj ją za pomocą metody pośredniczącej, jeśli nie istnieje? Na przykład: 'if (typeof window.console! =" Undefined ") {window.console = {debug: function() {}}}' itp. –

+0

Myślę, że masz rację, Tim (chociaż masz na myśli okno 'typeof .console == "niezdefiniowane" 'zamiast'! = ', prawda?). Być może powinienem zrezygnować z moich poszukiwań zwięzłości na rzecz właśnie przydzielenia w razie potrzeby konsoli dummy. –

0

Szukałem na rozwiązanie tego sam (jest to w jaki sposób znalazłem swoje pytanie).

Jak Tim wskazał, przeglądarki WebKit (Safari, Chrome) polegać na this będąc console w tym przypadku. Firefox jednak tego nie robi. Tak więc w FF można zmienić przypisanie funkcji i zachować numery linii (w przeciwnym razie wszystkie dzienniki wyglądają tak, jakby pochodziły z funkcji rejestrowania, co nie jest zbyt użyteczne). Najlepszym sposobem sprawdzenia, która przeglądarka jest, jest wykonanie i sprawdzenie wyniku. Oto jak to sprawdzić (w coffeescript):

# Check to see if reassigning of functions work 
f = console.log 
assignSupported = true 
try 
    f('Initializing logging...') 
catch e 
    assignSupported = false 

Później, po dokonaniu funkcje sprawdzić assignSupported i działać odpowiednio:

levels = 
    ERROR: 1 
    WARN: 2 
    LOG: 3 
    INFO: 4 
    DEBUG: 6 

log.setLevel = (newLevel) -> 
    for label, level of levels 
    if level > newLevel # Skip low levels 
     continue 

    name = label.toLowerCase() 
    f = -> # Fallback - empty function. In Js: var f = function() {} 
    if console?[name] 
     if assignSupported 
     f = console[name] # Wee, we'll have line numbers. 
     else 
     # Webkit need the this of console.log (namely, console) 
     # preserved, so we use a wrapper. 
     # 
     # Calling console[name] within the returned wrapper 
     # makes [name] a subject of the closure, meaning 
     # that it's the last value in the iteration - 
     # we need to preserve it. 
     f = ((n) -> 
      return (-> console[n].apply(console, arguments)))(name) 
    log[name] = f 

log.setLevel levels.DEBUG 

Linie:

f = ((n) -> 
    return (-> console[n].apply(console, arguments)))(name) 

może wyglądać trochę dziwny. Jest tak dlatego, że name jest zmienną pętli i jest leksykalnie związana, co oznacza, że ​​zostanie użyta wartość w momencie wykonania, która zawsze będzie ostatnią wartością level.Kompiluje do tego javascript (w przypadku jest to łatwiejsze do odczytania):

f = (function(n) { 
    return (function() { 
    return console[n].apply(console, arguments); 
    }); 
})(name); 
Powiązane problemy