onclick = xxx這種賦值寫法綁定事件的原理是什麼?

剛入門不久,能力有限,這個問題我描述起來有點困難,只有勞煩各位大神細看了

我之前一直以為js底層存在類似下面這樣的代碼:

//給所有dom對象定義好onclick值為一個空函數
HTMLElement.prototype.onclick = function(){};

//給所有dom對象綁定默認點擊回調函數:點擊時都執行一次自己的onclick方法
[].map.call(document.all,function(item){
item.addEventListener("click",function(){
this.onclick();
});
});

然後我認為給同一個元素多次添加事件函數,會形成一個待執行的函數隊列,那麼onclick以後無論怎麼賦值,執行順序會相對固定。

然而有如下可運行的代碼我又無法解釋(請展開全部之後再閱讀代碼,避免混亂):

//改變onclick的函數。此時body["click"]的事件隊列第一個函數為alert(1);
document.body.onclick = function(){alert(1)}

//body["click"]事件隊列里增加了alert(2);點擊時依次執行alert(1)、alert(2)
document.body.addEventListener("click",function(){alert(2)});

//再改變onclick的函數。此時body["click"]的事件隊列第一個函數換為alert(3);
document.body.onclick = function(){alert(3)}

然後這時候點擊body,先後順序本應該是alert(3)、alert(2),實際卻是alert(3)在後面?

為什麼僅僅憑一個賦值操作改變了onclick的值就能導致事件執行的順序變了呢?

是「隊列」的思想錯誤了,還是onclick=xxx,不是我想的那麼簡單?

補充後續思考:

如果onclick賦值時有內部操作改變了執行函數的隊列,那js為什麼要這麼做呢?


合理猜測:給onclick賦值的內部操作時,remove掉原來的,add上新的。


路過

不同瀏覽器不一定是這個結果

底層代碼肯定不是JS

僅趴了機器上幾年前最老的blink代碼看了下

EventListenerMap 里靠的是 EventListenerVector

這玩意就是個 Vector

typedef Vector&

這麼搞的

onclick setting 時候是 vector-&>find 後沒有對應 handle

再 append 進去的

再次 setting 時是 find 有

就先 remove 老的再 append

沒見 Vector 有用到(定義過) replace 方法

所以(在這麼實現的瀏覽器上)才有這種現象

最終還是輪子哥猜對了。


訂閱者模式,內部應該是通過一個列表來維護事件響應的,所以每次為OnClick賦值的時候會替換原來的事件委託。


這個ff和chrome/safari行為是不一致的(edge未測試)。按照現在的規範(https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-attributes:event-handlers-12),blink/webkit的行為是錯誤的。

嗯,恭喜輪子猜對了一個錯誤的實現。


js就是猜的人多,而不去看源碼或規範,所以有一堆誤導大眾的文章


你這是毛病得改,對未在需求文檔中定義的行為不要妄加猜測,因為說不定哪天就改了呢。

這個可能是listener和onclick一個是列表,一個是屬性,屬性賦值自然覆蓋。先執行屬性,再執行監聽器列表。

當然也可以有其它和實現,那表現就不一樣了,你依賴這種調用順序是錯誤的,我完全可以所有listener並發,符合需求,卻讓你的程序出bug,你這就是作死,不要學習那些上古遺留的糟粕


屬性,setter方法,去掉舊的,加上新的……你懂了吧,我猜的


跟所謂的主線程隊列裡面加入子線程是不是一個道理(疑惑


輪子哥猜中了。就是隊列,出了重新進,就從隊尾到隊頭了


推薦閱讀:

有哪些常用軟體是用WEB前端技術寫的?底層使用瀏覽器殼?
我可以只用flex布局嗎?
網站的 logo 圖片用 img 標籤還是背景圖片合適?
以下 CSS 柵格布局除了用 table 以外,有什麼其他的方法嗎?
到底 CSS 還有必要語義么?若沒有必要,類名要怎樣寫?

TAG:前端開發 | JavaScript | HTML5 |