Zepto 提供了豐富的工具函式,下面來一一解讀。
原始碼版本
本文閱讀的原始碼為 zepto1.2.0
$.extend
$.extend
方法可以用來擴充套件目標物件的屬性。目標物件的同名屬性會被源物件的屬性覆蓋。
$.extend
其實呼叫的是內部方法 extend
, 所以我們先看看內部方法 extend
的具體實現。
function extend(target, source, deep) {
for (key in source) // 遍歷源物件的屬性值
if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { // 如果為深度複製,並且源物件的屬性值為純粹物件或者陣列
if (isPlainObject(source[key]) && !isPlainObject(target[key])) // 如果為純粹物件
target[key] = {} // 如果源物件的屬性值為純粹物件,並且目標物件對應的屬性值不為純粹物件,則將目標物件對應的屬性值置為空物件
if (isArray(source[key]) && !isArray(target[key])) // 如果源物件的屬性值為陣列,並且目標物件對應的屬性值不為陣列,則將目標物件對應的屬性值置為空陣列
target[key] = []
extend(target[key], source[key], deep) // 遞迴呼叫extend函式
} else if (source[key] !== undefined) target[key] = source[key] // 不對undefined值進行復制
}複製程式碼
extend
的第一個引數 taget
為目標物件, source
為源物件, deep
表示是否為深度複製。當 deep
為 true
時為深度複製, false
時為淺複製。
extend
函式用for···in
對source
的屬性進行遍歷如果
deep
為false
時,只進行淺複製,將source
中不為undefined
的值賦值到target
對應的屬性中(注意,這裡用的是!==
,不是!=
,所以只排除嚴格為undefined
的值,不包含null
)。如果source
對應的屬性值為物件或者陣列,會保持該物件或陣列的引用。如果
deep
為true
,並且source
的屬性值為純粹物件或者陣列時3.1. 如果
source
的屬性為純粹物件,並且target
對應的屬性不為純粹物件時,將target
的對應屬性設定為空物件3.2. 如果
source
的屬性為陣列,並且target
對應屬性不為陣列時,將target
的對應屬性設定為空陣列3.3. 將
source
和target
對應的屬性及deep
作為引數,遞迴呼叫extend
函式,以實現深度複製。
現在,再看看 $.extend
的具體實現
$.extend = function(target) {
var deep, args = slice.call(arguments, 1)
if (typeof target == 'boolean') {
deep = target
target = args.shift()
}
args.forEach(function(arg) { extend(target, arg, deep) })
return target
}複製程式碼
在說原理之前,先來看看 $.extend
的呼叫方式,呼叫方式如下:
$.extend(target, [source, [source2, ...]])
或
$.extend(true, target, [source, ...])複製程式碼
在 $.extend
中,如果不需要深度複製,第一個引數可以是目標物件 target
, 後面可以有多個 source
源物件。如果需要深度複製,第一個引數為 deep
,第二個引數為 target
,為目標物件,後面可以有多個 source
源物件。
$.extend
函式的引數設計得很優雅,不需要深度複製時,可以不用顯式地將 deep
置為 false
。這是如何做到的呢?
在 $.extend
函式中,定義了一個陣列 args
,用來接受除第一個引數外的所有引數。
然後判斷第一個引數 target
是否為布林值,如果為布林值,表示第一個引數為 deep
,那麼第二個才為目標物件,因此需要重新為 target
賦值為 args.shift()
。
最後就比較簡單了,迴圈源物件陣列 args
, 分別呼叫 extend
方法,實現對目標物件的擴充套件。
$.each
$.each
用來遍歷陣列或者物件,原始碼如下:
$.each = function(elements, callback) {
var i, key
if (likeArray(elements)) { // 類陣列
for (i = 0; i < elements.length; i++)
if (callback.call(elements[i], i, elements[i]) === false) return elements
} else { // 物件
for (key in elements)
if (callback.call(elements[key], key, elements[key]) === false) return elements
}
return elements
}複製程式碼
先來看看呼叫方式:$.each(collection, function(index, item){ ... })
$.each
接收兩個引數,第一個引數 elements
為需要遍歷的陣列或者物件,第二個 callback
為回撥函式。
如果 elements
為陣列,用 for
迴圈,呼叫 callback
,並且將陣列索引 index
和元素值 item
傳給回撥函式作為引數;如果為物件,用 for···in
遍歷屬性值,並且將屬性 key
及屬性值傳給回撥函式作為引數。
注意回撥函式呼叫了 call
方法,call
的第一個引數為當前元素值或當前屬性值,所以回撥函式的上下文變成了當前元素值或屬性值,也就是說回撥函式中的 this
指向的是 item
。這在dom集合的遍歷中相當有用。
在遍歷的時候,還對回撥函式的返回值進行判斷,如果回撥函式返回 false
(if (callback.call(elements[i], i, elements[i]) === false)
) ,立即中斷遍歷。
$.each
呼叫結束後,會將遍歷的陣列或物件( elements
)返回。
$.map
可以遍歷陣列(類陣列)或物件中的元素,根據回撥函式的返回值,將返回值組成一個新的陣列,並將該陣列扁平化後返回,會將 null
及 undefined
排除。
$.map = function(elements, callback) {
var value, values = [],
i, key
if (likeArray(elements))
for (i = 0; i < elements.length; i++) {
value = callback(elements[i], i)
if (value != null) values.push(value)
}
else
for (key in elements) {
value = callback(elements[key], key)
if (value != null) values.push(value)
}
return flatten(values)
}複製程式碼
先來看看呼叫方式: $.map(collection, function(item, index){ ... })
elements
為類陣列或者物件。callback
為回撥函式。當為類陣列時,用 for
迴圈,當為物件時,用 for···in
迴圈。並且將對應的元素(屬性值)及索引(屬性名)傳遞給回撥函式,如果回撥函式的返回值不為 null
或者 undefined
,則將返回值存入新陣列中,最後將新陣列扁平化後返回。
$.camelCase
該方法是將字串轉換成駝峰式的字串
$.camelCase = camelize複製程式碼
$.camelCase
呼叫的是內部方法 camelize
,該方法在前一篇文章《讀Zepto原始碼之內部方法》中已有闡述,本篇文章就不再展開。
$.contains
用來檢查給定的父節點中是否包含有給定的子節點,原始碼如下:
$.contains = document.documentElement.contains ?
function(parent, node) {
return parent !== node && parent.contains(node)
} :
function(parent, node) {
while (node && (node = node.parentNode))
if (node === parent) return true
return false
}複製程式碼
先來看看呼叫:$.contains(parent, node)
引數 parent
為父子點,node
為子節點。
$.contains
的主體是一個三元表示式,返回的是一個匿名函式。三元表示式的條件是 document.documentElement.contains
, 用來檢測瀏覽器是否支援 contains
方法,如果支援,則直接呼叫 contains
方法,並且將 parent
和 node
為同一個元素的情況排除。
否則,返回另一外匿名函式。該函式會一直向上尋找 node
元素的父元素,如果能找到跟 parent
相等的父元素,則返回 true
, 否則返回 false
$.grep
該函式其實就是陣列的 filter
函式
$.grep = function(elements, callback) {
return filter.call(elements, callback)
}複製程式碼
從原始碼中也可以看出,$.grep
呼叫的就是陣列方法 filter
$.inArray
返回指定元素在陣列中的索引值
$.inArray = function(elem, array, i) {
return emptyArray.indexOf.call(array, elem, i)
}複製程式碼
先來看看呼叫 $.inArray(element, array, [fromIndex])
第一個引數 element
為指定的元素,第二個引數為 array
為陣列, 第三個引數 fromIndex
為可選引數,表示從哪個索引值開始向後查詢。
$.inArray
其實呼叫的是陣列的 indexOf
方法,所以傳遞的引數跟 indexOf
方法一致。
$.isArray
判斷是否為陣列
$.isArray = isArray複製程式碼
$.isArray
呼叫的是內部方法 isArray
,該方法在前一篇文章《讀Zepto原始碼之內部方法》中已有闡述。
$.isFunction
判讀是否為函式
$.isFunction = isFunction複製程式碼
$.isFunction
呼叫的是內部方法 isFunction
,該方法在前一篇文章《讀Zepto原始碼之內部方法》中已有闡述。
$.isNumeric
是否為數值
$.isNumeric = function(val) {
var num = Number(val), // 將引數轉換為Number型別
type = typeof val
return val != null &&
type != 'boolean' &&
(type != 'string' || val.length) &&
!isNaN(num) &&
isFinite(num)
|| false
}複製程式碼
判斷是否為數值,需要滿足以下條件
- 不為
null
- 不為布林值
- 不為NaN(當傳進來的引數不為數值或如
'123'
這樣形式的字串時,都會轉換成NaN) - 為有限數值
- 當傳進來的引數為字串的形式,如
'123'
時,會用到下面這個條件來確保字串為數字的形式,而不是如123abc
這樣的形式。(type != 'string' || val.length) && !isNaN(num)
。這個條件的包含邏輯如下:如果為字串型別,並且為字串的長度大於零,並且轉換成陣列後的結果不為NaN,則斷定為數值。(因為Number('')
的值為0
)
$.isPlainObject
是否為純粹物件,即以 {}
常量或 new Object()
建立的物件
$.isPlainObject = isPlainObject複製程式碼
$.isPlainObject
呼叫的是內部方法isPlainObject
,該方法在前一篇文章《讀Zepto原始碼之內部方法》中已有闡述。
$.isWindow
是否為瀏覽器的 window
物件
$.isWindow = isWindow複製程式碼
$.isWindow
呼叫的是內部方法 isWindow
,該方法在前一篇文章《讀Zepto原始碼之內部方法》中已有闡述。
$.noop
空函式
$.noop = function() {}複製程式碼
這個在需要傳遞迴調函式作為引數,但是又不想在回撥函式中做任何事情的時候會非常有用,這時,只需要傳遞一個空函式即可。
$.parseJSON
將標準JSON格式的字串解釋成JSON
if (window.JSON) $.parseJSON = JSON.parse複製程式碼
其實就是呼叫原生的 JSON.parse
, 並且在瀏覽器不支援的情況下,zepto
還不提供這個方法。
$.trim
刪除字串頭尾的空格
$.trim = function(str) {
return str == null ? "" : String.prototype.trim.call(str)
}複製程式碼
如果引數為 null
或者 undefined
,則直接返回空字串,否則呼叫字串原生的 trim
方法去除頭尾的空格。
$.type
型別檢測
$.type = type複製程式碼
$.type
呼叫的是內部方法 type
,該方法在前一篇文章《讀Zepto原始碼之內部方法》中已有闡述。
能檢測的型別有 "Boolean Number String Function Array Date RegExp Object Error"
系列文章
參考
最後,所有文章都會同步傳送到微信公眾號上,歡迎關注,歡迎提意見:
作者:對角另一面