JavaScript/Canvas, behöver hjälp

Permalänk

JavaScript/Canvas, behöver hjälp

Sitter och försöker förstå mig på en grej i JS och Canvas som jag inte blir klok på.

Ritar med koden ut en lila quadraticCurve och det är ju inga konstigheter. Koden kommer från ett bidrag till js1k av snubben som driver firefly.nu

<canvas id="c"> </canvas> <div id="hej" style="position:fixed;left:10px;top:10px;color:#000;"></div> <script> var hej = document.getElementById("hej"),r="",i; B = document.getElementById("c"), C = B.getContext('2d'); for(e in C){ C[ e[0] + (e[5]||0) ] = C[e] } B.width=B.height=600; C.bP(); C.mo(86, 282); C.qa(112, 488, 167, 459); C.strokeStyle = "hsla(" + 255 + ",100%," + 50 + "%,.9)"; C.stroke(); for(i=0;i<e.length;i++){ r+=e[i]+""; } hej.innerHTML = r; </script>

Problemet är att jag inte fattar hur C.qa och bP och mo kan motsvara quadraticCureTo, beginPath och moveTo. Det går ju i vanliga fall inte att förkorta dessa.

Jag plockade bort allt onödigt och upptäckte att det är en for...in som gör att det på något sätt är möjligt:

for(e in C){ C[ e[0] + (e[5]||0) ] = C[e] }

Utan detta i koden går det inte att skriva t.ex. bP i stället för beginPath. Det verkar dessutom som att detta kan läggas in i vilken kod som helst så kan man få saker ritade genom förkortningar.

C[e] innehåller värdet 16 (eller 10 på hexadecimal form, eller 00010000 binärt) vilket på något sätt (?) ska motsvara "Do not synchronously decode images - draw what we have" och det är precis vad arrayen e[0-29] innehåller:

e[0]== D e[1]== R e[2]== A e[3]== W e[4]== W e[5]== I e[6]== N e[7]== D e[8]== O e[9]== W e[10]== _ e[11]== A e[12]== S e[13]== Y e[14]== N e[15]== C e[16]== _ e[17]== D e[18]== E e[19]== C e[20]== O e[21]== D e[22]== E e[23]== _ e[24]== I e[25]== M e[26]== A e[27]== G e[28]== E e[29]== S

Varför funkar detta? Vad gör for...in-koden?

Visa signatur

1

Permalänk
Medlem

C är ett 2d-context för canvasen, den har ett antal metoder, och metoderna är även åtkomliga i form av en associativ (namn-baserad) array.
Dvs. om contexten C har en metod C.moveTo, så finns fältet C["moveTo"].

for(e in C)
loopar över alla namn i C-arrayen, där 'e' får värdet av namnet (ex. så kommer e ha värdena "moveTo", "lineTo" osv eftersom C har metoder med motsvarande namn)

C[ e[0] + (e[5]||0) ] = C[e]
Skapar ett nytt fält i arrayen som består av det första tecknet i namnet (e[0]), samt det sjätte tecknet (e[5], eller 0 om det inte finns något sjätte tecken), det nya fältet tilldelas samma värde som tidigare (så C["beginPath"] ger e="beginPath" vilket ger C["bP"]=C["beginPath"], vilket gör att det kommer finnas en ny metod på C, nämnligen C.bP (vilket alltså är samma som C.beginPath)

Hoppas detta gick att förstå.

Visa signatur

The difference between stupidity and genius - the latter has limits

Permalänk
Medlem

[QUOTE=Mats Myntsamlare;11383340]...
Problemet är att jag inte fattar hur C.qa och bP och mo kan motsvara quadraticCureTo, beginPath och moveTo. Det går ju i vanliga fall inte att förkorta dessa.

Jag plockade bort allt onödigt och upptäckte att det är en for...in som gör att det på något sätt är möjligt:

for(e in C){ C[ e[0] + (e[5]||0) ] = C[e] }

Utan detta i koden går det inte att skriva t.ex. bP i stället för beginPath. Det verkar dessutom som att detta kan läggas in i vilken kod som helst så kan man få saker ritade genom förkortningar.
...
Varför funkar detta? Vad gör for...in-koden?[/QUOTE]

Till att börja med: koden är syntaktiskt korrekt men ful och, som du kanske har märkt, inte helt uppenbar. Jag rekommenderar inte att du använder denna metod för att programmera javascript.

Först lite bakgrund: Javascript är ett objektorienterat programmeringsspråk där varje datatyp representeras av ett objekt. Objekt i javascript innehåller nyckel-/värdepar, dvs ett nyckelord eller namn (som representeras av en sträng) och ett värde (sträng, nummer, boolean, funktion etc).
Det for-in-loopen gör är att iterera över alla medlemmar i objektet C. Raden for (e in C) innebär bokstavligen "stega igenom alla tillgängliga medlemmar i objekt C och låt [den globala="globala"] variabeln e hålla den aktuella nyckeln". Eftersom nyckeln i ett objekts nyckel-/värdepar är en sträng kan man plocka bokstäver ur den, vilket är vad uttrycket e[0] + (e[5]||0) gör: Det tar första tecknet och sjätte, eller 0 om det inte finns något sjätte tecken. Resultatet av detta uttryck – en två tecken lång textsträng – används för att sätta ett nytt nyckel-/värdepar på objektet som för tillfället itereras över (vilket får anses vara fult gjort). Värdet för denna nyckel sätts till värdet som C[e] refererar till.

Kort sagt så skapar for-loopen ett två tecken långt alias för alla medlemmar i objektet. Skulle objektet ha flera nyckel-/värdepar med samma tecken på första- och sjätteplats så skriver det nya aliaset över det gamla.

En annan bit info som kan vara intressant i sammanhanget är att C["tomat"] betyder samma sak som C.tomat. Om man har ett objekt 'Katapult' med en funktion 'kastaTomat' kan man alltså nå denna funktion genom antingen Katapult.kastaTomat eller Katapult["kastaTomat"]

Visa signatur

Kom-pa-TI-bilitet

Permalänk

Koden är kanske inte den snyggaste

Trots era utförliga svar så fattar jag ändå inte riktigt tror jag...

Kan man säga att det finns en tvådimensionell array i C då, typ C[e[0-29]]? Är e[0] == D från början eller är det iterationen som har gjort att den blir D?

Hur visste den att det var just bP och inte qz (eller vad som helst) som skulle "länkas" till metoden för beginPath?

Ahh, ja, ni märker kanske att jag inte har gått några javascriptkurser

Visa signatur

1

Permalänk
Medlem
Skrivet av Mats Myntsamlare:

Kan man säga att det finns en tvådimensionell array i C då, typ C[e[0-29]]? Är e[0] == D från början eller är det iterationen som har gjort att den blir D?

Hur visste den att det var just bP och inte qz (eller vad som helst) som skulle "länkas" till metoden för beginPath?

Nja, inte riktigt. C är ett objekt med properties. Varje property kan, som i förra exemplet, nås genom antingen Objekt["property"] eller Objekt.property — det är ingen skillnad mellan dessa två. När du for-in-loopar över ett objekt får du ut namnen på dessa properties. I for (var namn in Objekt) { ... hämtas Objekts properties ut ett och ett och görs tillgänga genom variabeln namn. En propertys namn presenteras som en textsträng, och för att nå propertyns värde använder vi uttrycket Objekt[namn]. Om namn exempelvis innehåller strängen "korv" är Objekt[namn] == Objekt["korv"] eller Objekt.korv. Vi kan INTE skriva Objekt.namn då detta är det samma som Objekt["namn"], vilket inte når samma property.

När vi ser tillbaka till ditt exempel ser vi att for (var e in C) { innebär att e kommer innehålla ett namn på en property i C. Då namn representeras som strängar kan vi nå varje enskild bokstav i namnet genom e[0] ... e[längden på namnet]. För en property med namn "beginPath" är e[0] == "b" och e[5] == "P". C[ e[0] + (e[5]||0) ] = C[e] blir i detta fall C["bP"] = C["beginPath"]. Man lägger till en ny property i C utifrån första och sjätte bokstaven på propertyn som för tillfället "besöks" i for-in-loopen.

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Skrivet av Teknocide:

Nja, inte riktigt. C är ett objekt med properties. Varje property kan, som i förra exemplet, nås genom antingen Objekt["property"] eller Objekt.property — det är ingen skillnad mellan dessa två. När du for-in-loopar över ett objekt får du ut namnen på dessa properties. I for (var namn in Objekt) { ... hämtas Objekts properties ut ett och ett och görs tillgänga genom variabeln namn. En propertys namn presenteras som en textsträng, och för att nå propertyns värde använder vi uttrycket Objekt[namn]. Om namn exempelvis innehåller strängen "korv" är Objekt[namn] == Objekt["korv"] eller Objekt.korv. Vi kan INTE skriva Objekt.namn då detta är det samma som Objekt["namn"], vilket inte når samma property.

När vi ser tillbaka till ditt exempel ser vi att for (var e in C) { innebär att e kommer innehålla ett namn på en property i C. Då namn representeras som strängar kan vi nå varje enskild bokstav i namnet genom e[0] ... e[längden på namnet]. För en property med namn "beginPath" är e[0] == "b" och e[5] == "P". C[ e[0] + (e[5]||0) ] = C[e] blir i detta fall C["bP"] = C["beginPath"]. Man lägger till en ny property i C utifrån första och sjätte bokstaven på propertyn som för tillfället "besöks" i for-in-loopen.

Nu känns det som jag fattar! Tack för förklaringen

Visa signatur

1