Jak działa ten zaciemniony JavaScript?


93

Jak działa poniższy JavaScript?

Rozumiem, że to zminimalizowany kod. Próbowałem trochę rozjaśnić to, ale nie mogę uzyskać jasnej koncepcji, w jaki sposób osiąga ten efekt. Widzę, że używa ciągów znaków do jakiejś iteracji, użycia obiektu Date, dziwnej manipulacji na ciągach, funkcji matematycznych, a następnie kod drukuje sam.

Jak można przepisać ten sam efekt na minimalnym przykładzie?

eval(z='p="<"+"pre>"/* ,.oq#+     ,._, */;for(y in n="zw24l6k\
4e3t4jnt4qj24xh2 x/* =<,m#F^    A W###q. */42kty24wrt413n243n\
9h243pdxt41csb yz/* #K       q##H######Am */43iyb6k43pk7243nm\
r24".split(4)){/* dP      cpq#q##########b, */for(a in t=pars\
eInt(n[y],36)+/*         p##@###YG=[#######y */(e=x=r=[]))for\
(r=!r,i=0;t[a/*         d#qg `*PWo##q#######D */]>i;i+=.05)wi\
th(Math)x-= /*        aem1k.com Q###KWR#### W[ */.05,0>cos(o=\
new Date/1e3/*      .Q#########Md#.###OP  A@ , */+x/PI)&&(e[~\
~(32*sin(o)*/* ,    (W#####Xx######.P^     T % */sin(.5+y/7))\
+60] =-~ r);/* #y    `^TqW####P###BP           */for(x=0;122>\
x;)p+="   *#"/* b.        OQ####x#K           */[e[x++]+e[x++\
]]||(S=("eval"/* l         `X#####D  ,       */+"(z=\'"+z.spl\
it(B = "\\\\")./*           G####B" #       */join(B+B).split\
(Q="\'").join(B+Q/*          VQBP`        */)+Q+")//m1k")[x/2\
+61*y-1]).fontcolor/*         TP         */(/\\w/.test(S)&&"#\
03B");document.body.innerHTML=p+=B+"\\n"}setTimeout(z)')//

JSFiddle


8
Fajna animacja ... może faktycznie gdzieś to się skończy!
tymeJV

7
Och, fajnie. Nie zauważyłem skrzypiec.
ThiefMaster

37
Nazywa się Quine i jest to jeden z bardziej fantastycznych Quine, jakie kiedykolwiek widziałem. en.wikipedia.org/wiki/Quine_(computing)
David Souther

9
@Roko C. Buljan Myślę, że to jego strona: aem1k.com
Alexander

5
Wygląda na to, że autor umieścił teraz wersję z adnotacjami na GitHub.
Der Hochstapler

Odpowiedzi:


67

Przedmowa : Upiększyłem kod i obszernie opatrzyłem go adnotacjami na http://jsfiddle.net/WZXYr/2/

Rozważ najbardziej zewnętrzną warstwę:

eval(z = '...');

W zmiennej przechowywany jest ciąg kodu z. Operator przypisania zwraca przypisaną wartość, więc ciąg kodu jest również przekazywany jako argument do eval.

Ciąg kodu zbiegnie wewnątrz eval. Kod jest wyjątkowo tępy, nawet po wyczyszczeniu, ale wygląda na to, że:

  1. Przeanalizuj ciąg składający się z 36 liczb o podstawie 36, oddzielonych znakiem 4.
  2. Wypełnij mapę wartości, używając zmiennych globalnych e , xoraz ydo utrzymywania stanu mapy. Stan mapy jest częściowo funkcją bieżącej sekundy na zegarze ściennym ( new Date / 1e3).
  3. Korzystając z wartości mapy, kod generuje ciąg wyjściowy, p
    • kod używa p += " *#"[index]do podjęcia decyzji, czy użyć spacji, gwiazdki czy znaku krzyżyka, gdzie indexfaktycznie jeste[x++] + e[x++] (jak wspomniano powyżej ei xjest odpowiedzialny za stan mapy)
    • jeśli indeks jest większy niż długość " *#", istnieje kod rezerwowy, który wypełnia dane wyjściowe pznakami z z. Znaki wewnętrzne są zapełniane postaciami animacji, podczas gdy znaki zewnętrzne są pobierane zz .

Na końcu kodu jest wywołanie setTimeout(z) , które asynchronicznie ocenia ciąg kodu z. To powtórzenie wywołania zumożliwia zapętlenie kodu.

Prosty przykład:

Oto bardzo prosta wersja ( http://jsfiddle.net/5QXn8/ ):

eval(z='p="<"+"pre>";for(i=0;i<172;++i)if(i > 62 && i < 67)p+="!---"[~~(new Date/1e2 + i)%4];else p += ("eval(z=\'" + z + "\')")[i];document.body.innerHTML = p;setTimeout(z)')
  1. forPętli dodaje każda postać do łańcucha wyjściowego p(ciąg jest długie 172 znaków):

    for(i=0;i<172;++i)
    
  2. Warunek wewnętrzny decyduje, czy jesteśmy na postaci między pozycjami 62 a 67, którą są animowane postacie:

    if(i > 62 && i < 67)
    
  3. Jeśli tak, to wydrukuj z !---przesunięciem o dziesiątą część drugiej wartości zegara ściennego. Zapewnia to efekt animacji.

    p+="!---"[~~(new Date/1e2 + i)%4]
    

    (Cała nieprzyjemność dookoła new Datejest tak naprawdę po to, aby przekształcić wartość daty w liczbę od 0 do 3).

  4. W przeciwnym razie, jeśli nie jesteśmy na animowanym znaku, wypisz iznak indeksu z ciągu zdefiniowanego przez

    "eval(z='" + z + "')"
    

    Oznacza to, że ciąg kodu zotoczony znakami eval('i ').

  5. Na koniec wypisz ciąg i użyj setTimeoutdo kolejkowania kolejnego wykonania z:

    document.body.innerHTML = p;setTimeout(z)
    

Zauważ, że moje ostatnie wyjście jest nie całkiem w porządku - ja nie stanowiły ukośniki pod koniec - ale powinna nadal daje całkiem dobry pomysł, w jaki sposób technika działa w ogóle.


8
Zwróć uwagę na ten github.com/aemkei/world/blob/master/annotated.js - własną wersję z adnotacjami autora w GitHub.
Benjamin Gruenbaum

36

Oto źródło z adnotacjami. Ps: Jestem autorem;)

function z(){                     // will be replaced with eval

  p = "<" + "pre>";               // use <pre> tag for formatted output

  for (                           // loop though lines
    y in n = (                    // y - the line number
      "zw24"      +               // n - the encoded data
      "l6k4"      +               // every line holds encoded data
      "e3t4"      +
      "jnt4"      +               // string will be concated in build process
      "qj24"      +
      "xh2  4"    +               // data after spaces will be ignored but
      "2kty24"    +               // … is used to not break block comments
      "wrt4"      +               // … which will save some chars
      "13n24"     +
      "3n9h24"    +
      "3pdxt4"    +
      "1csb   4"  +
      "3iyb6k4"   +
      "3pk724"    +
      "3nmr24"
    ).split(4)                    // data will be split by (unused) 4

  ){
    for (                         // loop throug every char in line
      a in t = parseInt(          // numbers are encoded as string
        n[y],                     // … with a base of 36
        36
      ) + (                       // large number will be converted to string
        e =                       // e - holds the rendered globe
        x =                       // x - horizonal position
        r = []                    // r - bitmap flag if pixel is set
      )
    ){
      r = !r;                     // toggle binary flag

      for (                       // look though bitmap states
        i = 0;                 
        t[a] > i;                 // draw pixel t[a]-times
        i += .05
      )
        with (Math)               // refer to Math later
          x -= .05,
          0 > cos(                // prevent backface visibility
            o =
              new Date / 1e3 +    // get rotation based on current time
              x / PI
          ) && (
            e[                    // access matrix
              ~~(                 // convert float to integer
                sin(o) *          // rotate around y axis
                sin(.5 + y/7) *
                32                // scale up the globe
              ) + 60              // move to center
            ] = -~r               // store bitmap state in render matrix
          )
    }

    for (                         // loop through columns
      x = 0;
      122 > x;                    // break after char 122
    ) p += "   *#"[               // add space, asterisk or hash
        e[x++] +                  // … based pixel opacity
        e[x++]
      ] || (S = (                 // otherwise use the original code
        "eval(z='" +              // inception of missing "eval" statement
          z
            .split(B = "\\")      // escape \ with \\
            .join(B + B)

            .split(Q = "'")       // escape ' with \'
            .join(B + Q) +

          Q +                     // add missing ')

          ")////////"             // add extra chars to fill mapping
        )[
          x / 2 +                 // get character at current position
          61 * y-1
        ]

      ).fontcolor(                // colorize outpu
        /\w/.test(S) &&           // test for [0-9A-Z]
        "#03B"                    // render blue
                                  // otherwise pink (default)
      );

    document.body.innerHTML =     // render output
      p +=                        // append new line
      B +                         // add backspace
      "\n";                       // add new line
  }

  setTimeout(z)                   // render animation on next frame
}
z()

5
Uwaga, jest to również wyjaśnione w tym filmie youtube.com/watch?v=RTxtiLp1C8Y
Benjamin Gruenbaum

21

Oto kolejna ręcznie odszyfrowana wersja, przenosząca całą inicjalizację z wyrażenia do własnych instrukcji:

z='p="<"+"pre>"/* ,.oq#+     ,._, */;for(y in n="zw24l6k\
4e3t4jnt4qj24xh2 x/* =<,m#F^    A W###q. */42kty24wrt413n243n\
9h243pdxt41csb yz/* #K       q##H######Am */43iyb6k43pk7243nm\
r24".split(4)){/* dP      cpq#q##########b, */for(a in t=pars\
eInt(n[y],36)+/*         p##@###YG=[#######y */(e=x=r=[]))for\
(r=!r,i=0;t[a/*         d#qg `*PWo##q#######D */]>i;i+=.05)wi\
th(Math)x-= /*        aem1k.com Q###KWR#### W[ */.05,0>cos(o=\
new Date/1e3/*      .Q#########Md#.###OP  A@ , */+x/PI)&&(e[~\
~(32*sin(o)*/* ,    (W#####Xx######.P^     T % */sin(.5+y/7))\
+60] =-~ r);/* #y    `^TqW####P###BP           */for(x=0;122>\
x;)p+="   *#"/* b.        OQ####x#K           */[e[x++]+e[x++\
]]||(S=("eval"/* l         `X#####D  ,       */+"(z=\'"+z.spl\
it(B = "\\\\")./*           G####B" #       */join(B+B).split\
(Q="\'").join(B+Q/*          VQBP`        */)+Q+")//m1k")[x/2\
+61*y-1]).fontcolor/*         TP         */(/\\w/.test(S)&&"#\
03B");document.body.innerHTML=p+=B+"\\n"}setTimeout(z)';

p = "<" + "pre>";
n = ["zw2", "l6k", "e3t", "jnt", "qj2", "xh2 x/* =<,m#F^    A W###q. */", "2kty2", "wrt", "13n2", "3n9h2", "3pdxt", "1csb yz/* #K       q##H######Am */", "3iyb6k", "3pk72", "3nmr2", ""]
for (y in n) {
    e = [];
    x = 0;
    r = true;
    t = parseInt(n[y], 36) + "";
    for (a in t) {
        r = !r
        for (i = 0; i < t[a]; i += 0.05) {
             x -= 0.05;
             o = new Date / 1e3 + x / Math.PI
             if (Math.cos(o) < 0)
                 e[~~(32 * Math.sin(o) * Math.sin(0.5 + y / 7)) + 60] = -~r;
        }
    for (x = 0; x < 122;) {
        S = "eval" + "(z='" + z.split(B = "\\").join(B + B).split(Q = "'").join(B + Q) + Q + ")//m1k"
        p += "   *#"[e[x++] + e[x++]] || S[x/2+61*y-1]).fontcolor(/\w/.test(S[x/2+61*y-1]) && "#03B");
    }
    p += B + "\n";
    document.body.innerHTML = p;
}
setTimeout(z)

Oto, co się dzieje:

  • zto wielowierszowy ciąg zawierający cały kod. To jest evaled.
  • Na końcu kodu zjest przekazywany do setTimeout. Działa jak requestAnimationFrameieval razem, oceniając to w interwale z najwyższą możliwą szybkością.
  • Kod inicjuje się p, bufor ciągu, do którego zostanie dołączony kod HTML, oraz ntablica liczb zakodowanych w formacie base-36 (połączonych w łańcuch przez "4", komentarze są nieistotnymi śmieciami, które nie są uwzględniane przez parseInt).
  • każda liczba w nkoduje jedną linię ( n.length == 16). Jest teraz wyliczone .
  • Inicjalizowana jest grupa zmiennych, niektóre zamaskowane jako eliterały tablicowe, ale są one następnie rzutowane na liczby ( x) lub booleans ( r) lub stringi ( t), gdy są używane.
  • Każda cyfra w numerze tjest wyliczana, odwracając wartość logiczną w rkażdej turze. Dla różnych kątów xiw zależności od aktualnego czasu new Date / 1000 (tak, aby dała animację), tablica ejest wypełniana za pomocą operatorów bitowych - gdzie 1kiedy rjest fałszem i 2s kiedyr jest prawdziwe w tym czasie.
  • Następnie pętla iteruje 61 kolumn obrazu, od x=0do 122 w podwójnych krokach, dodając pojedyncze znaki do p.
  • Bbędąc ukośnikiem odwrotnym, łańcuch Sjest tworzony z ciągu kodu zprzez unikanie ukośników odwrotnych i apostrofów, aby uzyskać dokładną reprezentację tego, jak wyglądał w źródle.
  • Co dwie kolejne cyfry od esą dodawane i używane w celu uzyskania dostępu do postaci z " *#", aby zbudować animowany obraz. Jeśli jeden z indeksów nie jest zdefiniowany, NaNindeks jest zastępowany niezdefiniowanym znakiem, a zamiast tego Spobierany jest odpowiedni znak z ciągu (sprawdź wzór x/2+61*y-1). Jeśli ten znak powinien być znakiem słowa , jest różnie kolorowany przy użyciu fontcolormetody String .
  • Po każdym wierszu końcowa spacja i znak podziału wiersza są dodawane p, a ciąg HTML zostaje przypisany do treści dokumentu.

Jak można przepisać ten sam efekt na minimalny przykład?

Oto inny przykład:

setInterval(z='s=("setInterval(z=\'"+\
z.replace(/[\\\\\']/g,"\\\\$&")+"\')"\
).match(/.{1,37}/g).join("\\\\\\n");d\
ocument.body.innerHTML=\"<\\pre>"+s.s\
lice(0, 175)+String( + new Date()).fo\
ntcolor("red")+s.slice(188)')

( demo na jsfiddle.net )

Zawiera wszystkie istotne rzeczy potrzebne do tego rodzaju animacji:

  • setInterval i Date do animacji
  • Rekonstrukcja własnego kodu ( podobna do quine ), tutaj:

    s = ( "setInterval(z='" // the outer invokation
          + z.replace(/[\\\']/g,"\\$&") // the escaped version
        + "\')" ) // the end of the assignment
        .match(/.{1,37}/g).join("\\\n"); // chunked into lines
    
  • Dane wyjściowe via document.body.innerHTMLi <pre>element

  • Zastąpienie niektórych części kodu animowanym ciągiem znaków

2
Muszę przyznać, świetna odpowiedź!
rafaelcastrocouto

5

Oceniany jest łańcuch zawierający cały kod, a pętla jest wykonywana przez przekroczenie limitu czasu; Ciąg jest przechowywany w zmiennej o nazwie zi pośrodku kodu, między komentarzami /*i */znajduje się tam „Earth ASCII Art”. Kod analizuje komentarze i zmienia zawartość dokumentu, zachowując js i aktualizując grafikę. Poniżej znajduje się tylko fragment kodu:

  p="<pre>";
  for(y in n="zw24l6k4e3t4jnt4qj24xh2 x42kty24wrt413n243n9h243pdxt41csb yz43iyb6k43pk7243nmr24".split(4)){ 
    for(a in t = parseInt(n[y],36)+(e=x=r=[]))
      for(r=!r,i=0;t[a]>i;i+=.05)
        with(Math) x-= .05,0>cos(o=new Date/1e3+x/PI)&&(e[~~(32*sin(o)*sin(.5+y/7))+60] =-~ r);
          for(x=0;122>x;) p += "   *#"[e[x++]+e[x++\]] ||
              (S=("eval"+"(z=\'"+z.split(B = "\\\\").join(B+B).split(Q="\'").join(B+Q)+Q+")//m1k")[x/2+61*y-1]).fontcolor(/\\w/.test(S)&&"#\03B");
    p += B+"\\n"
    document.body.innerHTML= p
  }

6
Tak czy inaczej, niewiarygodne, jak grafika wokół równika ma większy obrót ... niesamowite. +1 BTW
Roko C. Buljan
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.