件綁定的執行次序
在W3C的標準下,事件綁定使用的是addEventLisenter。
但微軟這家伙經常不按常理出牌。它提供的事件綁定方法是。
關于attachEvent的種種不足,先不一一列舉?;蛟S我們已經忍受著用attachEvent來綁定事件。
//object.attachEvent(event, function);
el.attachEvent('onclick', method);
但當在同一元素上綁定多次,你就會知道,微軟的這個私人的方法又是多么“不可理喻”。
el.attachEvent("onclick", method1);
el.attachEvent("onclick", method2);
el.attachEvent("onclick", method3);
el.attachEvent("onclick", method4);
在各個IE下運行,點擊el后,各個方法的執行次序是:
???? IE6/IE7: method2 -> method4 -> method3 -> method1
???? IE8: method4 -> method3 -> method2 -> method1
???? IE9: method1 -> method2 -> method3 -> method4
IE6/7是一種看不出規律的隨機執行,IE8是逆序執行,IE9是順序執行。
無論常識還是W3C標準,這種同一個事件綁定了多個方法的情況,方法的執行次序都應該是先綁定先執行。從IE6-IE9的演變,可以看得出微軟還是有在不斷改進的。但畢竟各個版本的IE存在,使得我們不得不注意綁定在同一事件上的多個方法之間不要有順序依賴。
????? YUI2的事件綁定并沒有對執行次序做兼容,如果有些場景的確需要綁定多個方法且方法間需要順序執行。那我們就只能自己實現了,大致思路是:在先判斷綁定的el對象上是否已經同一個事件類型的handlers,如果有則不會重復綁定而是把該對象的handler合并到handlers中成為一個方法,相當于這樣function c() { a(); b(); },按照順序的壓入方法實現在IE下不會出現綁定多個方法時能順序執行。
function addEvent(el, type, handler) {
???????? if (el.attachEvent) { // IE
???????????????? // 為元素的事件類型創建一個哈希表
???????????????? el._events=el._events || {};
???????????????? if (!handler) { return; }
???????????????? // 取得事件處理函數的數組的引用
???????????????? var handlers=el._events[type];
???????????????? if (handlers) {
????????????????????????? if(typeof handler._index==='undefined' || handlers[handler._index] !==handler) {
?????????????????????????????????? // 將事件處理函數存入數組
?????????????????????????????????? handlers.push(handler);
?????????????????????????????????? // 為事件處理函數增加索引
?????????????????????????????????? handler._index=handlers.length - 1;
????????????????????????? }
???????????????? } else {
????????????????????????? // 創建事件處理函數的數組
????????????????????????? el._events[type]=[];
????????????????????????? // 緩存總的事件綁定函數
????????????????????????? el._eventHandlers=function (e) {
???????????????? ???????????????? e=e || window.event;
?????????????????????????????????? var handlers=el._events[type];
?????????????????????????????????? // 依次執行所有緩存的事件處理方法
?????????????????????????????????? for (var i=0, len=handlers.length; i
??????????????????????????????????????????? typeof handlers[i]==='function' && handlers[i](e);
?????????????????????????????????? }
????????????????????????? };
????????????????????????? // 綁定一次事件,來處理所有綁定在該事件上的所有函數
????????????????????????? el.attachEvent('on' + type, el._eventHandlers);
????????????????????????? addEvent(el, type, handler);
???????????????? }
???????? } else { // 現代瀏覽器
???????????????? el.addEventListener(type, handler, false);
???????? }
}
相應的,移除事件的方法也要做一些處理。
function removeEvent(el, type, handler) {
???????? if (el.detachEvent) { // IE
???????????????? // 取得事件處理函數的數組的引用
???????????????? var handlers=el._events && el._events[type];
???????????????? if (handlers) {
????????????????????????? if (handler) {
?????????????????????????????????? // 刪除事件處理函數隊列里的函數
?????????????????????????????????? delete handlers[handler._index];
?????????????????????????????????? // 刪除函數的索引
?????????????????????????????????? delete handler._index;
?????????????????????????????????? if (handlers.length===0) {
??????????????????????????????????????????? // 解除總的事件綁定
??????????????????????????????????????????? el.detachEvent('on' + type, el._eventHandlers);
?????????????????????????????????? }
????????????????????????? } else {
?????????????????????????????????? // 解除總的事件綁定
?????????????????????????????????? el.detachEvent('on' + type, el._eventHandlers);
?????????????????????????????????? // 刪除該事件綁定上的所有事件處理函數隊列
?????????????????????????????????? delete el._events[type];
????????????????????????? }
???????????????? }
???????? } else { // 現代瀏覽器
???????????????? el.removeEventListener(el, type, handler);
???????? }
}
為了實現各個瀏覽器下,同一事件綁定多個方法的次序能正確執行。代碼上不得不多做一些處理。有興趣的可以再細致讀一下代碼,如有問題,歡迎指正。即使無需用到,也需要清楚,綁定在同一事件上的多個方法之間不要有順序依賴。
function fn1() {
setTimeout(()=> {
console.log('fn1執行')
fn2('fn1傳遞過去的參數')
}, 1000)
}
function fn2(data) {
setTimeout(()=> {
console.log('fn2執行', data)
fn3('fn2傳遞過去的參數')
}, 1000)
}
function fn3(data) {
setTimeout(()=> {
console.log('fn3執行', data)
}, 1000)
}
fn1()
執行結果如下:
我們發現這種方式雖然能實現,但是代碼量一旦變多,可讀性就降低了。
function fn1() {
return new Promise((resolve, reject)=> {
console.log('fn1執行')
setTimeout(()=> {
console.log('fn1結束')
resolve('fn1傳遞過去的參數')
}, 1000)
})
}
function fn2(data) {
return new Promise((resolve, reject)=> {
console.log('fn2執行,接收的參數', data)
setTimeout(()=> {
resolve('fn2傳遞過去的參數')
}, 1000)
})
}
function fn3(data) {
return new Promise((resolve, reject)=> {
console.log('fn3執行,接收的參數', data)
setTimeout(()=> {
resolve('fn3傳遞過去的參數')
}, 1000)
})
}
fn1().then(fn2).then(fn3).then(res=> {
console.log('最后一個', res)
})
生成器就是能返回一個迭代器的函數,它也是一個函數,對比普通的函數,多了一個*,在它的函數體內可以使用yield關鍵字,函數會在每個yield后暫停,等待,直到這個生成的對象,調用下一個next(),每調用一次next會往下執行一次yieId,然后暫停。
這里是云端源想IT,幫你輕松學IT”
嗨~ 今天的你過得還好嗎?
我們總是先揚起塵土
然后抱怨自己看不見
- 2024.04.17 -
JavaScript是一種輕量級的編程語言,通常用于網頁開發,以增強用戶界面的交互性和動態性。然而在HTML中,有多種方法可以嵌入和使用JavaScript代碼。
本文就帶大家深入了解如何在HTML中使用JavaScript。
要在HTML中使用JavaScript,我們需要使用<script>標簽。這個標簽可以放在<head>或<body>部分,但通常我們會將其放在<body>部分的底部,以確保在執行JavaScript代碼時,HTML文檔已經完全加載。
使用 <script> 標簽有兩種方式:直接在頁面中嵌入 JavaScript 代碼和包含外部 JavaScript 文件。
包含在 <script> 標簽內的 JavaScript 代碼在瀏覽器總按照從上至下的順序依次解釋。
所有 <script> 標簽都會按照他們在 HTML 中出現的先后順序依次被解析。
HTML 為 <script> 定義了幾個屬性:
1)async:可選。表示應該立即下載腳本,但不妨礙頁面中其他操作。該功能只對外部 JavaScript 文件有效。
如果給一個外部引入的js文件設置了這個屬性,那頁面在解析代碼的時候遇到這個<script>的時候,一邊下載該腳本文件,一邊異步加載頁面其他內容。
2)defer:可選。表示腳本可以延遲到整個頁面完全被解析和顯示之后再執行。該屬性只對外部 JavaScript 文件有效。
3)src:可選。表示包含要執行代碼的外部文件。
4)type:可選。表示編寫代碼使用的腳本語言的內容類型,目前在客戶端,type 屬性值一般使用 text/javascript。不過這個屬性并不是必需的,如果沒有指定這個屬性,則其默認值仍為text/javascript。
1.1 直接在頁面中嵌入JavaScript代碼
內部JavaScript是將JavaScript代碼放在HTML文檔的<script>標簽中。這樣可以將JavaScript代碼與HTML代碼分離,使結構更清晰,易于維護。
在使用<script>元素嵌入JavaScript代碼時,只須為<script>指定type屬性。然后,像下面這樣把JavaScript代碼直接放在元素內部即可:
<script type="text/javascript">
function sayHi(){
alert("Hi!");
}
</script>
如果沒有指定script屬性,則其默認值為text/javascript。
包含在<script>元素內部的JavaScript代碼將被從上至下依次解釋。在解釋器對<script>元素內部的所有代碼求值完畢以前,頁面中的其余內容都不會被瀏覽器加載或顯示。
在使用<script>嵌入JavaScript代碼的過程中,當代碼中出現"</script>"字符串時,由于解析嵌入式代碼的規則,瀏覽器會認為這是結束的</script>標簽??梢酝ㄟ^轉義字符“\”寫成<\/script>來解決這個問題。
1.2 包含外部 JavaScript 文件
外部JavaScript是將JavaScript代碼放在單獨的.js文件中,然后在HTML文檔中通過<script>標簽的src屬性引用這個文件。這種方法可以使代碼更加模塊化,便于重用和共享。
如果要通過<script>元素來包含外部JavaScript文件,那么src屬性就是必需的。這個屬性的值是一個指向外部JavaScript文件的鏈接。
<script type="text/javascript" src="example.js"></script>
與解析嵌入式JavaScript代碼一樣,在解析外部JavaScript文件(包括下載該文件)時,頁面的處理也會暫時停止。
注意:帶有src屬性的<script>元素不應該在其<script>和</script>標簽之間再包含額外的JavaScript代碼。如果包含了嵌入的代碼,則只會下載并執行外部腳本文件,嵌入的代碼會被忽略。
通過<script>元素的src屬性還可以包含來自外部域的JavaScript文件。它的src屬性可以是指向當前HTML頁面所在域之外的某個域中的完整URL。
<script type="text/javascript" src="http://www.somewhere.com/afile.js"></script>
于是,位于外部域中的代碼也會被加載和解析。
1.3 標簽的位置
在HTML中,所有的<script>標簽會按照它們出現的先后順序被解析。在不使用defer和async屬性的情況下,只有當前面的<script>標簽中的代碼解析完成后,才會開始解析后面的<script>標簽中的代碼。
通常,所有的<script>標簽應該放在頁面的<head>標簽中,這樣可以將外部文件(包括CSS和JavaScript文件)的引用集中放置。
然而,如果將所有的JavaScript文件都放在<head>標簽中,會導致瀏覽器在呈現頁面內容之前必須下載、解析并執行所有JavaScript代碼,這可能會造成明顯的延遲,導致瀏覽器窗口在加載過程中出現空白。
為了避免這種延遲問題,現代Web應用程序通常會將所有的JavaScript引用放置在<body>標簽中的頁面內容的后面。這樣做可以確保在解析JavaScript代碼之前,頁面的內容已經完全呈現在瀏覽器中,從而加快了打開網頁的速度。
JavaScript 解析過程包括兩個階段:預處理(也稱預編譯)和執行。
1、執行過程
HTML 文檔在瀏覽器中的解析過程是:按照文檔流從上到下逐步解析頁面結構和信息。
JavaScript 代碼作為嵌入的腳本應該也算做 HTML 文檔的組成部分,所以 JavaScript 代碼在裝載時的執行順序也是根據 <script> 標簽出現的順序來確定。
你是不是厭倦了一成不變的編程模式?想要突破自我,挑戰新技術想要突破自我,挑戰新技術?卻遲遲找不到可以練手的項目實戰?是不是夢想打造一個屬于自己的支付系統?那么,恭喜你,云端源想免費實戰直播——《微實戰-使用支付寶/微信支付服務,網站在線支付功能大揭秘》正在進行,點擊前往獲取源碼!云端源想
2、預編譯
當 JavaScript 引擎解析腳本時候,他會在與編譯期對所有聲明的變量和函數預先進行處理。當 JavaScript 解析器執行下面腳本時不會報錯。
alert(a); //返回值 undefined
var a=1;
alert(a); //返回值 1
由于變量聲明是在預編譯期被處理的,在執行期間對于所有的代碼來說,都是可見的,但是執行上面代碼,提示的值是 undefined 而不是 1。
因為變量初始化過程發生在執行期,而不是預編譯期。在執行期,JavaScript 解析器是按照代碼先后順序進行解析的,如果在前面代碼行中沒有為變量賦值,則 JavaScript 解析器會使用默認值 undefined 。
由于第二行中為變量 a 賦值了,所以在第三行代碼中會提示變量 a 的值為 1,而不是 undefined。
fun(); //調用函數,返回值1
function fun(){
alert(1);
}
函數聲明前調用函數也是合法的,并能夠正確解析,所以返回值是 1。但如果是下面這種方式則 JavaScript 解釋器會報錯。
fun(); //調用函數,返回語法錯誤
var fun=function(){
alert(1);
}
上面的這個例子中定義的函數僅作為值賦值給變量 fun 。在預編譯期,JavaScript 解釋器只能夠為聲明變量 fun 進行處理,而對于變量 fun 的值,只能等到執行期時按照順序進行賦值,自然就會出現語法錯誤,提示找不到對象 fun。
總結:聲明變量和函數可以在文檔的任意位置,但是良好的習慣應該是在所有 JavaScript 代碼之前聲明全局變量和函數,并對變量進行初始化賦值。在函數內部也是先聲明變量,后引用。
通過今天的分享,相信大家已經對JavaScript在HTML中的應用有了一定的了解。這只是冰山一角,JavaScript的潛力遠不止于此。希望這篇文章能激發大家對編程的熱情,讓我們一起在編程的世界里探索更多的可能性!
我們下期再見!
END
文案編輯|云端學長
文案配圖|云端學長
內容由:云端源想分享
*請認真填寫需求信息,我們會在24小時內與您取得聯系。