1. 程式人生 > >jq中extend函式中的deep引數

jq中extend函式中的deep引數

       昨天在研究繼續研究JS外掛的時候注意到了extend這個函式,然後就用幾個示例去加深對它的印象,不過在過程中對於deep這個引數產生了疑惑。當jq1.1.4版本後extend這個函式增加了一個引數deep,用來進行選擇是否深拷貝,深淺拷貝這裡我就不說了,這裡直接用例子來看一下

給出3個例子,大家應該就明白了:

1.我們可以發現當我們去改變a中的age值的時候,b中改變

var a = {tom: { age: 15 }};
var b = { tom: { age: 14 } }
extend(a, b);
a.tom.age = 25;
console.log(a.tom.age); //25
console.log(b.tom.age);//25

2.我們可以發現當我們去改變a中的age值的時候,b中沒有改變

var a = {tom: { age: 15 }};
var b = { tom: { age: 14 } }
extend(true,a, b);
a.tom.age = 25;
console.log(a.tom.age); //25
console.log(b.tom.age);//14

3.我們可以發現當我們去改變a中的age值的時候,b中沒有改變

var a = {tom: { age: 15 }};
var b = { tom: { age: 14 } }
extend(false,a, b);
a.tom.age = 25;
console.log(a.tom.age); //25
console.log(b.tom.age);//14

我們可以發現3個例子的區別就是第一個引數deep是否存在和為true和false,而且第一個例子是淺拷貝,第二個和第三個都是深拷貝

這裡可以有2個結論:

1.這個函式預設就是淺拷貝

2.傳true和false都是深拷貝

一開始對於第2個結論很不理解,講道理true為深拷貝,那麼false肯定為淺拷貝呀,為什麼都一樣呢?在網上查了很多資料,發現還是不明白,後來去官網查了一下知道(證明以後我們查東西還是去官網比較靠譜,網上很多部落格都是斷章取義),第一個引數不能為false。https://api.jquery.com/jquery.extend/  (可以去看中文的文件,也可以用chrome瀏覽器翻譯)

Warning: Passingfalse for the first argument is not supported.

原來不能傳false,之所以這樣是我們一直都是慣性思維,理解true為深,那麼false肯定為淺咯~   -。-(慣性思維害死人,我還一直糾結為什麼會這樣)

其實到這裡我們都是知其然,不知其所以然,在糾結這個問題的過程中,我查了原始碼,終於知道其所以然了。

target =  arguments [ 0 ] | | { }  

 //就是這一句話~(大家可以定位到這句話),它是把這個函式的第一個引數傳遞給target,最重要的是這裡用了一個 | | 或,如果當我第一個是假的話就直接賦值一個空物件給target,這樣就可以理解,為什麼我們可以傳true不能傳false了,當我們傳true的時候為真,它就直接把true給它了,當我傳false的時候為假,它就會把一個空物件給target。

因為上面這個條件,我們就可以解釋為什麼當進行後面那個判斷時,我傳false會進不去的原因了,就是因為typeof (target)為object!!!!!

if ( typeof (target) === "boolean" ) {//如果第一個引數是boolean型別,則將該引數賦給deep,即是否深拷貝
        deep = target;
         target = arguments[1] || {};

 // skip the boolean and the target 
         i = 2;
     }

然後再看一個例子:

var empty={};
var a = {tom: { age: 15 }};
var b = { tom: { age: 14 } }
extend(empty,a,b);
empty.tom.age = 25;
console.log(empty.tom.age);//25
console.log(a.tom.age); //15
console.log(b.tom.age);//25

我們可以發現,當我們去淺拷貝的時候,它只是會影響到最後一個物件的引數,這個也可以理解,因為後面的引數會覆蓋前面的,所以淺拷貝的時候,最後被合併的物件(empty)引用的是最後一個引數物件( b )的屬性。

所以當我們對外掛引數進行覆蓋的時候最好用這種方式:

第一個引數為true,第二個引數為空物件,就是為了不讓其他物件的值被覆蓋掉。

var empty={};
var a = {tom: { age: 15 }};
var b = { tom: { age: 14 } }
empty=extend(true,{},a,b);
empty.tom.age = 25;
console.log(empty.tom.age);//25
console.log(a.tom.age); //15
console.log(b.tom.age);//14

最後附上別人對這個函式的原始碼解析:

jQuery.extend = jQuery.fn.extend = function() {
    /*
        傳入的物件分為擴充套件物件和被擴充套件物件
    */
    var options, name, src, copy, copyIsArray, clone, //
        target = arguments[0] || {},    //被擴充套件的物件
        i = 1,                          //設定擴充套件物件的起始值,預設從第二項開始
        length = arguments.length,      //傳遞引數的個數,以便下面迴圈擴充套件物件使用
        deep = false;                   //預設淺複製

    /*
        處理深層拷貝或淺拷貝情況
        extend(Boolean,src1,src2..srcN);
    */
    if ( typeof target === "boolean" ) {

        deep = target;  //將deep設為target,此時target為傳進來的Boolean值,true or false;

        target = arguments[ i ] || {};  //重新設定被擴充套件物件,為引數的第二項

        i++;    //重設擴充套件物件的起始值,從第三項開始
    }

    /*
        被擴充套件的不是物件或函式,可能是String,Number或其他;
        extend("",src1,src2...srcN);
    */

    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {}; //重新設定target的值為空物件
    }

    /*
        當只傳入一個物件
        extend(src1);
        將target設為jQuery物件或者jQuery.prototype,來擴充套件jQuery靜態屬性方法或是例項屬性方法
        $.extend(src1);   //擴充套件jQuery物件
        $.fn.extend(src1) //擴充套件jQuery.prototype
    */
    if ( i === length ) {
        target = this;
        i--;    //重設擴充套件物件起始值,從第0個開始
    }

    /*
        被擴充套件物件和擴充套件物件所有情況處理完畢,開始迴圈進行拷貝
        對從i開始的多個引數進行遍歷
    */

    for ( ; i < length; i++ ) {
        
        if ( (options = arguments[ i ]) != null ) {  //只處理有定義擴充套件物件
            //擴充套件基本物件
            for ( name in options ) {       //迴圈每一項擴充套件物件
                src = target[ name ];       
                copy = options[ name ];     

                // 防止迴圈引用,window === window.window.window
                if ( target === copy ) {
                    continue;
                }

                /*
                    物件或陣列做深拷貝
                    deep:判斷是否要深拷貝
                    copy:保證copy存在
                    jQuery.isPlainObject:判斷copy是否是一個純粹的物件,通過{} 或 new Object 建立
                    jQuery.isArray:判斷是否為陣列
                */
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    //為陣列
                    if ( copyIsArray ) {
                        copyIsArray = false; //設為false,以便下次再重新判斷是否為陣列
                        clone = src && jQuery.isArray(src) ? src : [];  //設定clone為一個數組

                    } else {
                        clone = src && jQuery.isPlainObject(src) ? src : {}; //設定clone為一個物件
                    }

                    //遞迴深度拷貝
                    target[ name ] = jQuery.extend( deep, clone, copy );

                //過濾未定義的值
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }

    //返回修改後的物件
    return target;
};

 

相關推薦

jqextend函式deep引數

       昨天在研究繼續研究JS外掛的時候注意到了extend這個函式,然後就用幾個示例去加深對它的印象,不過在過程中對於deep這個引數產生了疑惑。當jq1.1.4版本後extend這個函式增加了一個引數deep,用來進行選擇是否深拷貝,深淺拷貝這裡我就不說了,這裡直

Linux Cmain函式引數argc和argv

                                          &

C++main函式引數argc和argv的使用例項

含義解釋 (1).int argc:即為arguments count(引數數) 執行程式傳送給main函式命令列引數的總數,包括可執行程式名。當argc=1時表示只有一個程式名稱,此時儲存在argv

JS建構函式有return的分析

目錄 寫在前面 demo幫助理解 寫在前面 簡單的總結來說就一句話:如果return的值是基本資料型別的話,則忽略return,直接返回例項化的物件;如果return的值是引用型別的話,則不再返回例項化的物件,而是直接返回return返回的引用型別的值。 demo幫助理解

javamain函式的變數定義及其使用

public class exe1 { static int i=10; int k=3; public static void main(String[] args) {   k=5; System.out.println("i="+i); System.out.println("k="+k); } } 報

MFCSetWorldTransform函式XFORM結構體的使用

使用XFORM來控制DC時,需要先設定繪圖模式SetGraphicsMode為GM_ADVANCED,再用SetWorldTransform。否則SetWorldTransform函式會失敗對映後的座標與對映前的座標關係為x' = x * eM11 + y * eM21 +

[Python筆記]函式關鍵字引數,收集引數與分配引數的使用例項

Stock類 class Stock: def __init__(self): self.itemList = [] def addItem(self, name, price): # 建立商品 return {'name': name

c和C++main函式引數的意義和用法

main函式是C++的主函式,除了程式本身外,任何函式都不可以呼叫main函式。main函式中可以新增引數,也可以不寫。main函式預設有兩個引數,main(int argc, char ** argv),其中,argc是用來表面main函式究竟呼叫了幾個引數,因為程式本身的檔名就是一個

JS函式引數傳遞到底是按值傳遞還是按引用傳遞

首先我們知道JS中的資料型別大致可以分為簡單資料型別和複雜資料型別; 當我們宣告一個變數並給它賦值時,可以賦給其簡單值和複雜值(以下堆記憶體和棧記憶體的地址表示均隨意取的,只是為了區分,不代表真實的記憶體地址); 針對簡單資料型別: 例1 var simpleData1 = 18 v

main函式兩個引數的用法總結

1、定義  C語言規定main函式的引數只能有兩個,習慣上這兩個引數寫為argc和argv。因此,main函式的函式頭可寫為: main (argc,argv)C語言還規定argc(第一個形參)必須是整型變數,argv( 第二個形參)必須是指向字串的指標陣列。加上形參說明後,main函式的

getopt----解析main函式引數

   轉自------  Linux下getopt()函式的簡單使用   "a:b:cd::e",這就是一個選項字串。對應到命令列就是-a ,-b ,-c ,-d, -e 。冒號又是什麼呢? 冒號表示引數,一個冒號就表示這個選項後面必須

不可不知:函式預設引數的陷阱

現象 def foo(x, y=[]): y.append(x) return y print(foo(1)) print(foo(1, [3, 4])) print(foo(5)) ''' [1] [3, 4, 1] [1, 5] '''       

函式引數為object... 和 object[] 的區別

先給出兩個示例函式 方法1: public void testobject(object... params){ ///省略此處程式碼 } 方法2: public void testobject(object[] params){ ///省略此處程式碼 }   區別

Java程式利用main函式args引數實現引數的傳遞

1.執行Java程式的同時,可以通過輸入引數給main函式中的接收引數陣列args[],供程式內部使用!即當你在Java命令列後面帶上引數,Java虛擬機器就直接把它們存放到了main方法中的引數String數組裡了。 2..args是Java命令列引數,因為引數可以為多個,所以要用陣列來存

Python函式定義及引數例項

1.函式定義 函式就是完成特定功能的一個語句組,這組語句可以作為一個單位使用,並且給它取一個名字 ,可以通過函式名在程式的不同地方多次執行(這通常叫函式呼叫) 預定義函式(可以直接使用) 自定義函式(自己編寫) 為什麼使用函式? 降低程式設計難度,通常將

Python函式多型別傳值和冗餘引數函式的遞迴呼叫

1.多型別傳值和冗餘引數 多型別傳值: def fun(x,y): return x +y print fun(3,5) 8 print fun(*t) 3 def fun(x,y,z): return x + y + z t1 = (1,2,3)

Python函式引數【轉載】

原文地址:廖雪峰的官方網站:函式的引數. 定義函式的時候,我們把引數的名字和位置確定下來,函式的介面定義就完成了。對於函式的呼叫者來說,只需要知道如何傳遞正確的引數,以及函式將返回什麼樣的值就夠了,函式內部的複雜邏輯被封裝起來,呼叫者無需瞭解。 Python的函式定義非常簡單,但靈活

python函式的實參和形參以及預設引數和收集引數

一.實參和形參 例項:>>> def MyFirstFunction(name):                    ”函式定義過程中的hame是叫形參“ &nb

C語言:函式引數的傳值與傳地址

任務程式碼: #include <stdio.h> void swap(int *a ,int *b)//按之前對指標認識,*a代表指標變數a,a儲存的是地址,*a是地址的值。 { //但是可以看到下面傳輸過程中swap(

js拼接字串含有帶空格的引數函式(對前一篇的補充)

        針對前一篇的例子,有的時候會偷個小懶,會寫成如下程式碼: "<a href=javascript:getProductDetail('"+result[i].spec+"','"+result[i].name+"')>" &