解析jQuery.extend和淘寶KISSY.mix方法源碼


一、jQuery.extend方法

1、用途

     jQuery.extend方法是將多個對象(提供對象)的屬性(包括原型中的屬性)復制給另一個對象(要擴展的目標對象),使目標對象增強行為;當提供對象有而目標對象沒有的屬性(包括方法),則直接復制給目標對象,

當它們有相同的屬性名(即key鍵相同),且值為對象,設置參數deep = true時,數組和簡單對象會遞歸合並,否則直接覆蓋,不會合並。

 

2、用法

jQuery.extend( target, [ object1 ], [ objectN ] )
target 一個對象,如果附加的對象被傳遞給這個方法那么它將接收新的屬性,如果它是唯一的參數將擴展jQuery的命名空間
object1 一個對象,它包含額外的屬性合並到第一個參數

objectN 包含額外的屬性合並到第一個參數

jQuery.extend( [ deep ], target, object1, [ objectN ] )
deep 如果是true,合並成為遞歸(又叫做深拷貝)。
target 對象擴展。這將接收新的屬性。
object1 一個對象,它包含額外的屬性合並到第一個參數
objectN 包含額外的屬性合並到第一個參數

3、源碼解析
// 源碼解析 update-time:2014/05/20
(function( window, undefined ) {
    var jQuery = function() {
        // ...
    };
    jQuery.extend = function() {
        // 提供合並的對象,提供合並對象的屬性,目標對象的屬性值,提供合並對象的屬性值
        // 布爾值(判斷提供的合並對象屬性值類型是否為數組),遞歸中的目標對象(目標對象的屬性),目標對象
        var options, name, src, copy, copyIsArray, clone,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false;

        // 處理第一個參數為boolean類型
        if ( typeof target === "boolean" ) {
            deep = target;
            target = arguments[1] || {};
            // 略過第一個boolean類型參數和目標擴展對象,提供合並屬性的對象從第三個參數開始
            i = 2;
        }

        // 目標參數類型不是對象、函數,則重置為一個新的空對象
        if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
            target = {};
        }

        // 當參數只有一個,擴展jQuery本身( this指向jQuery )
        if ( length === i ) {
            target = this;
            // 提供合並屬性的對象從第一個參數開始
            --i;
        }

        // 枚舉提供合並的對象
        for ( ; i < length; i++ ) {
            // 提供合並的對象不為null,注意這里比較的是值,不包括null類型
            if ( (options = arguments[ i ]) != null ) {
                // 枚舉提供合並對象的屬性
                for ( name in options ) {
                    src = target[ name ];
                    copy = options[ name ];
     
                    // 當目標對象與被復制屬性值指向同一引用,則跳出本次循環
                    if ( target === copy ) {
                        continue;
                    }
     
                    // 被復制屬性值的類型為對象、數組
                    if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                        // 指定遞歸中要擴展的目標為數組
                        if ( copyIsArray ) {
                            copyIsArray = false; // 需要重置為false,因為jQuery.isPlainObject(copy)為true時,始終都是執行第一個if語句
                            clone = src && jQuery.isArray(src) ? src : [];
                        } 
                        // 指定遞歸中要擴展的目標為對象
                        else {
                            clone = src && jQuery.isPlainObject(src) ? src : {};
                        }

                        // 使用jQuery.extend方法進行遞歸
                        target[ name ] = jQuery.extend( deep, clone, copy );
     
                    // 被復制屬性值的類型不是對象、數組、undefined,則將被復制屬性值賦值到目標對象中
                    } else if ( copy !== undefined ) {
                        target[ name ] = copy;
                    }
                }
            }
        }
     
        // 返回被合並的對象,即目標對象
        return target;
    };
     
    // 擴展jQuery命名空間下的方法,增強行為
    jQuery.extend({
        method: function () {
            console.log('test');
        },
        noConflict: function () {
            //...   
        },
        isReady: false,
        // ...
    });
 
    window.jQuery = window.$ = jQuery;
 
})( window );
View Code
    測試代碼如:
jQuery.method(); // test
    注意:通過jQuery.extend擴展jQuery本身的方法,是jQuery的靜態方法,不能在帶有選擇器的jQuery對象上使用。


二、KISSY.mix方法(KISSY1.20版本)

1、用途

與jQuery.extend方法用途相似,不過KISSY.mix只允許一個提供對象參數,參數也不同。

2、用法


KISSY.mix(receiver , supplier [ , overwrite = true , whitelist , deep ]) supplier 對象的成員復制到 receiver 對象上.
 receiver (object) – 屬性接受者對象.
 supplier (object) – 屬性來源對象.
 overwrite (boolean) – 是否覆蓋接受者同名屬性.
 whitelist (Array<string>) – 屬性來源對象的屬性白名單, 僅在名單中的屬性進行復制.
 deep (boolean) – 是否進行深度 mix (deep copy)

3、源碼解析
(function (S, undefined) {
    var host = this,
        meta = {
            mix:function (r, s, ov, wl, deep) {
				// 參數中只有一個對象,返回對象
                if (!s || !r) {
                    return r;
                }
				// ov為undefined,重寫為true
                if (ov === undefined) {
                    ov = true;
                }
                var i, p, len;
				// 存在白名單,將白名單中屬性(且該屬性在提供對象中)進行mix
                if (wl && (len = wl.length)) {
                    for (i = 0; i < len; i++) {
                        p = wl[i];
                        if (p in s) {
                            _mix(p, r, s, ov, deep);
                        }
                    }
				// 不存在白名單,直接mix提供對象中屬性
                } else {
                    for (p in s) {
                        _mix(p, r, s, ov, deep);
                    }
                }
				// 返回擴展對象
                return r;
            }
        },

        _mix = function (p, r, s, ov, deep) {
			// 存在ov參數且為true(會重寫同名屬性) 或者 p屬性不被r對象枚舉(表示不重寫同名屬性)
            if (ov || !(p in r)) {
                var target = r[p], src = s[p];
                // 兩個屬性值全等,函數返回為空,跳出並進行下一輪循環
                if (target === src) {
                    return;
                }
                // 來源是數組和對象,並且要求深度 mix
                if (deep && src && (S.isArray(src) || S.isPlainObject(src))) {
                    // 目標值為對象或數組,直接 mix
                    // 否則 新建一個和源值類型一樣的空數組/對象,遞歸 mix
                    var clone = target && (S.isArray(target) || S.isPlainObject(target)) ?
                                target : (S.isArray(src) ? [] : {});

                    r[p] = S.mix(clone, src, ov, undefined, true);
				
				// 擴展對象(增加對象屬性,ov為true時覆蓋對象屬性)
                } else if (src !== undefined) {
                    r[p] = s[p];
                }
            }
        },

        seed = (host && host[S]) || {};	
		// console.log(seed); //Object { }


    host = seed.__HOST || (seed.__HOST = host || {});
	// console.log(host); //window

	// window.KISSY獲取meta成員mix方法和擁有__HOST屬性(值為window)
    S = host[S] = meta.mix(seed, meta);
	
	// 擴展方法
	S.mix(S, {
		method: function () {
			console.log('test');
		},
		method1: function () {
			//...
		},
		method2: function () {
			//...
		}
		// ...
	});
	
	//返回擴展后的KISSY對象
	return S;

})('KISSY', undefined)
 
        

     測試代碼如:

KISSY.method(); //test

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM