{"id":59,"date":"2010-05-03T14:36:06","date_gmt":"2010-05-03T12:36:06","guid":{"rendered":"https:\/\/alistapart.com\/it\/article\/migliore-riduzione-di-javascript\/"},"modified":"2010-05-03T14:36:06","modified_gmt":"2010-05-03T12:36:06","slug":"migliore-riduzione-di-javascript","status":"publish","type":"article","link":"https:\/\/alistapart.com\/it\/article\/migliore-riduzione-di-javascript\/","title":{"rendered":"Una migliore riduzione al minimo di JavaScript"},"content":{"rendered":"<p><img decoding=\"async\" src=\"http:\/\/alistapart.com\/it\/wp-content\/uploads\/sites\/2\/2010\/05\/microscopio.png\" border=\"0\" alt=\"Microscopio\" title=\"microscopio\" style=\"float: right;\" \/><\/p>\n<p>Negli scorsi anni, si \u00e8 diffusa enormemente la ricerca sulla performance, grazie agli studi dello Yahoo! Exceptional Performance Team e di Steve Souders di Google. La maggior parte di questa ricerca studia non la singola pagina HTML, bens\u00ec le risorse necessarie a quella pagina per essere visualizzata e per avere il comportamento previsto.<\/p>\n<p>Sebbene sia i CSS sia JavaScript possano essere inclusi in una pagina HTML, le best practice incoraggiano la memorizzazione del codice CSS e JavaScript in file esterni, che possano essere scaricati e memorizzati separatamente. La ricerca sulla performance ha come fulcro il seguente interrogativo: come si possono scaricare ed applicare queste risorse esterne in maniera pi\u00f9 efficiente? Il primo approccio consiste nel limitare il numero delle richieste esterne dal momento che l&#8217;overhead di ogni richiesta HTTP \u00e8 alto. Il secondo approccio? Fate in modo che il vostro codice sia quanto pi\u00f9 piccolo possibile.<\/p>\n<div class=\"paragrafo\">\n<h2>La storia del risparmio dei byte di JavaScript<\/h2>\n<p>Douglas Crockford introdusse <a href=\"http:\/\/www.crockford.com\/javascript\/jsmin.html\">JSMin<\/a> nel 2004 come un modo per rimpicciolire i file JavaScript prima che vengano messi in un ambiente di produzione. Il suo semplice tool rimuoveva gli spazi, i tab ed i commenti dai file JavaScript, riducendone sostanzialmente la dimensione rispetto al file originale. La logica dietro al suo tool era valida: ridurre la dimensione del codice JavaScript voleva dire aumentarne la velocit\u00e0 di download, risultando in una migliore esperienza d&#8217;uso.<\/p>\n<p>Tre anni pi\u00f9 tardi, l&#8217;ingegnere di Yahoo! Julien Lecomte <a href=\"http:\/\/www.julienlecomte.net\/blog\/2007\/08\/11\/\">present\u00f2<\/a> lo <a href=\"http:\/\/developer.yahoo.com\/yui\/compressor\">YUI Compressor<\/a>. Lo scopo dello YUI Compressor consisteva nel ridurre la dimensione dei file JavaScript ancora di pi\u00f9 che con JSMin, applicando delle ottimizzazioni accorte al codice sorgente: oltre e rimuovere i commenti, gli spazi e i tabe, lo YUI Compressor rimuove in maniera sicura i line break, diminuendo la dimensione complessiva del file. Tuttavia, il maggior risparmio sui byte lo si ottiene rimpiazzando i nomi delle variabili locali con nomi composti da uno o due caratteri. Ad esempio, supponete di avere la seguente funzione:<\/p>\n<pre>  function sum(num1, num2) {<br \/>    return num1 + num2;<br \/>  }<\/pre>\n<p>Lo YUI Compressor la fa diventare cos\u00ec:<\/p>\n<pre>function sum(A,B){return A+B;}<\/pre>\n<p>Notate come le due variabili locali, <code>num1<\/code> and <code>num2<\/code>, siano state rimpiazzate da <code>A<\/code> e <code>B<\/code>, rispettivamente. Dal momento che lo YUI Compressor effettivamente fa il parsing dell&#8217;intero input di JavaScript, pu\u00f2 sostituire i nomi delle variabili locali in maniera sicura, senza introdurre errori nel codice. La funzione nel complesso continua a funzionare come in origine dal momento che i nomi delle variabili sono irrilevanti per la sua funzionalit\u00e0. In media, lo YUI Compressor pu\u00f2 comprimere i file fino al 18% in pi\u00f9 rispetto a JSMin.<\/p>\n<p>Al giorno d&#8217;oggi, la pratica comune \u00e8 quella di usare un tool di riduzione della dimensione insieme alla compressione HTTP, per ridurre ancora di pi\u00f9 la dimensione dei file JavaScript. Tutto ci\u00f2 risulta in un maggior risparmio rispetto all&#8217;uso di un singolo metodo.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Aumentare la minimizzazione<\/h2>\n<p>Un paio di anni fa, quando ho cominciato a fare il debug di grandi quantit\u00e0 di codice di produzione, ho realizzato che lo YUI Compressor non applicava la sostituzione del nome della variabile ad una porzione piuttosto significativa del mio codice. Infastidito da quello che consideravo un grosso spreco di bytes, ho esplorato i pattern di codifica dello YUI Compressor per aumentarne i poteri di riduzione. Ho presentato i miei risultati, <a href=\"http:\/\/www.slideshare.net\/nzakas\/extreme-javascript-compression-with-yui-compressor\">Extreme JavaScript Compression with YUI Compressor<\/a> [JavaScript: compressione estrema con lo YUI Compressor, <em>ndr<\/em>], internamente a Yahoo!.<\/p>\n<p>Durante le mie indagini, ho scoperto dei coding pattern che impedivano allo YUI Compressor di fare la sostituzione del nome della variabile: modificando o evitando questi coding pattern, si pu\u00f2 migliorare la performance dello YUI Compressor.<\/p>\n<h3>Le feature diaboliche di JavaScript<\/h3>\n<p>Tutti coloro i quali hanno seguito le lezioni o letto gli scritti di Douglas Crockford, sanno a cosa ci si riferisce quando si parla delle parti \u201cdiaboliche\u201d di JavaScript: si tratta di quelle parti che disorientano e\/o ci impediscono di scrivere codice pulito che abbia una buona performance. La funzione <code>eval()<\/code> e l&#8217;istruzione <code>with<\/code> sono due colossali esempi di JavaScript diabolico. Sebbene ci siano altri fattori, entrambe queste feature forzano lo YUI Compressor a smettere di sostituire variabili. Per capire perch\u00e9, dobbiamo capire quanto sia intricato il loro funzionamento.<\/p>\n<h3>Lavorare con <code>eval()<\/code><\/h3>\n<p>Il compito dell&#8217;istruzione <code>eval()<\/code> \u00e8 quello di prendere una stringa ed interpretarla come codice JavaScript. Ad esempio:<\/p>\n<pre>eval(\"alert('Hello world!');\");<\/pre>\n<p>La parte infida di <code>eval()<\/code> \u00e8 che ha accesso a tutte le variabili e funzioni che le stanno intorno. Ecco un esempio pi\u00f9 complesso:<\/p>\n<pre>  var message = \"Hello world!\";<br \/><br \/>  function doSomething() {<br \/>    eval(\"alert(message)\");<br \/>  }<br \/> <\/pre>\n<p>Quando chiamate <code>doSomething()<\/code>, viene visualizzato un avviso insieme al messaggio \u201cHello world!\u201d. Questo accade perch\u00e9 la stringa passata ad <code>eval()<\/code> accede alla variabile globale <code>message<\/code> e lo visualizza. Ora, considerate cosa succederebbe se rimpiazzaste automaticamente il nome della variabile <code>message<\/code>:<\/p>\n<pre>  var A = \"Hello world!\";<br \/><br \/>  function doSomething() {<br \/>    eval(\"alert(message)\"); <br \/>  }<br \/> <\/pre>\n<p>Avrete notato come cambiare il nome della variabile in <code>A<\/code> risulti in un errore quando <code>doSomething()<\/code> va in esecuzione (dal momento che <code>message<\/code> non \u00e8 definita). Il primo compito dello YUI Compressor \u00e8 quello di preservare la funzionalit\u00e0 del vostro script, quindi quando trova <code>eval()<\/code> smette di rimpiazzare le variabili. Questa potrebbe non sembrare un&#8217;idea cos\u00ec cattiva finch\u00e9 non realizzate tutte le implicazioni: la sostituzione dei nomi di variabile \u00e8 impedita non solo nel contesto <em>locale<\/em> in cui <code>eval()<\/code> \u00e8 chiamata, ma in tutti i contesti che le <em>stanno attorno<\/em>. Nell&#8217;esempio precedente, ci\u00f2 significa che sia il contesto all&#8217;interno di <code>doSomething()<\/code> che il contesto globale non possono avere i nomi di variabile sostituiti.<\/p>\n<p>Usare <code>eval()<\/code> ovunque nel codice implica che i nomi delle variabili globali non saranno mai cambiati. Considerate l&#8217;esempio seguente:<\/p>\n<pre>  function handleJSONP(object) {<br \/>    return object;<br \/>  }<br \/><br \/>  function interpretJSONP(code) {<br \/>    var data = eval(code);<br \/>    <br \/>    \/\/process data<br \/>  }<br \/> <\/pre>\n<p>In questo codice, fate finta che <code>handleJSONP()<\/code> e <code>interpretJSONP()<\/code> siano definite nel mezzo di altre funzioni. <a href=\"http:\/\/en.wikipedia.org\/wiki\/JSON#JSONP\">JSONP<\/a> \u00e8 un formato di comunicazione Ajax molto usato che richiede che la risposta sia interpretata dal motore di JavaScript. Per questo esempio, un modello di risposta JSONP potrebbe essere come questo:<\/p>\n<pre>  handleJSONP({message:\"Hello world!\"});<br \/> <\/pre>\n<p>Se riceveste indietro questo codice dal server attraverso una chiamata <code>XMLHttpRequest<\/code>, lo step seguente sarebbe quello di valutarlo: a questo punto <code>eval()<\/code> diventa molto utile. Ma il solo fatto di avere <code>eval()<\/code> nel codice significa che nessuno dei nomi degli identificatori globali potr\u00e0 essere sostituito. L&#8217;opzione migliore consiste nel limitare il numero di variabili globali che introducete.<\/p>\n<p>Potete spesso farla franca creando una funzione anonima che si esegua da s\u00e9, tipo:<\/p>\n<pre>  (function() {<br \/>    function handleJSONP(object) {<br \/>        return object;<br \/>    }<br \/><br \/>    function interpretJSONP(code) {<br \/>        var data = eval(code);<br \/>    <br \/>        \/\/process data<br \/>    }<br \/>   })();<br \/> <\/pre>\n<p>Questo codice non introduce alcuna nuova variabile globale, ma dal momento che viene usata <code>eval()<\/code>, nessun nome di variabile verr\u00e0 rimpiazzato. Il risultato definitivo (110 bytes) \u00e8 il seguente:<\/p>\n<p>(Gli a capo sono segnati con \u00bb <em>\u2014Ed.<\/em>)<\/p>\n<pre>  (function(){function handleJSONP(object){return object}function \u00bb<br \/>   interpretJSONP(code){var data=eval(code)}})();<br \/> <\/pre>\n<p>La cosa carina di JSONP \u00e8 che si basa sull&#8217;esistenza di un unico identificatore globale: la funzione a cui deve essere passato il risultato (in questo caso, <code>handleJSONP()<\/code>). Questo significa che non ha bisogno di accedere ad alcuna variabile locale o funzione e vi d\u00e0 l&#8217;opportunit\u00e0 di segregare la funzione <code>eval()<\/code> nella sua funzione globale. Notate inoltre che dovete spostare <code>handleJSONP()<\/code> al di fuori perch\u00e9 sia globale, cos\u00ec che nemmeno il suo nome venga sostituito:<\/p>\n<pre>  \/\/my own eval<br \/>  function myEval(code) {<br \/>    return eval(code);<br \/>  }<br \/><br \/>  function handleJSONP(object) {<br \/>    return object;<br \/>  }<br \/><br \/>  (function() {<br \/>    function interpretJSONP(code) {<br \/>        var data = myEval(code);<br \/>    <br \/>        \/\/process data<br \/>    }<br \/>  })();<br \/> <\/pre>\n<p>La funzione <code>myEval()<\/code> ora funziona come <code>eval()<\/code> tranne che non pu\u00f2 accedere alle variabili locali. Pu\u00f2 comunque accedere a tutte le variabili globali e alle funzioni. Se il codice che viene eseguito da <code>eval()<\/code> non avr\u00e0 mai bisogno di accedere alle variabili locali, allora questo approccio \u00e8 il migliore. Tenendo l&#8217;unico riferimento a <code>eval()<\/code> al di fuori della funzione anonima, farete in modo che ogni nome di variabile all&#8217;interno di quella funzione possa essere sostituito. Ecco l&#8217;output:<\/p>\n<pre>  function myEval(code){return eval(code)}function handleJSONP \u00bb<br \/>  (a){return a}(function(){function a(b){var c=myEval(b)}})();<br \/> <\/pre>\n<p>Potete vedere che sia <code>interpretJSON()<\/code>, <code>code<\/code> e <code>data<\/code> sono stati rimpiazzati (da <code>a<\/code>, <code>b<\/code> e <code>c<\/code> rispettivamente). Il risultato \u00e8 di 120 bytes, che, noterete, \u00e8 maggiore dell&#8217;esempio senza <code>eval()<\/code> segregato. Questo non vuol dire che l&#8217;approccio \u00e8 bacato, \u00e8 solo che questo codice d&#8217;esempio \u00e8 fin troppo piccolo per vederne l&#8217;impatto. Se applicaste questo cambiamento su un codice JavaScript di 100KB, allora vedreste che il codice risultante \u00e8 molto pi\u00f9 piccolo rispetto a lasciare <code>eval()<\/code> al suo posto.<\/p>\n<p>Ovviamente, la miglior opzione \u00e8 quella di non usare <code>eval()<\/code> per niente, poich\u00e9 facendo ci\u00f2 vi evitereste molte acrobazie per rendere felice lo YUI Compressor. Comunque, se proprio dovete, la vostra scelta migliore per una minimizzazione ottimale \u00e8 quella di segregare <code>eval()<\/code>.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>L&#8217;istruzione <code>with<\/code><\/h2>\n<p>L&#8217;istruzione <code>with<\/code> \u00e8 la seconda feature diabolica che interferisce con la tecnica di sostituzione delle variabili dello YUI Compressor. Per quelli che non vi hanno familiarit\u00e0, l&#8217;istruzione <code>with<\/code> \u00e8 stata progettata (in teoria) per ridurre la dimensione del codice eliminando il bisogno di scrivere gli stessi nomi di variabile pi\u00f9 volte. Considerate il seguente esempio:<\/p>\n<pre>  var object = {<br \/>   message: \"Hello, \",<br \/>   messageSuffix: \", and welcome.\"<br \/>  };<br \/>  object.message += \"world\" + object.messageSuffix;<br \/>  alert(object.message);<br \/> <\/pre>\n<p>L&#8217;istruzione <code>with<\/code> vi permette di riscrivere il codice come:<\/p>\n<pre>  var object = {<br \/>    message: \"Hello, \",<br \/>    messageSuffix: \", and welcome.\"<br \/>  };<br \/>  with (object) {<br \/>    message += \"world\" + messageSuffix;<br \/>    alert(message);\t<br \/>  }<br \/> <\/pre>\n<p>Effettivamente, l&#8217;istruzione <code>with<\/code> evita il bisogno di ripetere \u201cobject\u201d pi\u00f9 volte all&#8217;interno del codice. Ma questo risparmio ha un suo costo. Innanzitutto, ci sono delle <a href=\"http:\/\/www.nczonline.net\/blog\/2009\/02\/10\/javascript-variable-performance\/\">implicazioni sulla performance<\/a> usando l&#8217;istruzione <code>with<\/code>, poich\u00e9 si accede pi\u00f9 lentamente alle variabili locali. Ci\u00f2 accade perch\u00e9 le variabili all&#8217;interno dell&#8217;istruzione <code>with<\/code> sono ambigue fino al momento dell&#8217;esecuzione: potrebbero essere di propriet\u00e0 del oggetto del contesto dell&#8217;istruzione <code>with<\/code> oppure essere variabili della funzione o un altro contesto di esecuzione. Per meglio comprendere questa ambiguit\u00e0, osservate il codice quando la variabile locale <code>message<\/code> viene aggiunta e viene rimossa la definizione di <code>object<\/code>:<\/p>\n<pre>  var message = \"Yo, \";<br \/><br \/>  with (object) {<br \/>    message += \"world\" + messageSuffix;<br \/>    alert(message);<br \/>  }<br \/> <\/pre>\n<p>Quando l&#8217;identificatore <code>message<\/code> \u00e8 usato all&#8217;interno dell&#8217;istruzione <code>with<\/code>, potrebbe far riferimento alla variabile locale <code>message<\/code> oppure potrebbe far riferimento ad una propriet\u00e0 chiamata <code>message<\/code> su <code>object<\/code>. Dal momento che JavaScript \u00e8 un linguaggio \u201clate binding\u201d, non ci sono modi per sapere quale sia il vero riferimento per <code>message<\/code> senza aver prima eseguito il codice ed aver determinato se <code>object<\/code> ha la propriet\u00e0 chamata <code>message<\/code>. Osservate come il late binding ha effetto su questo codice:<\/p>\n<pre>  function displayMessage(object) {<br \/>    var message = \"Yo, \";<br \/><br \/>    with (object){<br \/>      message += \"world\" + messageSuffix;<br \/>      alert(message);<br \/>    }<br \/>  }<br \/><br \/>  displayMessage({ message: \"Hello, \", messageSuffix: \", and welcome.\" });<br \/>  displayMessage({ messageSuffix: \", and welcome.\" });<br \/> <\/pre>\n<p>La prima volta che viene chiamato <code>displayMessage()<\/code>, l&#8217;oggetto che gli viene passato ha la propriet\u00e0 chiamata <code>message<\/code>. Quando va in esecuzione l&#8217;istruzione <code>with<\/code>, il riferimento a <code>message<\/code> viene mappato sulla propriet\u00e0 dell&#8217;oggetto e pertanto il messaggio visualizzato \u00e8 \u201cHello, world, and welcome.\u201d. La seconda volta, l&#8217;oggetto che viene passato ha solo la propriet\u00e0 <code>messageSuffix<\/code>, col significato che il riferimento a <code>message<\/code> all&#8217;interno dell&#8217;istruzione <code>with<\/code> fa riferimento alla variabile locale; il messaggio visualizzato \u00e8 quindi \u201cYo, world, and welcome.\u201d.<\/p>\n<p>Dal momento che lo YUI Compressor in effetti non esegue il codice JavaScript, non ha possibilit\u00e0 di sapere se gli identificatori in un&#8217;istruzione <code>with<\/code> siano propriet\u00e0 dell&#8217;oggetto (nel qual caso, <em>non \u00e8 <\/em>sicuro rimpiazzarli) o se siano riferimenti a variabili locali (in questo caso, <em>\u00e8<\/em> sicuro rimpiazzarli). Lo YUI Compressor tratta l&#8217;istruzione <code>with<\/code> allo stesso modo di <code>eval()<\/code>: quando \u00e8 presente: non effettua la sostituzione delle variabili nella funzione n\u00e9 in nessuno dei contesti di esecuzione.<\/p>\n<p>A differenza della funzione <code>eval()<\/code>, non c&#8217;\u00e8 modo di segregare l&#8217;istruzione <code>with<\/code> in maniera che non abbia effetto sulla maggior parte del codice. La mia raccomandazione \u00e8 di evitare del tutto di usare l&#8217;istruzione <code>with<\/code>: sebbene sembra che faccia risparmiare byte nel momento in cui scriviamo il codice, in realt\u00e0 si perdono bytes rinunciando alla feature della sostituzione di variabile dello YUI Compressor. La funzione <code>displayMessage()<\/code> viene ridotta cos\u00ec:<\/p>\n<pre>  function displayMessage(object){var message=\"Yo, \";with(object) \u00bb<br \/>  {message+=\"world\"+messageSuffix;alert(message)}};<br \/> <\/pre>\n<p>Si tratta di 112 bytes. Se si riscrive la funzione evitando l&#8217;istruzione <code>with<\/code>, <code>displayMessage()<\/code> diventa cos\u00ec:<\/p>\n<pre>  function displayMessage(object) {<br \/>    var message = \"Yo, \";<br \/><br \/>    object.message += \"world\" + object.messageSuffix;<br \/>    alert(object.message);<br \/>  }<br \/> <\/pre>\n<p>Quando viene ridotta, questa nuova versione della funzione diventa:<\/p>\n<pre>  function displayMessage(a){var b=\"Yo, \";a.message+=\"world\"+ \u00bb<br \/>  a.messageSuffix;alert(a.message)};<br \/> <\/pre>\n<p>La dimensione di questa \u00e8 di 93 bytes, nonostante il codice sorgente originale sia pi\u00f9 grande. Il codice sorgente ridotto diventa pi\u00f9 piccolo perch\u00e9 abbiamo usato la sostituzione di variabile.<\/p>\n<\/div>\n<div class=\"paragrafo\">\n<h2>Conclusioni<\/h2>\n<p>La funzionalit\u00e0 di sostituzione di variabile dello YUI Compressor pu\u00f2 far risparmiare molti byte riducendo il vostro JavaScript. Dal momento che lo YUI Compressor cerca di evitare di danneggiare il vostro codice sostituendo in maniera errata dei nomi di variabile, non effettuer\u00e0 la sostituzione di variabile quando vengono usati la funzione <code>eval()<\/code> o l&#8217;istruzione <code>with<\/code>. Queste feature \u201cdiaboliche\u201d alterano la maniera in cui il codice JavaScript viene interpretato e impediscono allo YUI Compressor di sostituire in maniera sicura i nomi di variabile, al costo di molti byte non risparmiati. Evitate questa penalizzazione stando alla larga da <code>eval()<\/code> o segregandola lontano dal resto del vostro codice. Inoltre, evitate anche l&#8217;istruzione <code>with<\/code>. Questi step vi assicureranno che il vostro codice non ponga ostacoli ad una riduzione ottimale.<\/p>\n<\/div>\n<div id=\"credits\">\n<ul>\n<li>Illustrazioni di {carlok}<\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Come per i CSS, JavaScript funziona al meglio e pi\u00f9 efficacemente quando \u00e8 memorizzato in un file esterno che possa essere scaricato e messo in cache separatamente dalle singole pagine HTML del nostro sito. Per migliorare la performance, limitiamo il numero di richieste all&#8217;esterno e rendiamo il nostro JavaScript il pi\u00f9 piccolo possibile. Gli schemi di minimizzazione di JavaScript nacquero nel 2004 con JSMin e sono progrediti fino allo YUI Compressor nel 2007. Ora l&#8217;inventore di Extreme JavaScript Compression with YUI Compressor ci rivela quali sono i coding pattern che interferiscono con la compressione e quali sono le tecniche per modificare o evitare questi coding pattern, con l&#8217;intento di migliorare la performance dello YUI Compressor. Pensate piccolo e vivete alla grande.<\/p>\n","protected":false},"author":818,"featured_media":7000574,"comment_status":"open","ping_status":"open","template":"","categories":[271,16,278],"tags":[],"coauthors":[293],"class_list":["post-59","article","type-article","status-publish","has-post-thumbnail","hentry","category-javascript","category-numero-5-4-maggio-2010","category-workflow-tools"],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/article\/59","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/article"}],"about":[{"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/types\/article"}],"author":[{"embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/users\/818"}],"replies":[{"embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/comments?post=59"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/media\/7000574"}],"wp:attachment":[{"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/media?parent=59"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/categories?post=59"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/tags?post=59"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/alistapart.com\/it\/wp-json\/wp\/v2\/coauthors?post=59"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}