【前端基礎】動態腳本與JSONP
博主入職兩個月了,越來越感受到打好基礎對於前端工程師的重要性,在向著狂拽酷炫的框架&構建工具狂飈之前,必須有一個堅實的基礎打底,才不至於輕易翻車。所以博主最近一直在惡補《JS高級程序設計》,發現了很多第一次讀時忽略的、有趣的地方。不愧是經典,常讀常新呀!
PART 1 最熟悉的陌生人——動態腳本
對於一些剛接觸前端不久的同學來說,“動態腳本”可能是一個有些陌生的字眼,我也是偶然看高程,才想起同桌的你……不,才註意到這個技術。但事實上,這是每個前端每天都會用到的技術,也是從jQuery到各種狂拽酷炫的前端框架得以運行的基礎技術。那它到底是誰呢?
來看一行代碼:
<scriptsrc="jquery.js"></script>
眼熟吧?
寫過吧?
恭喜你,你已經是會使用動態腳本技術的前端攻城獅了!
……納尼?
沒錯!不要覺得這行代碼司空見慣,其實仔細研究下,還頗有點因缺斯艇呢~
記住一點,網頁歸根到底就是一個HTML頁面,除此之外別無他物。其他如JS、CSS、圖片等,都是頁面上的資源,從屬於這個頁面。JS代碼只有作為script類型的DOM節點的內部文本的形式被添加到網頁上,在瀏覽器解析DOM結構解析到script節點時,才會被執行。動態腳本,顧名思義,是“動態”的JS代碼——“動態”的意思是,不是預先寫在HTML頁面上的,而是在頁面解析的過程中被添加上去的。
動態添加腳本有兩種方法:一是DOM操作,像插入其他類型的DOM節點一樣插入一個script節點到頁面,其內部的JS代碼會被立即執行;二是如jQuery那個例子所示,從某個路徑加載js文件到頁面上來,可以理解為文件中的代碼被復制粘貼到這個script標簽中了,這些代碼會在加載完成後被執行。
舉個栗子:
<!-- 出於簡略,只寫body的代碼 --> <body> <!-- 靜態腳本 --> <script> console.log(1); var scriptDOM = document.createElement(‘script‘); scriptDOM.innerHTML = ‘console.log(2);‘; // 動態腳本的內容 document.body.appendChild(scriptDOM); // 動態插入script DOM節點 console.log(3); </script> </body>
這個頁面上有一段靜態腳本,其在執行時會向body插入一個script子節點。用瀏覽器打開這個頁面,console會順序打印出“1 2 3”這三個數字。查看頁面,變成了這樣:
<!-- 出於簡略,只寫body的代碼 --> <body> <!-- 靜態腳本 --> <script> console.log(1); var scriptDOM = document.createElement(‘script‘); scriptDOM.innerHTML = ‘console.log(2);‘; // 動態腳本的內容 document.body.appendChild(scriptDOM); // 動態插入script DOM節點 console.log(3); </script> <!-- 動態插入的的腳本 --> <script>console.log(2);</script> </body>
這個最終頁面有趣的地方在於,包裹“console.log(2);”的script標簽明明排在原來的標簽的後面,但2卻比3先打印出來。事實上,往任何地方插入動態腳本,其中的代碼都會在插入後立刻執行。通過src屬性引入的JS代碼也是如此,一旦加載完成就立刻執行。
這是腳本語言靈活性的絕佳體現呀!想想編譯型語言如C++、Java,所有代碼必須預先編譯好才能執行,無法做到像JS這樣,不用編譯不說,還可以在原本的代碼執行到一半時,忽然插進來一堆新代碼並且立即得到執行;就好比原本宴會邀請到了99個賓客,飯吃到一半,忽然來了個不速之客,還是個自來熟,坐下就開始與眾人談笑風生、觥籌交錯,於是東道主JavaScript順手就把賓客人數改成了100,毫無違和感;若是換成C++或Java做東,只會在宴會開始後把所有入口全部封死,不再允許任何人進入。
PART 2 動態腳本的應用:JSONP
“跨域”是前端面試中幾乎必考的問題,而JSONP是一個比較簡單好使的解決方案。
JSONP誕生的背景是,“跨域”只是AJAX所受的安全策略限制,只要域名、協議、端口這3項有1項不一致,瀏覽器就禁止發送AJAX請求。而像<script><img><link>這類標簽的src屬性不受此限,可以填寫任意域名的地址(想想jQuery的CDN地址,還有一堆圖床網站……)。於是聰明的前端攻城獅們想到了利用動態腳本來獲取json數據的套路,名曰JSON Padding,意思是“JSON填充”。
那具體怎麽個做法呢?分三步
步驟1
讓服務器端略改下代碼:當一個get請求的查詢參數裏有callback一項時,譬如一個URL長這樣:
http://someurl.com/data?callback=handler
那麽服務器就不要直接把JSON文件作為響應內容了,而是返回一個動態生成的JS文件,其中的代碼是
handler({JSON數據});
其實就是一個handler函數的調用,同時把JSON作為參數“填充”進去。
步驟2
回到前端。定義好負責處理JSON數據的handler函數:
function handler(json) { // 處理json數據 }
步驟3
在需要跨域獲取數據時,向頁面插入一個script DOM元素:
var script= document.createElement(‘script‘); script.src = ‘http://someurl.com/data?callback=handler‘; document.body.appendChild(script);
這個script元素的src屬性值帶有一個callback查詢參數,於是服務器會返回步驟1中的代碼,這段代碼被下載完成後立刻執行,handler恰好是已經定義好的函數,而夢寐以求的跨域JSON數據就這樣作為handler的實參被傳到了當前頁面上!
最後是兩點註意事項:
1. 用這種方法可以請求任何類型的數據,不限制為JSON,只是因為JSON最常用,所以命名為JSONP。
2. JSONP是為了幫AJAX繞開跨域限制而使用的一種技巧,它本身跟AJAX沒有半毛錢關系,在不支持AJAX的瀏覽器上照樣可以用JSONP技術。
【前端基礎】動態腳本與JSONP