JavaScript ที่สับสนนี้ทำงานอย่างไร


93

JavaScript ต่อไปนี้ทำงานอย่างไร

ฉันเข้าใจว่าเป็นรหัสย่อ ฉันพยายามขจัดความสับสนเล็กน้อย แต่ฉันไม่สามารถเข้าใจแนวคิดที่ชัดเจนว่ามันบรรลุผลนี้ได้อย่างไร ฉันเห็นว่ามันใช้ Strings สำหรับการวนซ้ำบางประเภทใช้วัตถุ Date การปรับแต่งสตริงแปลก ๆ ฟังก์ชันคณิตศาสตร์แล้วโค้ดก็พิมพ์ออกมาเอง

จะเขียนเอฟเฟกต์เดียวกันใหม่ด้วยตัวอย่างขั้นต่ำได้อย่างไร

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
แอนิเมชั่นเจ๋ง ๆ ... อาจจะจบลงด้วยการใช้มันที่ไหนสักแห่ง!
tymeJV

7
ดี. ไม่ทันสังเกตซอ.
ThiefMaster

37
นี่เรียกว่า Quine และนี่เป็นหนึ่งใน Quine ที่ยอดเยี่ยมกว่าที่ฉันเคยเห็น en.wikipedia.org/wiki/Quine_( การคำนวณ)
David Souther

9
@Roko C.Buljan ฉันคิดว่านี่คือเพจของเขา: aem1k.com
Alexander

5
ดูเหมือนว่าผู้เขียนได้วางเวอร์ชันที่มีคำอธิบายประกอบไว้ใน GitHub แล้ว
Der Hochstapler

คำตอบ:


67

คำนำ : ฉันตกแต่งและใส่คำอธิบายประกอบโค้ดอย่างกว้างขวางที่http://jsfiddle.net/WZXYr/2/

พิจารณาชั้นนอกสุด:

eval(z = '...');

zสตริงรหัสถูกเก็บไว้ในตัวแปร ตัวดำเนินการกำหนดส่งคืนค่าที่กำหนดดังนั้นสตริงรหัสจึงถูกส่งเป็นอาร์กิวเมนต์ไปevalด้วย

สตริงรหัสทำงานภายในของz evalรหัสมีลักษณะป้านมากแม้ว่าจะถูกล้างข้อมูล แต่ดูเหมือนว่าจะ:

  1. แยกสตริงของฐาน-36 4ตัวเลขเบี่ยงตัวอักษร
  2. เติมแผนที่ของค่าใช้ตัวแปรทั่วโลกe, xและyการระงับรัฐแผนที่ สถานะแผนที่เป็นส่วนหนึ่งของฟังก์ชันของวินาทีปัจจุบันบนนาฬิกาแขวน ( new Date / 1e3)
  3. โดยใช้ค่าแผนที่รหัสจะสร้างสตริงเอาต์พุต p
    • รหัสใช้p += " *#"[index]ในการตัดสินใจว่าจะใช้ช่องว่างเครื่องหมายดอกจันหรือเครื่องหมายแฮชที่indexจริงe[x++] + e[x++](ตามที่กล่าวไว้ข้างต้นeและxรับผิดชอบต่อสถานะแผนที่)
    • หากดัชนีมีขนาดใหญ่กว่าความยาวของ" *#"มีรหัสสำรองที่ populates การส่งออกที่มีตัวละครจากp ตัวละครภายในมีประชากรที่มีตัวอักษรภาพเคลื่อนไหวในขณะที่ตัวละครนอกที่ดึงมาจากzz

ในตอนท้ายของรหัสที่มีการเรียกร้องให้ซึ่งถ่ายทอดสดประเมินสตริงรหัสsetTimeout(z) zการเรียกใช้ซ้ำนี้zช่วยให้โค้ดวนซ้ำ

ตัวอย่างง่ายๆ:

นี่คือเวอร์ชันที่เรียบง่ายสุด ๆ ( 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. forห่วงเพิ่มตัวละครสตริงออกแต่ละที่p(สตริงเป็น 172 ตัวอักษร):

    for(i=0;i<172;++i)
    
  2. เงื่อนไขภายในจะตัดสินว่าเราอยู่ในตัวละครระหว่างตำแหน่ง 62 ถึง 67 ซึ่งเป็นตัวละครเคลื่อนไหว:

    if(i > 62 && i < 67)
    
  3. ถ้าเป็นเช่นนั้นให้พิมพ์ออก!---โดยเลื่อนตามค่าที่สิบของค่านาฬิกาแขวนอันที่สอง สิ่งนี้ให้เอฟเฟกต์ภาพเคลื่อนไหว

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

    (ความน่ารังเกียจทั้งหมดอยู่ที่new Dateนั่นจริงๆเพื่อเปลี่ยนค่าวันที่เป็นตัวเลขระหว่าง 0 ถึง 3)

  4. มิฉะนั้นหากเราไม่ได้ใช้อักขระที่เคลื่อนไหวให้พิมพ์iอักขระดัชนีจากสตริงที่กำหนดโดย

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

    นั่นคือสตริงรหัสzล้อมรอบด้วยและeval('')

  5. สุดท้ายส่งออกสตริงและใช้setTimeoutเพื่อจัดคิวการดำเนินการอื่นของz:

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

โปรดทราบว่าผลลัพธ์สุดท้ายของฉันไม่ได้ค่อนข้างขวา - ฉันไม่ได้คิดเป็นเครื่องหมายในช่วงท้ายที่ - แต่ก็ยังควรให้ความคิดที่ดีงามของวิธีการเทคนิคการทำงานโดยทั่วไป


8
โปรดสังเกตgithub.com/aemkei/world/blob/master/annotated.jsซึ่งเป็นเวอร์ชันที่มีคำอธิบายประกอบของผู้เขียนเองใน GitHub
Benjamin Gruenbaum

36

นี่คือแหล่งที่มาของคำอธิบายประกอบ Ps: ฉันเป็นคนเขียน;)

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
หมายเหตุมีอธิบายไว้ในวิดีโอyoutube.com/watch?v=RTxtiLp1C8Y
Benjamin Gruenbaum

21

นี่คืออีกเวอร์ชันหนึ่งที่ถูกลดความยุ่งเหยิงด้วยตนเองโดยย้ายการเริ่มต้นทั้งหมดออกจากนิพจน์เป็นคำสั่งของตัวเอง

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)

นี่คือสิ่งที่เกิดขึ้น:

  • zเป็นสตริงหลายบรรทัดที่มีรหัสทั้งหมด มันเป็นevalเอ็ด
  • ในตอนท้ายของรหัสที่ถูกส่งไปยังz setTimeoutทำงานเหมือนrequestAnimationFrameและevalร่วมกันประเมินเป็นช่วง ๆ ในอัตราสูงสุดที่เป็นไปได้
  • โค้ดเริ่มต้นตัวเองpสตริงบัฟเฟอร์ที่จะต่อท้าย HTML และnอาร์เรย์ของตัวเลขที่เข้ารหัสฐาน 36 (รวมเข้ากับสตริงโดย"4"ความคิดเห็นเป็นขยะที่ไม่เกี่ยวข้องซึ่งไม่ได้รับการพิจารณาparseInt)
  • แต่ละหมายเลขในnเข้ารหัสหนึ่งบรรทัด ( n.length == 16) ตอนนี้มันเป็นแจกแจง
  • ตัวแปรหลายตัวถูกเริ่มต้นบางตัวปลอมตัวเป็นลิเทอรัลของeอาร์เรย์ แต่จะถูกส่งไปยังตัวเลข ( x) หรือบูลีน ( r) หรือสตริง ( t) เมื่อใช้
  • แต่ละหลักในตัวเลขtจะถูกแจกแจงโดยจะกลับค่าบูลีนrในแต่ละเทิร์น สำหรับมุมที่แตกต่างกันxและขึ้นอยู่กับเวลาปัจจุบัน new Date / 1000 (เพื่อให้แอนิเมชั่น) อาร์เรย์eจะถูกเติมโดยใช้ตัวดำเนินการระดับบิตโดยที่1เมื่อrเป็นเท็จและ2เมื่อrเป็นจริงในเวลานั้น
  • จากนั้นวงไม่สำทับ 61 คอลัมน์ของภาพจากx=0122 pในขั้นตอนที่สองต่อท้ายตัวอักษรเดียว
  • Bเป็นแบ็กสแลชสตริงSถูกสร้างขึ้นจากสตริงโค้ดzโดยการหลีกเลี่ยงแบ็กสแลชและเครื่องหมายอะพอสทรอฟีเพื่อให้ได้การแสดงที่ถูกต้องของสิ่งที่ดูในแหล่งที่มา
  • ทุก ๆ สองหมายเลขติดต่อกันeจะถูกเพิ่มและใช้เพื่อเข้าถึงอักขระจาก" *#"เพื่อสร้างภาพเคลื่อนไหว หากไม่ได้กำหนดดัชนีอย่างใดอย่างหนึ่งNaNดัชนีจะเปลี่ยนเป็นอักขระที่ไม่ได้กำหนดและใช้อักขระตามลำดับจากSสตริงแทน (ตรวจสอบสูตรx/2+61*y-1) ถ้าตัวละครที่ควรจะเป็นตัวอักษรคำว่ามันเป็นสีที่แตกต่างกันโดยใช้วิธีการสตริงfontcolor
  • หลังจากแต่ละบรรทัดระบบจะเพิ่ม backspace และ linebreak ต่อท้ายและpสตริง HTML จะถูกกำหนดให้กับเนื้อหาของเอกสาร

เอฟเฟกต์เดียวกันนี้สามารถเขียนซ้ำได้อย่างไรสำหรับตัวอย่างเล็กน้อย

นี่คือตัวอย่างอื่น ๆ :

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)')

( สาธิตที่ jsfiddle.net )

มีทุกสิ่งที่เกี่ยวข้องที่คุณต้องการสำหรับแอนิเมชั่นประเภทนี้:

  • setIntervalและDateสำหรับแอนิเมชั่น
  • การสร้างรหัสของตัวเองใหม่ ( quine -like ) ที่นี่:

    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
    
  • เอาต์พุตผ่านdocument.body.innerHTMLและ<pre>องค์ประกอบ

  • การแทนที่โค้ดบางส่วนด้วยสตริงแบบเคลื่อนไหว

2
ต้องยอมรับคำตอบที่ดี!
rafaelcastrocouto

5

สตริงที่มีรหัสทั้งหมดจะได้รับการประเมินและการหมดเวลาทำให้เกิดการวนซ้ำ สตริงจะถูกเก็บไว้ในตัวแปรที่มีชื่อzและอยู่ตรงกลางของโค้ดระหว่างความคิดเห็น/*และ*/มี "Earth ASCII Art" โค้ดจะแยกวิเคราะห์ความคิดเห็นและเปลี่ยนแปลงเนื้อหาของเอกสารเก็บ js และอัปเดตงานศิลปะ การร้องเป็นเพียงโค้ดที่หั่นบาง ๆ :

  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
ไม่ว่าจะด้วยวิธีใดก็ตามที่ไม่น่าเชื่อว่ากราฟิกรอบ ๆ เส้นศูนย์สูตรมีการหมุนมากขึ้น ... +1 BTW
Roko C.Buljan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.