JavaScript傳遞變量:值傳遞?引用傳遞?

今天在看 seajs-2.2.1/src/util-events.js源碼,裏面有段代碼不是很理解:javascript

var events = data.events = {}

// Bind event
seajs.on = function(name, callback) {
  var list = events[name] || (events[name] = [])
  list.push(callback)
  return seajs
}

局部變量 list的值的修改,居然會影響到全局變量 events[name],因而猜測到 JavaScript裏面應該有引用傳遞一說!趕忙查了下網絡資料,惡補基礎知識,彙總以下:php

 


 

文章一:JavaScript傳遞變量:值傳遞?引用傳遞?java

當變量A賦值給變量B時,會將棧中的值複製一份到爲新變量分配的空間中。 網絡

如何理解?函數

var x = y = 1;
y = 2;
alert(x); // 輸出 1

x的值爲多少?性能

var obj = {};
var sub = {};
sub['id'] = 3;
obj['sub'] = sub;
sub['id'] = 4;
alert(obj['sub']['id']); // 輸出4

obj['sub']['id']的值又爲多少?他們真的符合你的預期嗎?lua

咱們分別運行2段代碼,發現第1段程序中x的值沒有改變,而第2段程序中的obj['sub']['id']的值卻改變了。一樣是一個賦值操做,一樣是修改另一份拷貝的值,爲何一段程序源變量沒變,一段程序源變量變化了呢?這個傳遞究竟是按值傳遞仍是按引用傳遞的呢?spa

李鬆峯翻譯的《JavaScript 高級程序設計 第二版》中給出了答案。翻譯

在開始的這2個例子中,事實上都複製了A的值給B,不一樣的是,在第一個例子中,A的值是int型的1,而在第二個例子中,A的值是一個地址指針,這個地址指針能夠訪問到一個對象,複製以後,第1個例子中的B的值變成了新的int, 他的值爲1,而第2個例子中B的值變成了新的地址指針,他的值爲這個對象的地址。設計

下面的例子能夠幫助理解

function setName(obj){
	obj.name = "test1";
	obj = {};
	obj.name = "test2";
}
var person = new Object();
setName(person);
alert(person.name); // 輸出 test1

能夠看到的是,儘管調用了setName函數修改了變量的name屬性,可是person.name的值並無改變。這是由於在函數中,obj指向的地址被改變了,所以修改這個地址的name屬性,並不會對原地址的name屬性形成影響。從另外一個方面,也印證了JavaScript的傳遞是按值傳遞。

 


 

文章二:JavaScript中 值的訪問參數傳遞 的問題

數據類型

在 JavaScript 中數據類型能夠分爲兩類:

  • 原始數據類型值 primitive type,好比 Undefined,Null,String,Number,Boolean
  • 引用類型值,也就是對象類型 Object type,好比 Object,Function,Array,Date,RegExp等。

聲明變量時不一樣的內存分配

  • 原始值:存儲在棧(stack)中的簡單數據段,也就是說,它們的值直接存儲在變量訪問的位置。這是由於這些原始類型佔據的空間是固定的,因此可將他們存儲在較小的內存區域 – 棧中。這樣存儲便於迅速查尋變量的值。
  • 引用值:存儲在堆(heap)中的對象,也就是說,存儲在變量處的值是一個指針(point),指向存儲對象的內存地址。這是由於:引用值的大小會改變,因此不能把它放在棧中,不然會下降變量查尋的速度。相反,放在變量的棧空間中的值是該對象存儲在堆中的地址。地址的大小是固定的,因此把它存儲在棧中對變量性能無任何負面影響。

不一樣的內存分配機制也帶來了不一樣的訪問機制

在JavasSript中是不容許直接訪問保存在堆內存中的對象的,因此在訪問一個對象時,首先獲得的是這個對象在堆內存中的地址,而後再按照這個地址去得到這個對象中的值,這就是傳說中的按引用訪問。而原始類型的值則是能夠直接訪問到的。

複製變量時的不一樣

  • 原始值:在將一個保存着原始值的變量複製給另外一個變量時,會將原始值的副本賦值給新變量,此後這兩個變量是徹底獨立的,他們只是擁有相同的value而已。
  •  

  • 引用值:在將一個保存着對象內存地址的變量複製給另外一個變量時,會把這個內存地址賦值給新變量,也就是說這兩個變量都指向了堆內存中的同一個對象,他們中任何一個做出的改變都會反映在另外一個身上。(這裏要理解的一點就是,複製對象時並不會在堆內存中新生成一個如出一轍的對象,只是多了一個保存指向這個對象指針的變量罷了)

     

參數傳遞的不一樣

首先咱們應該明確一點:ECMAScript中全部函數的參數都是按值來傳遞的。可是爲何涉及到原始類型與引用類型的值時仍然有區別呢,還不就是由於內存分配時的差異。 (我對比了一下,這裏和複製變量時遵循的機制徹底同樣的,你能夠簡單地理解爲傳遞參數的時候,就是把實參複製給形參的過程)

  • 原始值:只是把變量裏的值傳遞給參數,以後參數和這個變量互不影響。
  • 引用值:對象變量它裏面的值是這個對象在堆內存中的內存地址,這一點你要時刻銘記在心!所以它傳遞的值也就是這個內存地址,這也就是爲何函數內部對這個參數的修改會體如今外部的緣由了,由於它們都指向同一個對象呀。或許我這麼說了之後你對書上的例子仍是有點不太理解,那麼請看圖吧:

     

因此,若是是按引用傳遞的話,是把第二格中的內容(也就是變量自己)整個傳遞進去(就不會有第四格的存在了)。但事實是變量把它裏面的值傳遞(複製)給了參數,讓這個參數也指向原對象。所以若是在函數內部給這個參數賦值另外一個對象時,這個參數就會更改它的值爲新對象的內存地址指向新的對象,但此時原來的變量仍然指向原來的對象,這時候他們是相互獨立的;但若是這個參數是改變對象內部的屬性的話,這個改變會體如今外部,由於他們共同指向的這個對象被修改了!來看下面這個例子吧:(傳說中的call by sharing)

var obj1 = {
  value:'111'
};

var obj2 = {
  value:'222'
};

function changeStuff(obj){
  obj.value = '333';
  obj = obj2;
  return obj.value;
}


var foo = changeStuff(obj1);

console.log(foo);// '222' 參數obj指向了新的對象obj2
console.log(obj1.value);

/* '333' obj1仍然指向原來的對象,之因此value改變了,
 *是由於changeStuff裏的第一條語句,這個時候obj是指向obj1的 .
 *再囉嗦一句,若是是按引用傳遞的話,這個時候obj1.value應該是等於'222'的
*/

 

好了,以上就是關於這個問題的所有解釋了。

各位有興趣的話能夠去了解一下call by value ,call by reference call by sharing 等函數傳遞的機制call by sharing

還有stackoverflow上對於函數傳遞的這個問題解釋得至關精闢,值得一看。(下面有連接)

參考:

 

摘自:http://fehacker.com/?p=57

 


 

文章三:(知乎網討論)JavaScript傳遞參數若是是Object的話,是按值傳遞仍是按引用傳遞?

http://www.zhihu.com/question/27114726

相關文章
相關標籤/搜索