源碼版本
本文閱讀的源碼為 zepto1.2.0
$.extend
$.extend
方法可以用來擴展目標對象的屬性。目標對象的同名屬性會被源對象的屬性覆蓋。
$.extend
其實調(diào)用的是內(nèi)部方法 extend
, 所以我們先看看內(nèi)部方法 extend
的具體實現(xiàn)。
function extend(target, source, deep) { for (key in source) // 遍歷源對象的屬性值 if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { // 如果為深度復制,并且源對象的屬性值為純粹對象或者數(shù)組 if (isPlainObject(source[key]) && !isPlainObject(target[key])) // 如果為純粹對象 target[key] = {} // 如果源對象的屬性值為純粹對象,并且目標對象對應的屬性值不為純粹對象,則將目標對象對應的屬性值置為空對象 if (isArray(source[key]) && !isArray(target[key])) // 如果源對象的屬性值為數(shù)組,并且目標對象對應的屬性值不為數(shù)組,則將目標對象對應的屬性值置為空數(shù)組 target[key] = [] extend(target[key], source[key], deep) // 遞歸調(diào)用extend函數(shù) } else if (source[key] !== undefined) target[key] = source[key] // 不對undefined值進行復制 }
extend
的第一個參數(shù) taget
為目標對象, source
為源對象, deep
表示是否為深度復制。當 deep
為 true
時為深度復制, false
時為淺復制。
extend
函數(shù)用for···in
對source
的屬性進行遍歷如果
deep
為false
時,只進行淺復制,將source
中不為undefined
的值賦值到target
對應的屬性中(注意,這里用的是!==
,不是!=
,所以只排除嚴格為undefined
的值,不包含null
)。如果source
對應的屬性值為對象或者數(shù)組,會保持該對象或數(shù)組的引用。如果
deep
為true
,并且source
的屬性值為純粹對象或者數(shù)組時
3.1. 如果 source
的屬性為純粹對象,并且 target
對應的屬性不為純粹對象時,將 target
的對應屬性設(shè)置為空對象
3.2. 如果 source
的屬性為數(shù)組,并且 target
對應屬性不為數(shù)組時,將 target
的對應屬性設(shè)置為空數(shù)組
3.3. 將 source
和 target
對應的屬性及 deep
作為參數(shù),遞歸調(diào)用 extend
函數(shù),以實現(xiàn)深度復制。
現(xiàn)在,再看看 $.extend
的具體實現(xiàn)
$.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
的調(diào)用方式,調(diào)用方式如下:
$.extend(target, [source, [source2, ...]]) 或$.extend(true, target, [source, ...])
在 $.extend
中,如果不需要深度復制,第一個參數(shù)可以是目標對象 target
, 后面可以有多個 source
源對象。如果需要深度復制,第一個參數(shù)為 deep
,第二個參數(shù)為 target
,為目標對象,后面可以有多個 source
源對象。
$.extend
函數(shù)的參數(shù)設(shè)計得很優(yōu)雅,不需要深度復制時,可以不用顯式地將 deep
置為 false
。這是如何做到的呢?
在 $.extend
函數(shù)中,定義了一個數(shù)組 args
,用來接受除第一個參數(shù)外的所有參數(shù)。
然后判斷第一個參數(shù) target
是否為布爾值,如果為布爾值,表示第一個參數(shù)為 deep
,那么第二個才為目標對象,因此需要重新為 target
賦值為 args.shift()
。
最后就比較簡單了,循環(huán)源對象數(shù)組 args
, 分別調(diào)用 extend
方法,實現(xiàn)對目標對象的擴展。
$.each
$.each
用來遍歷數(shù)組或者對象,源碼如下:
$.each = function(elements, callback) { var i, key if (likeArray(elements)) { // 類數(shù)組 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 }
先來看看調(diào)用方式:$.each(collection, function(index, item){ ... })
$.each
接收兩個參數(shù),第一個參數(shù) elements
為需要遍歷的數(shù)組或者對象,第二個 callback
為回調(diào)函數(shù)。
如果 elements
為數(shù)組,用 for
循環(huán),調(diào)用 callback
,并且將數(shù)組索引 index
和元素值 item
傳給回調(diào)函數(shù)作為參數(shù);如果為對象,用 for···in
遍歷屬性值,并且將屬性 key
及屬性值傳給回調(diào)函數(shù)作為參數(shù)。
注意回調(diào)函數(shù)調(diào)用了 call
方法,call
的第一個參數(shù)為當前元素值或當前屬性值,所以回調(diào)函數(shù)的上下文變成了當前元素值或?qū)傩灾?,也就是說回調(diào)函數(shù)中的 this
指向的是 item
。這在dom集合的遍歷中相當有用。
在遍歷的時候,還對回調(diào)函數(shù)的返回值進行判斷,如果回調(diào)函數(shù)返回 false
(if (callback.call(elements[i], i, elements[i]) === false)
) ,立即中斷遍歷。
$.each
調(diào)用結(jié)束后,會將遍歷的數(shù)組或?qū)ο螅?nbsp;elements
)返回。
$.map
可以遍歷數(shù)組(類數(shù)組)或?qū)ο笾械脑?,根?jù)回調(diào)函數(shù)的返回值,將返回值組成一個新的數(shù)組,并將該數(shù)組扁平化后返回,會將 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) }
先來看看調(diào)用方式: $.map(collection, function(item, index){ ... })
elements
為類數(shù)組或者對象。callback
為回調(diào)函數(shù)。當為類數(shù)組時,用 for
循環(huán),當為對象時,用 for···in
循環(huán)。并且將對應的元素(屬性值)及索引(屬性名)傳遞給回調(diào)函數(shù),如果回調(diào)函數(shù)的返回值不為 null
或者 undefined
,則將返回值存入新數(shù)組中,最后將新數(shù)組扁平化后返回。
$.camelCase
該方法是將字符串轉(zhuǎn)換成駝峰式的字符串
$.camelCase = camelize
$.camelCase
調(diào)用的是內(nèi)部方法 camelize
,該方法在前一篇文章《讀Zepto源碼之內(nèi)部方法》中已有闡述,本篇文章就不再展開。
$.contains
用來檢查給定的父節(jié)點中是否包含有給定的子節(jié)點,源碼如下:
$.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 }
先來看看調(diào)用:$.contains(parent, node)
參數(shù) parent
為父子點,node
為子節(jié)點。
$.contains
的主體是一個三元表達式,返回的是一個匿名函數(shù)。三元表達式的條件是 document.documentElement.contains
, 用來檢測瀏覽器是否支持 contains
方法,如果支持,則直接調(diào)用 contains
方法,并且將 parent
和 node
為同一個元素的情況排除。
否則,返回另一外匿名函數(shù)。該函數(shù)會一直向上尋找 node
元素的父元素,如果能找到跟 parent
相等的父元素,則返回 true
, 否則返回 false
$.grep
該函數(shù)其實就是數(shù)組的 filter
函數(shù)
$.grep = function(elements, callback) { return filter.call(elements, callback) }
從源碼中也可以看出,$.grep
調(diào)用的就是數(shù)組方法 filter
$.inArray
返回指定元素在數(shù)組中的索引值
$.inArray = function(elem, array, i) { return emptyArray.indexOf.call(array, elem, i) }
先來看看調(diào)用 $.inArray(element, array, [fromIndex])
第一個參數(shù) element
為指定的元素,第二個參數(shù)為 array
為數(shù)組, 第三個參數(shù) fromIndex
為可選參數(shù),表示從哪個索引值開始向后查找。
$.inArray
其實調(diào)用的是數(shù)組的 indexOf
方法,所以傳遞的參數(shù)跟 indexOf
方法一致。
$.isArray
判斷是否為數(shù)組
$.isArray = isArray
$.isArray
調(diào)用的是內(nèi)部方法 isArray
,該方法在前一篇文章《讀Zepto源碼之內(nèi)部方法》中已有闡述。
$.isFunction
判讀是否為函數(shù)
$.isFunction = isFunction
$.isFunction
調(diào)用的是內(nèi)部方法 isFunction
,該方法在前一篇文章《讀Zepto源碼之內(nèi)部方法》中已有闡述。
$.isNumeric
是否為數(shù)值
$.isNumeric = function(val) { var num = Number(val), // 將參數(shù)轉(zhuǎn)換為Number類型 type = typeof val return val != null && type != 'boolean' && (type != 'string' || val.length) && !isNaN(num) && isFinite(num) || false }
判斷是否為數(shù)值,需要滿足以下條件
不為
null
不為布爾值
不為NaN(當傳進來的參數(shù)不為數(shù)值或如
'123'
這樣形式的字符串時,都會轉(zhuǎn)換成NaN)為有限數(shù)值
當傳進來的參數(shù)為字符串的形式,如
'123'
時,會用到下面這個條件來確保字符串為數(shù)字的形式,而不是如123abc
這樣的形式。(type != 'string' || val.length) && !isNaN(num)
。這個條件的包含邏輯如下:如果為字符串類型,并且為字符串的長度大于零,并且轉(zhuǎn)換成數(shù)組后的結(jié)果不為NaN,則斷定為數(shù)值。(因為Number('')
的值為0
)
$.isPlainObject
是否為純粹對象,即以 {}
常量或 new Object()
創(chuàng)建的對象
$.isPlainObject = isPlainObject
$.isPlainObject
調(diào)用的是內(nèi)部方法isPlainObject
,該方法在前一篇文章《讀Zepto源碼之內(nèi)部方法》中已有闡述。
$.isWindow
是否為瀏覽器的 window
對象
$.isWindow = isWindow
$.isWindow
調(diào)用的是內(nèi)部方法 isWindow
,該方法在前一篇文章《讀Zepto源碼之內(nèi)部方法》中已有闡述。
$.noop
空函數(shù)
$.noop = function() {}
這個在需要傳遞回調(diào)函數(shù)作為參數(shù),但是又不想在回調(diào)函數(shù)中做任何事情的時候會非常有用,這時,只需要傳遞一個空函數(shù)即可。
$.parseJSON
將標準JSON格式的字符串解釋成JSON
if (window.JSON) $.parseJSON = JSON.parse
其實就是調(diào)用原生的 JSON.parse
, 并且在瀏覽器不支持的情況下,zepto
還不提供這個方法。
$.trim
刪除字符串頭尾的空格
$.trim = function(str) { return str == null ? "" : String.prototype.trim.call(str)}
如果參數(shù)為 null
或者 undefined
,則直接返回空字符串,否則調(diào)用字符串原生的 trim
方法去除頭尾的空格。
$.type
類型檢測
$.type = type
$.type
調(diào)用的是內(nèi)部方法 type
,該方法在前一篇文章《讀Zepto源碼之內(nèi)部方法》中已有闡述。
能檢測的類型有 "Boolean Number String Function Array Date RegExp Object Error"
系列文章
參考
作者:對角另一面
標簽: zepto, 前端, javascript, jquery, 源碼解讀
http://www.cnblogs.com/hefty/p/6788852.html