jQuery 2.0.3 原始碼分析 鉤子機制
jQuery提供了一些快捷函式來對dom物件的屬性進行存取操作. 這一部分還是比較簡單的.
根據API這章主要是分解5個方法
- .attr() 獲取匹配的元素集合中的第一個元素的屬性的值 或 設定每一個匹配元素的一個或多個屬性。
- .prop() 獲取匹配的元素集中第一個元素的屬性(property)值或設定每一個匹配元素的一個或多個屬性。
- .removeAttr() 為匹配的元素集合中的每個元素中移除一個屬性(attribute)。
- .removeProp() 為集合中匹配的元素刪除一個屬性(property)。
- .val() 獲取匹配的元素集合中第一個元素的當前值或設定匹配的元素集合中每個元素的值
jQuery的主要工作還是為了解決瀏覽器的相容性. 這部分的方法一般都有2個特點.
- set方法和get方法一體化. 根據引數數量來判斷是set還是get.
- value可以傳入一個閉包. 這個閉包的返回值才是真正的value.
先看一組HTML結構
<input id="Aaron" type="checkbox" checked="checked" />
用attr,與prop取值出input元素上的checked
分別會取得什麼值?
$('input').attr('checked') //checked $('input').prop('checked') // true
看到這裡應該知道這兩個方法的區別了。其實從方法名也可以大致猜出來,.attr()、.prop()分別取的是節點的attribute值、property值。
attribute和property的區別
attribute:特性
- 直接寫在標籤上的屬性,可以通過setAttribute、getAttribute進行設定、讀取
property:屬性
- 通過“.”號來進行設定、讀取的屬性,就跟Javascript裡普通物件屬性的讀取差不多
觀察一張圖很直觀的理解:
attributes是一個類陣列的容器,說得準確點就是NameNodeMap,總之就是一個類似陣列但又和陣列不太一樣的容器。attributes的每個數字索引以名值對(name=”value”)的形式存放了一個attribute節點。
attributes是會隨著新增或刪除attribute節點動態更新的。
特性的操作:
- getAttribute
- setAttribute
- removeAttribute
property就是一個屬性,如果把DOM元素看成是一個普通的Object物件,那麼property就是一個以名值對(name=”value”)的形式存放在Object中的屬性。要新增和刪除property也簡單多了,和普通的物件沒啥分別。
之所以attribute和property容易混倄在一起的原因是,很多attribute節點還有一個相對應的property屬性
DOM元素一些預設常見的attribute節點都有與之對應的property屬性,比較特殊的是一些值為Boolean型別的property,如一些表單元素。
總的來說:基本可以總結為attribute節點都是在HTML程式碼中可見的,而property只是一個普通的名值對屬性
- jQuery.prototype.attr
- jQuery.prototype.prop
- jQuery.prototype.removeAttr
- jQuery.prototype.removeProp
- jQuery.prototype.val
jQuery把又長又難記的函式用外觀模式包裝成attr,prop,內部setAttribute,getAttribute是低階API,實現核心功能, 從而隱藏了使用者程式對jQuery各個模組呼叫的複雜性
看看原始碼的實現
attr: function( ele, value ) { return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); },
prop: function( name, value ) { return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); },
暴露給api的原型方法非常簡單,只有一句話.把引數交給jQuery.access函式去處理. jQuery.access主要作用是修正引數.
access函式裡的第二個引數jQuery.attr. 這個引數的作用是告訴access方法, 修正完引數後再去呼叫 jQuery.attr方法.
access方法是可以被抽象出複用的一組對引數的修正方法,通過分解成單一的資料後,然後呼叫傳遞的回撥處理鉤子 比如 attr,css, prop.等等
// Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function access: function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, length = elems.length, bulk = key == null; // Sets many values if ( jQuery.type( key ) === "object" ) { //傳遞是物件 chainable = true; for ( i in key ) { //遞迴呼叫 jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); } // Sets one value } else if ( value !== undefined ) { chainable = true; if ( !jQuery.isFunction( value ) ) { raw = true; } if ( bulk ) { // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); fn = null; // ...except when executing function values } else { bulk = fn; fn = function( elem, key, value ) { return bulk.call( jQuery( elem ), value ); }; } } if ( fn ) { for ( ; i < length; i++ ) { fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); } } } return chainable ? elems : // Gets bulk ? fn.call( elems ) : length ? fn( elems[0], key ) : emptyGet; },
access原始碼部分比較簡單, 就是物件傳參分解成單一的引數從而set,get處理
你知道一些核心jQuery函式都有自己的“外掛API”稱為“鉤子”?
大概意思如下:
jQuery提供一個API來呼叫使用者自定義的函式,用於擴充套件,以便獲取和設定特定屬性值
主要是:.attr()
, .prop()
, .val()
and .css()四種類型的處理
鉤子都有相似的結構
var someHook = { get: function(elem) { // obtain and return a value return "something"; }, set: function(elem, value) { // do something with value } }
如何使用?
在做css3屬性瀏覽器相容的時候,都需要特定的字首
Webkit的瀏覽器:-webkit-border-radius
Firefox:-moz-border-radius
此時我看可以採用一個CSS hook 可以標準化這些供應商字首的屬性,讓.css()
接受一個單一的,標準的屬性的名稱(border-radius
,或用DOM屬性的語法,borderRadius
)
判斷的程式碼省略,直接看實現
給某一元素設定borderRadius,為10px
$("#element").css("borderRadius", "10px");
為了做瀏覽器相容,我們不得不
if(webkit){ ........................ }else if(firefox){ ............................ }else if(...)更多
這是一種最沒技術含量的寫法了,如果我們換成一種hook的話
$.cssHooks.borderRadius = { get: function( elem, computed, extra ) { return $.css( elem, borderRadius ); }, set: function( elem, value) { elem.style[ borderRadius ] = value; } };
borderRadius = styleSupport( "borderRadius" ); //獲取到相對應的瀏覽器標準
這裡可能還不直觀的體現,我們深入到attr原始碼中看看
jQuery.attr 靜態方法
jQuery例項的方法都是呼叫最終的靜態方法:jQuery.attr
access函式最後把引數又傳遞給了jQuery.attr, 在jQuery.attr裡才真正進行setAttribute/getAttribute操作.
檢視原始碼關於attrHooks一個type
意思就是在使用attr(‘type’,??)設定的時候就會呼叫這個hooks,用於處理IE6-9 input屬性不可寫入的問題
attrHooks: { type: { set: function( elem, value ) { if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { // Setting the type on a radio button after the value resets the value in IE6-9 // Reset value to default in case type is set after value during creation var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } } },
省略部分程式碼attr原始碼
- 通過hooks = jQuery.attrHooks[ name ]方法,去適配對應的name,是否在合集中
- 如果是hooks然後又是get方法就呼叫 hooks && "set" in hooks && (ret = hooks.set( elem, value, name )
- 如果有ret返回值就return(hooks.set可能還不是最終匹配)
- 否則繼續往下走
其實這樣的思路,在sizzle選擇器也大量的運用了
鉤子就是介面卡原理,用來處理一些特殊的屬性,樣式或事件。而這些屬性,樣式或事件,我們可以通過瀏覽器的特徵嗅探,把相應的解決方法新增到介面卡中。有了這些介面卡,jQuery就可以省去許多if else 判定
那麼,利用鉤子處理相容與擴充套件的好處:
- 介面卡這種模式對於擴充套件新功能非常有利
- 如果採用鉤子處理的話,我們就省去了一大堆if else的分支判斷
- 由於JS用物件做為表進行查詢是比if條句與switch語句快很多
本章的重點在於如何靈活運用運用鉤子的原理,在實際專案中更好的處理相容與擴充套件
如果您看完本篇文章感覺不錯,請點選一下右下角的【推薦】來支援一下博主,謝謝!
如果是原創文章,轉載請註明出處!!!