js- 引用和複製(傳值和傳址)


好像通常不多人講到js中的引用和複製,不過弄清楚這個概念能夠幫助理解不少東西node

先講一下很基礎的東西,看看js中幾種數據類型分別傳的什麼
引用:對象、數組、函數
複製:數字、布爾
字符串單獨說明,由於它的特殊性,沒法肯定是傳遞引用仍是複製數值(由於字符串的值是無法改變的,因此糾結這個問題也是沒意義的)可是用於比較的時候顯然是屬於傳值比較(稍後具體說比較的事)面試

下面講一下在使用中的具體體現數組

最普通的使用就是賦值了
var a = 1;
var b = a;   //賦的是a的複製值
b ++;
alert(a);   //"1"   b的修改不影響a
/****************************************/
var a = [1];
var b = a;     //賦的是a的引用
b[0] ++;
alert(a);  //"2"   b的修改對a也有效    不過固然b = [2];這種修改對a是沒用的。。。。。。閉包

函數的參數
傳值的傳遞:傳給函數的是數值的一個複製,函數中對其的修改外部不可見
var a = 1;
var b = 2;
function change(a,b) {
  var c = a;
  a = b;      //用新引用覆蓋
  b = c;
  alert(a);   //"2"        
  alert(b);   //"1"
}
change(a,b);
alert(a);   //"1"        
alert(b);   //"2"
傳址的傳遞:傳給函數的是數值的一個引用,函數中對其屬性的修改外部可見,但用新引用覆蓋其則在外部不可見,好比
var a = [1, 2, 3];
var b = [5, 6];
function change(a,b) {
  a[0] = 4;    //對其屬性的修改外部可見 
  var c = a;
  a = b;      //用新引用覆蓋
  b = c;
  alert(a);   //"5,6"        
  alert(b);   //"4,2,3"
}
change(a,b);
alert(a);    //"4,2,3"
alert(b);     //"5,6"
從結果能夠看出a和b並無互換   由於用新引用覆蓋在外部不可見 這個很天然 由於函數只是拿到了引用 並無權力更改引用函數

下面這個就不一樣了
var a = [1, 2, 3];
var b = [5, 6];
function change() {
  var c = a;
  a[0] = 4;
  a = b;
  b = c;
};
change();
alert(a);   //"5,6"
alert(b);   //"4,2,3"
這裏成功實現互換 
又得提到js的塊級做用域了,這個放某些語言裏定然是要報未定義錯誤的,由於js沒有塊級做用域,因此它在change裏找不到變量a,b就會自覺的到上層去找,因此這裏的a,b是全局變量的引用
而上面的那個a,b則是change函數中的變量,在調用函數時傳遞了a,b的引用賦給了這兩個變量,可是並不能改變全局中的a,b,這裏換個名字大概就好理解多了
這個稍微提下    有些走題了。。。。
回到引用和複製 在比較運算中的注意事項
傳值的比較比較的是數值 而傳址的比較比較的是引用,引用不一樣即便數值相同也不等
1 == 1;    //true
1 === 1;   //true
[0] == [0]; //false
[0][0] == [0][0];    //true
[0][0] === [0][0];   //true
[0].toString() == [0].toString();   //true   
 
閉包中。。。。
閉包大概是js中最糾結的東西 咱們部門面試的經典試題,常考不衰啊。。。。
這裏我先不說閉包的東西,只說一下涉及傳值和引用的部分,等我哪天肯定本身能夠用清晰的條例、簡明的語言、生動的實例完全的說清楚那個東西,再詳細介紹這個提js不能不提的傢伙。。。
閉包中,內部函數用外部函數的局部變量使用引用的方式而不是複製
其實這也是理解閉包的一個很重要的部分 能夠用這個解釋一個很經典的閉包現象,不少地方在說明閉包的時候都會用到的一個例子
/*構造一個函數,給數組中的節點設置事件處理程序,當點擊一個節點時,alert出節點的序號*/
var add_handlers = function (nodes) {
    var i;
    for (i = 0, l = nodes.length; i < l; i ++) {
        nodes[i].onclick = function (e) {
           alert(i);    // 固然這裏的結果必然是每次alert的都是節點總數。。。。
        }
    }
};
爲何每次alert的都是節點總數 而不是預期的序號呢,這個若是用複製和引用解釋就至關容易了
由於內部函數在使用外部變量時使用引用的方式而不是複製,就是說我給每一個節點設置onclick事件的時候將i的引用傳遞給了alert,當我點擊節點觸發onclick事件的時候,i的值已經變成了節點總數。。。
var add_handlers = function (nodes) {
    var i;
    for (i = 0, l = nodes.length; i < l; i ++) {
 
        nodes[i].onclick = function (i) {
           return function(){
           alert(i);   
           }
 }(i);
    }
};
這樣修改後之因此正確是由於此時傳進去的是i的值的複製,其實和普通函數是同樣的,不要由於加了閉包就迷糊了,迴歸本源去思考就明白了,本源就是上面講到的傳址的傳遞
順便提一句,不要被閉包這個古怪的名字唬住了,其實它和咱們平時用的函數原理是同樣的,拋開那些「更長的生命週期」「保護私有變量」等閉包所謂的特性,把它當成一個普通的函數(也能夠換個角度把全局函數當作一個特殊的閉包),很容易就能夠理解了
關鍵是要放下全部的浮華,迴歸最本質。。。。。。又跑題了。。。。。。對象

相關文章
相關標籤/搜索