<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                本節目標在于增加對計算機底層的一些認識,掌握后可以更清楚地明了一些"坑"產生的原因。本節內容也是一些筆面試中考察的基礎知識點,將有助于加快打通"任督二脈"的步伐。 # 值傳送與引用傳遞 在上個小節中不小心又回到了選擇組件,在生產項目中這種選擇組件非常常用。還記得在班級管理中初始接觸[選擇組件](http://www.hmoore.net/yunzhiclub/springboot_angular_guide/1364543)時遇到[教師未自動選中的問題](http://www.hmoore.net/yunzhiclub/springboot_angular_guide/1372247)時只是草草的介紹了可以使用`compareFn`方法來比較兩個對象是否相等,但卻未說明原由: 根本上來講,由于js中對變量的`判等`會根據變量類型而采取不同的方法,所以在進行兩個`教師`是否相等的判斷上,js會認為兩個值一樣的`教師`僅僅是一對雙胞胎而已。如果問他兩個教師是否相等,它會毫不猶豫的告訴我們:不! js為什么在對象的判等上會這么處理呢,這還得從JS的變量傳遞方式說起: 基本上所有的語言都歸避不了值傳遞與引用傳遞的問題,也都存在值傳遞與引用傳遞。這包括了筆者當前接觸的所有的語言:C、C++、JAVA、JAVASCRIPT、PHP等。同時引用傳遞又被稱為:地址傳遞或指針傳遞。 > 注意:引用傳遞、地址傳遞及指針傳遞只是說法不同,教程中不會做刻意區分,用哪個詞語來描繪也將比較隨性。 **傳遞**發生了方法的調用上。比如有以下方法: ```javascript var a = function(name) { alert('the name is ' + name); }; ``` 然后調用上述方法: ``` a('mengyunzhi'); ``` ![](https://img.kancloud.cn/5d/3e/5d3ef00468f177ca8e5e63c96385403e_895x410.png) > TIP: 可以直接在chrome中的控制臺來測試JS代碼. 上述代碼在進行調用`a`方法的過程中,將`mengyunzhi`這個字符串傳遞給了`a`方法。而這個將`mengyunzhi`字符串傳遞給`a`方法,就叫做傳遞。 ## 值傳遞簡介 剛剛上面的例子就是最典型的值傳遞,也是在計算機語言的學習中接觸的最早的一種傳遞方法。也比較容易理解。就是簡單的在調用的過程中,把方法所需要的值傳給方法。把值傳給方法后,該方法對該值進行變更,不會對原來的值有任何的影響: 比如: ```javascript var a = function(name) { name = 'hello'; // ? alert('the name is ' + name); }; var name = 'mengyunzhi'; a(name); alert(name); // ? ``` * ? a方法接收了name值后,將其變更為`hello`。 * ? 原來的name還是`mengyunzhi`,并沒有授`a`方法改變其接收的`name`的值的影響。 ![](https://img.kancloud.cn/8d/e9/8de9c3ea8832364f7374cd88d5499a3f_939x446.gif) ## 引用傳送簡介 同樣是傳值。只要上面的基礎上稍加改動,那就不一樣了: ```javascript var a = function(value) { value.name = 'hello'; // ? alert('a: ' + value.name); } var value = {name: 'mengyunzhi'}; a(value); // ★ alert(value.name); // ? ``` ![](https://img.kancloud.cn/56/12/5612c8f83584ca44ebdefdba324f77df_939x446.gif) 如上所示: * ? a方法接收了`value`值后,將其`name`變更為`hello`。 * ? 原來的`value`上的`name`已經由`mengyunzhi`被成功的變更為了`hello`。也就是說:方法`a`中對自己變量`value`的改變直接影響到了原變量的值。 之所以會這樣的原因是:在★進行`a`的調用時,將`value`的**地址(引用、指針)傳遞給了`a`,而非`value`的值**。這種在方法調用時值的傳遞方式就是引用傳遞。 ## 二者區別 值傳遞與地址傳遞像極了上機實驗中遇到難題時的兩種解決方式。在上機實驗中遇到自己解決不了的問題是常有的事,而向大牛求助應該是最簡單最有效的一種方式。在求助的過程中按性別的不同,大概可以分為兩種求救模式。 ### 男生向大牛求助 ``` 某男生:大牛哥,這是我的代碼,能幫忙看看錯哪里了嗎? 大牛: 各種修改,各種DEBUG。 5分鐘后 大牛:請修改第35行,第75行.... ``` 這就是最典型的值傳遞,我們把自己的代碼傳給了大牛。大牛接收代碼后進代碼進行的修改并不會影響我們計算機上的源碼。這本就是擁有相同內容的**兩份**代碼。也就是:值傳遞就是最普通的`復制`+`粘貼`,經過此操作后1個變量變成了2個,對任何一個變量的修改都不會影響另外一個。 上述過程中。 ### 女生向大牛求助 情景一: ``` 某女生:大牛哥能幫幫我嗎? 大牛:請求控制對方的電腦 某女生:接受控制 大牛:修改對方的代碼并最終完成上機實驗。 ``` 情景二: ``` 某女生:大牛哥能幫幫我嗎? 大牛:你是第幾排第幾號 某女生:3排5號 ? 大牛:稍等馬上就來 ? ... ``` * ? 女生向大牛傳遞了一個地址 * ? 大牛將按地址找到該女生所在的電腦 而這便是現實生活的引用傳遞(地址傳遞、指針傳遞)。請求大牛幫忙時,直接把自己的計算機的控制權交給了大牛。此時大牛對代碼的修改就實實在在的發生在求助女生的計算機上。也就是說:無論誰在修改,修改的都是一份代碼。 ### 總結 值傳遞:在調用時,我的變量還是我的變量,傳遞給你的僅是個`贗品` 地址傳遞:在調用時,我的變量就是你的變量,傳遞給你的就是我擁有的那個`真品` ## 判斷 終于來到最關鍵的點上:什么時候進行是值傳遞、什么時候進行的是引用傳遞呢? 筆者認為這里面的規律可以簡單的歸納為:該語言認為容易復制的變量按值傳遞、不容易復制的變量按引用傳遞。 javascript:該語言認為基本類型是容易被復制的,所以所有的基本類型都是按值進行傳遞的,javascript的基本類型為:`字符串(String)、數字(Number)、布爾(Boolean)、對空(Null)、未定義(Undefined)、Symbol`,除此以外`對象(Object)、數組(Array)、函數(Function)`不屬于基本類型,則選擇按引用傳遞。 java: 該語言的想法和js差不多,也是認為基本類型是容易被復制的,所以按值進行傳遞,而其它的按引用進行傳遞。java的基本類型為:`byte(字節型)、short(短整型)、int(整型)、long(長整型)、float(單精度浮點型)、double(雙精度浮點型)、boolean(布爾型)、char(字符型)`。除此以外其它按地址傳遞。 php:該語言大體和java及js一致,認為基本的類型都是容易被復制的,所以按值傳遞。但有一點稍微不同的是:該語言認為數組也是比較容易復制的,所以數組在傳值時也是按值傳遞的方法,而其它的非基本類型則是按地址傳遞。 c: 該語言相對于上述幾門語言屬于低級語言(無貶低,只是由于它更貼近于直接操作硬件,所以就是這種叫法)。C語言中凡是要進行地址傳遞的,都會明確的在編寫代碼時指出,比較好區分。不同于其它語言的是:即使是主類型C語言也可以選擇按地址傳遞;而對于非主類型,則必然按地址進行傳遞(你無法在C語言中直接傳遞數組)。 # 延伸閱讀 既然已經講到這了,那么借機帶領大家再深入的理解一下。否則當有一天你在較權威的書籍上看到"js沒有引用傳遞,全部是按值傳遞"的論調時,一定會反過來懷疑教程的權威性。 繼續閱讀以前推薦先復習一下[4.6.2](http://www.hmoore.net/yunzhiclub/springboot_angular_guide/1396784)中**圖的認讀**部分。 > 本教程中的理論不見得是正確的,但一定是較于當前的現實情況適用的。因為我們一直認為:"適用的就是最好的!",在學習的過程中一定要把握好學習的邊界。 ## JAVASCRIPT與C 在值傳遞的過程中。幾種語言的理論大體是相同的,但細節稍有不同。javascript傳值過程大概如下: ```javascript var a = function(value1) { value1.name = 'hello'; alert('a: ' + value1.name); } var value = {name: 'meng'}; a(value); alert(value.name); ``` 則相應的執行過程如下: ![](https://img.kancloud.cn/e1/ab/e1abadaa5908a437b52cc23e02bc28e0_852x373.png) * ? 變量定義:開辟一塊沒有占用的內存給對象value(非主類型,存引用地址); * ? 再開辟一塊沒有占用的內存對象value中的屬性name(非主類型,存引用地址); * ? 最后給name中的值分配連續的內存空間用于存字符串meng。 * ? 調用方法a,傳入value。 * ? 由于value為對象,所以實際上獲取的value對象的地址引用值1232H。 * ? 將value的地址引用值1232H傳給方法a。 * ? 使用接收到的引用值1232H作為對象value1的引用。 * ? 開辟一塊沒有占用的內存給對象value1,該內存存入value1的引用值1232H。 所以執行`value1.name = 'hello'`內存中發生的變化筆者猜測試如下(未經考證): ![](https://img.kancloud.cn/1d/ba/1dbabbed6c75057a597dcd0eadb8a416_843x399.png) * ? 要存新的字符串,并要重新劃分一塊內存空間。(C語言是在原2340H位置做覆蓋處理還是如上圖一樣重新開辟一塊內存給`hello`,需要看具體的編碼設計。JS是否重新開辟一塊新的內容未驗證。JAVA是重新開辟一塊新內存)。 * ? 將原name的指向地址變更為新字符串`hello`所在的地址。 大概看清內存中的實際變化后,再重點看下?????。此5項共同決定了在進行引用傳值時,接收方則會把引用的值做為數字變量來處理,復制一個而非直接利用。 ## JAVA java則不然,同樣的代碼,JAVA則是如下分配內存空間的: ![](https://img.kancloud.cn/04/68/0468e54906e4dda53014741a38e08d9f_851x400.png) 在????步的傳值過程中,java向方法中傳入的并不是該變量所在內存單元的**值**,而是該變量所在內存單元的**地址**。這也是為什么有人會說JAVA是**地址**傳遞而js及c是均是**值**傳遞的原因。 ## 總結 筆者堅持認為:所有的語言都是有值傳遞及地址傳地遞的。傳過去容易復制的,就會用值傳遞;傳過去不容易復制的,就會用地址傳遞。而是否容易復制取決于偉大的語言創建者們的理解,比如javascript認為字符串是容易復制的,所以采用值傳遞;而java卻認為字符串是不容易復制的,所以就采用了地址傳遞。至于有部分權威的書籍堅持說js沒有址址傳遞,對于初學者而言只會增加我們理解該語言變量傳遞的難度,并無益處。 如果已經充分理解了上面幾張主要的內存圖,相信一定可以很好的理解以下代碼的運行結果: ``` var a = function(value1) { ? value1 = {name: 'hello'}; ? console.log('a: ' + value1.name); ? } var a1 = function(value1) { ? value1.name = 'hello'; ? console.log('a: ' + value1.name); } var value = {name: 'meng'}; ? a(value); ? console.log(value.name); ? a1(value); ? console.log(value.name); ? ``` * ? value的實際值為1232H * ? 將1232H傳入a,此時value1的值為1232H * ? 創建了新的對象`{name: hello}`,假設該對象的存儲地址為:1240H,則此時value1的值為1240H * ? 獲取1240H對應的對象中的name的值 * ? 獲取1232H對應的對象的name的值(value的值仍然為1232H,并未發生變化)。 * ? 將1232H傳入a1,此時value1的值為1232H * ? 設置1232H對應的對象中的name的值為hello(實際上為hello字符串對應的內存起始地址) * ? 獲取1232H對應的對象的name的值(value的值還然為1232H,并未發生變化)。 最終結果如下: ![](https://img.kancloud.cn/6a/bd/6abdc2fbc62f886330ae86a5ca867fa0_401x336.png) # compareFn 再回到選擇組件。正是由于js在對象的判等上采用的是`比較變量所在內存地址的值`的方法,該方法并不能很好的滿足當前現實需求。所以angular為`select`提供了`compareFn`屬性,在決定是否默認選中某個選項時,`select`將使用`compareFn`提供的方法來決定。如果該方法的值返回`true`,則選中;相反返回false則表示不選中。如果未給`select`傳入`compareFn`方法,則`select`將采用默認的js的判斷方法來決定該選中哪個選項。 再來回顧一下該方法: src/app/core/select/select.component.ts ``` /** * 比較函數,標識用哪個字段來比較兩個對象是否為同一個對象 * @param t1 源 * @param t2 目標 */ compareFn(t1: { id: number }, t2: { id: number }) { return t1 && t2 ? t1.id === t2.id : t1 === t2; // ? } ``` * ? 如果由外界傳入的klass、以及當前選項(指select里列表中的值)均存在,且兩者的id值相等時,則選中該選項。 舉個例子: 傳入`klass = {id: 1}`,klass列表:`[{id: 1}}, {id: 2}]`。則在依次執行:`compareFn({id: 1}, {id: 1})`,返回true,該項選中;`compareFn({id: 1}, {id: 2})`,返回false,該項不選中。 就到這吧。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看