讀zepto源碼之工具函數
讀zepto源碼之工具函數
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"
參考
- Zepto中文文檔
- Node.contains()
- Array.prototype.indexOf()
- String.prototype.trim()
文章來源:https://github.com/yeyuqiudeng/reading-zepto/blob/master/src/%E8%AF%BBZepto%E6%BA%90%E7%A0%81%E4%B9%8B%E5%B7%A5%E5%85%B7%E5%87%BD%E6%95%B0.md
讀zepto源碼之工具函數