JS 的引用賦值與傳值賦值
這個問題說大不大說小不小,如果你有幸踩了這個坑,一定會找這篇文章,哈哈~
現說一下JS數字的型別:基本型別和引用型別
先看下下面兩個栗子:
1 2 3 4 5 6 7 8 9 |
var a
= 30;
var b
= a;
a
= 20;
console.log(
b ) //
30
var a
= [1,2];
var b
= a;
a[0]
= 5;
console.log(
b ) //
[5,2]
|
簡單的說:
number,string型別都是基本型別,而基本型別存放在棧區,訪問時按值訪問,賦值是按照普通方式賦值;
物件和陣列是通過引用來賦值的,所以改變a的同時b也會跟著改變。
解決辦法:
1. var a = [1,2]; var b = a.slice(0); b[0] = 3; alert(a) //1,2 2. var a = [1,2]; var b = a.concat(0); concat會直接返回新的陣列物件好了下面詳細的說,看了上面秒懂的直接忽略(直接看下面阿里的面試題);
1、基本型別
基本的資料型別有:undefined,boolean,number,string,null。 基本型別存放在棧區,訪問是按值訪問的,就是說你可以操作儲存在變數中的實際的值。
當基本型別的資料賦值時,賦得是實際的值,a和b是沒有關聯關係的,b由a複製得到,相互獨立。(字面量的才是基本型別)
var a=10;
var b=a;
console.log(a+','+b); // 10,10
a++;
console.log(a+','+b) // 11,10
2、引用型別
引用型別指的是物件。可以擁有屬性和方法,並且我們可以修改其屬性和方法。引用物件存放的方式是:在棧中存放物件變數標示名稱和該物件在堆中的存放地址,在堆中存放資料。
物件使用的是引用賦值。當我們把一個物件賦值給一個新的變數時,賦的其實是該物件的在堆中的地址,而不是堆中的資料。也就是兩個物件指向的是同一個儲存空間,無論哪個物件發生改變,其實都是改變的儲存空間的內容,因此,兩個物件是聯動的。
3、陣列是引用型別
我們先來看一個例子:
var a = [1,2,3];
var b = a;
a = [4,5,6];
alert(b); //[1,2,3]
好像陣列是基本型別一樣。。,但是:
var a = [1,2,3];
var b = a;
a.pop();
alert(b); //[1,2]
這是怎麼回事?因為:(知乎解釋)
a = [4,5,6];//改變的是a引用本身,沒有改變陣列物件,a和b沒有了關係。 a.pop();//改變的是陣列物件,a引用沒有改變。 b = a;//該操作後,b直接指向陣列物件,不是b指向a,a再指向陣列。 //所以改變a引用並不會對b引用造成影響,改變陣列物件可以。作者:Intopass
連結:https://www.zhihu.com/question/26042362/answer/31903017
來源:知乎
這個問題就跟我之前在React todo-list 一篇中提到的問題一樣:
var tasks=this.state.data;
tasks=tasks.filter(function(i){
return i.index!=taskId;
});
由於filter函式是返回一個新的陣列,雖然仍然用tasks去接收,但這時候tasks的指向已經是新陣列啦,所以tasks和data已經不在有關係。(concat也是返回新陣列)
而push和splice函式是在原陣列上操作,所謂在原陣列操作,指的是指向不變,所以tasks和data是相關聯的。
4、引數傳遞
js的函式引數傳遞為值傳遞。
當傳入的是 基本型別的引數時:就是複製了份內容給i而已,i與age之間沒有關係。
function setAge(i)
{ alert(i);//24 i = 18; alert(i);//18,i的改變不會影響外面的age }; var age = 24; setAge(age); alert(age);//24當傳入的引數為引用型別時:
function setName(obj)
{ obj.name = 'haha'; }; var obj2 = new Object(); setName(obj2); alert(obj2.name); // haha這看起來很像是傳遞的是引用,因為obj.name受到改變了,但其實不是,其實還是值,因為obj2本身的值就是新物件的地址,所以傳進去的就是這個地址。
這是阿里2014年的筆試題:
var a = 1;
var obj = { b: 2 }; var fn = function () {}; fn.c = 3; function test(x, y, z) { x = 4; y.b = 5; z.c = 6; return z; } test(a, obj, fn); alert(a + obj.b + fn.c);答案:12
首先test傳遞進去的實參中,a是基本型別(,複製了一份值),obj是object(指向地址,你動我也動),fn也當然不是基本型別啦。在執行test的時候,x被賦值為4(跟a沒關係,各玩各的,a仍然為1),y的b被賦值為5,那obj的b也變為5,z的c變為6,那fn的c當然也會是6. 所以alert的結果應該是1+5+6 =12. (其實test不返回z也一樣,z仍然改變的)。
var a = 30;var b = a;a = 20;console.log( b )
var a = [1,2];var b = a;a[0] = 5;console.log( b )