天天看點

JavaScript并非“按值傳遞” 《純CSS打造銀色MacBook Air(完整版)》《擁Bootstrap入懷——模态框(modal)篇》《兩個簡單的Loading圖示》《純CSS打造銀色MacBook Air(完整版)》

JavaScript并非“按值傳遞” 《純CSS打造銀色MacBook Air(完整版)》《擁Bootstrap入懷——模态框(modal)篇》《兩個簡單的Loading圖示》《純CSS打造銀色MacBook Air(完整版)》

首先,在此聲明,這不是标題黨。

其次,再次聲明,文中會有部分引用的文章,筆者會在文中指出,而且,文中也會有部分筆者自己的了解,閱讀後如有意見建議,歡迎在評論區指出。

置頂文章:《純CSS打造銀色MacBook Air(完整版)》

上一篇:《擁Bootstrap入懷——模态框(modal)篇》

作者首頁:myvin

部落客QQ:851399101(點選QQ和部落客發起臨時會話)

聲明

本文是筆者在查閱資料的基礎上根據自己的了解來寫的,但對于一些問題博友可能會有争議,歡迎提出異議,歡迎讨論。

關于筆者的觀點總結,請務必看文章最後一段的3點總結。

在評論區,博友燒點飯對按值傳遞用《進階程式設計》給出了補充,筆者誤會了博友的好意,特在此向博友燒點飯表示歉意。點選博友ID燒點飯可以進去其首頁檢視博友空間。詳情請檢視評論區。

同時,也非常感謝各位答主的交流,筆者确實有所受益。希望我們都共同進步。

寫在前面

一、提出問題

也許會有人質疑:

“在JavaScript中,基本類型是按值傳遞,引用類型也是按值傳遞,你卻告訴我JavaScript中并不是按值傳遞?”

的确,在JavaScript中,基本類型是按值傳遞,但是“準确”來說,引用類型并不是按值傳遞,當然也不是按引用傳遞,那到底是什麼傳遞?

先給個引子。

在《JavaScript進階程式設計(第三版)》中關于參數傳遞是這樣講的,筆者引用如下:

ECMAScript中所有函數的參數都是按值傳遞的。也就是說,把函數外部的值複制給函數内部的參數,就和把值從一個變量複制到另一個變量一樣。基本類型值的傳遞如同基本類型變量的複制一樣,而引用類型值的傳遞,則如同引用類型變量的複制一樣。有不少開發人員在這一點上可能會感到困惑,因為通路變量有按值和按引用兩種方式,而參數隻能按值傳遞。
在向參數傳遞基本類型的值時,被傳遞的值會被複制給一個局部變量。在向參數傳遞引用類型的值時,會把這個值在記憶體中的位址複制給一個局部變量,是以這個局部變量的變化會反映在函數的外部。

書中明确講到參數的傳遞是按值傳遞的,筆者卻硬要說引用類型不是按值傳遞的,請相信,筆者沒有瞎。

筆者在反複看這一塊的時候思想活動是這樣的:不懂————略懂————不懂————認為引用類型是按引用傳遞————還是不懂————頭要炸了。

可以看到,筆者在這裡貌似犯了一個一部分人會犯的了解:引用類型的傳遞雖然可以看做是引用作為值的複制和傳遞,但是引用類型的傳遞有一些按引用傳遞的影子在裡面,但是書中點出是按值傳遞。

到這裡,問題出來了:引用類型值的傳遞到底是不是按值傳遞,或者說,如果是按值傳遞,那到底應該怎麼了解?如果不是按值傳遞,那該是按嘛傳遞?(筆者并不認為它是按值傳遞)

不知道大家當時是怎麼了解這個問題的,或者現在是怎麼了解這個問題的。反正當時筆者對于書中的解釋并不是很清楚。

上網随便一搜,關于JavaScript的按值傳遞的回答解釋倒是不少,但好多都是Ctrl+C,Ctrl+V來的,有的關于同一個問題不同的文章解釋的卻不相同,然後一路查,查到了維基,查到了stackoverflow,終于有了眉目。

現在這裡給出參考相應的維基百科和stackoverflow位址,可提前預覽,關于維基百科請檢視英文,中文的内容會大幅縮減。

二、分析問題

關于引用類型值傳遞的錯誤了解

(1)、基本類型值的傳遞

首先,簡單說一下基本類型值的傳遞,對于基本類型值的傳遞了解的可以直接跳過了。

首先給出結論:基本類型值的傳遞是按值傳遞的。

先來個簡單的例子:

function func(num){
  num=10;
  return num;
}
var outer=20;
var result=func(outer);
console.log(result,outer);
           

列印結果分别是:10和20。

在這段代碼中,實參outer作為參數傳遞給函數func,是以該實參被複制給形參num,這兩個參數是完全獨立的,各回各家各找各媽。在函數中,num值被指派10,但是并不影響函數外部的outer,outer依然是堅挺的20。

這個結論很好了解,基本類型值的大小是固定不變的,是放在棧中的,我們複制一個基本類型值,或者給一個函數的形參傳遞一個基本類型值,就相當于新開辟了一塊記憶體空間來存放這個基本類型值的副本,這兩個值完全相等,互相操作互不幹擾。

(2)、引用類型值的傳遞

對于引用類型的傳遞,筆者先給出一個筆者當時的錯誤了解:引用類型值的傳遞是按引用傳遞的。請注意:這裡是一個錯誤了解,隻是為了更好分析這個錯誤在哪。

Eg1:先給出一個例子:

function setSex(obj){
  obj.sex='girl';
}
var person = new Object();
setSex(person);
console.log(person.sex);
           

列印結果是:girl。

給出這樣的錯誤了解:obj和person指向同一個對象,即兩者儲存的是相同的引用,修改obj就相當于修改person,是以當person作為引用類型參數傳遞給函數後,修改形參obj的sex屬性,那person的sex屬性自然是girl,是以你看,這不就是按引用傳遞嗎。

Eg2:再給出一個例子:

function func(o2){
  o2={name:'xiaoming'};
  return o2;
}
var o1={color:'red'};
var result = func(o1);
console.log(o1,result);
           

這也是一個引用類型值的傳遞,如果引用類型值的傳遞是按引用傳遞的話,即o1和o2都指向堆中的同一個對象,那麼傳回的o2應該會和o1相同,但是列印結果是這樣子的:

Object {color: "red"} Object {name: "xiaoming"}
           

即o1并沒有因為随着o2的改變而改變。

當然,進行到這裡并沒有解決筆者在文章開頭提出的問題。因為第二個例子僅僅是提出了一個反例而已,然并卵,并沒有真正地解釋“既然你口口聲聲大言不慚地說這不是按引用傳遞,那這到底是什麼傳遞呢”。

下面,切入正題。

三、解決問題

注:以下部分内容引用自維基百科的Evaluation strategy,感興趣的可以先點選連結檢視嘗鮮。

維基百科的這篇Evaluation strategy中介紹了多種傳遞政策,在這裡筆者主要說一下和該博文相關的三種傳遞方式,即call by value、call by reference和call by sharing,即按值傳遞、按引用傳遞和按共享傳遞。

1、按值傳遞(call by value)

關于按值傳遞比較簡單,在這裡就不再贅述,簡單引用文中的一段解釋:

Call-by-value evaluation is the most common evaluation strategy, used in languages as different as C and Scheme. In call-by-value, the argument expression is evaluated, and the resulting value is bound to the corresponding variable in the function (frequently by copying the value into a new memory region). If the function or procedure is able to assign values to its parameters, only its local copy is assigned — that is, anything passed into a function call is unchanged in the caller's scope when the function returns.

上段英文了解起來也很簡單,可自行閱讀或pass。

2、按引用傳遞(call by reference)

按引用傳遞也叫pass-by-reference,它接收的是隐式引用,不是參數的副本,形參和實參指向的是同一個對象,是以修改形參,相對應的實參也會被修改,部分原文如下:

receives an implicit reference to a variable used as argument, rather than a copy of its value

對應着看上面剛提到的Eg2,JavaScript的引用類型值的傳遞并不是按引用傳遞,形參變化并沒有導緻實參做相應的變化。

那麼接下來看最後一個相關的傳遞方式,也就是筆者比較認同的JavaScript引用類型值傳參方式:call by sharing,按共享傳參。

3、按共享傳遞(call by sharing)

注:這裡是重點!!!

先來一段引用:

call by object-sharing" is an evaluation strategy first named by Barbara Liskov et al. for the language CLU in 1974.

按共享傳遞,也叫call by object或call by object-sharing,是麻省理工的Barbara Liskov在1974年在CLU語言中提出的,關于CLU程式設計語言的其他資訊,可以點選這裡跳轉到百度百科檢視。

使用按共享傳遞,函數接收的參數是對象引用的副本,它和按引用傳參有一定差別:

按引用傳遞,改變形參,實參也會相應變化;按共享傳遞,修改形參的屬性時,實參會相應改變,但是如果給形參重新指派,即給形參重新配置設定一個引用,是不會影響外部的實參的。

在這裡舉兩個例子:

(a)第一個例子就是上面提到的Eg2:

function func(o2){
  o2={name:'xiaoming'};
  return o2;
}
var o1={color:'red'};
var result = func(o1);
console.log(o1,result);
           

列印結果:

Object {color: "red"} Object {name: "xiaoming"}
           

在這裡傳遞引用類型參數是按共享傳參,按照上面提到的,按共享傳遞,修改形參的屬性時,實參會相應改變,但是如果給形參重新指派,即給形參重新配置設定一個引用,是不會影響外部的實參的,是以在該例子中,外部的實參o1并沒有因形參o1的重新指派而變化。

(b)第二個例子和第一個例子類似,隻是重新new了一個:

function func(o2){
  o2.color='blue';
  o2=new Object();
  o2.color='white';
  return o2.color;
}
var o1={color:'red'};
var result = func(o1);
console.log(o1.color,result);
           

修改形參o2的color屬性值為blue,按照按引用傳遞的特點,外部的o1的color屬性值相應改變,接下來的

o2=new Object()

雖然和形參o2同名,但是指向的已經不是同一個對象,即它們不是同一個引用(既然是new,自然是新的了,哈哈哈哈),是以再修改o2的color屬性值外部實參也不會有任何變化,在函數調用完畢以後,這個new起來的o2就會被銷毀。

是以,在這個例子中,列印結果是blue和white。

然後,在這裡再點兩點不太相關的東西,希望有助于我們了解:

首先是這一段引用:

It (call by sharing) is used by languages such as Python, Iota, Java (for object references), Ruby, JavaScript, Scheme, OCaml, AppleScript, and many others.

文中提到,按共享傳遞被應用到了許多語言中,其中也包括JavaScript,這樣我們也在JavaScript中找到了關于按共享引用的影子,算是對上述的一個不太合适的小小的印證吧。

接下來是這一段引用:

However, the term "call by sharing" is not in common use; the terminology is inconsistent across different sources. For example, in the Java community, they say that Java is pass-by-value, whereas in the Ruby community, they say that Ruby is pass-by-reference, even though the two languages exhibit the same semantics.

文中提到,按共享引用其實并不是普遍被提到,在不同的語言中,它的别稱都不太一樣,比如Java中,稱Java按值傳遞,在Ruby中,稱Ruby按引用傳遞,其實它們表達的是相同的含義。

是以,在JavaScript中,筆者更傾向于把JavaScript中關于引用類型傳遞的所謂的“按值傳遞”當做是按共享傳遞的一個别稱,但是它有别與JavaScript中基本類型值的按值傳遞。

總結問題

在此總結三點:

1. 關于書中的對于引用類型值的按值傳遞,可以了解為傳遞的是引用的副本,而對象的引用可以看做是一個值(它就是一個值),但是筆者不傾向于就因為這一點就把它認為是按值傳遞。

2. 關于JavaScript中的引用類型值的傳遞方式筆者更傾向于是按共享傳遞。這樣的話,它将按值傳遞和按引用傳遞很好的卻别開來,因為JavaScript中的引用類型值的傳遞有一部分按引用傳遞的特點在裡面,比較容易混淆。

3. 筆者對于書中的按值傳遞的說法不否定,隻是更将傾向于将引用類型值的按值傳遞了解為按共享傳遞的一種别稱。

如果文中的内容有任何錯誤不足,懇請指出,同時也歡迎在評論區談論。

Also known as "call by object" or "***********

轉載請記得說明作者和出處哦-.-

作者:myvin

原文出處:http://www.cnblogs.com/myvin/p/4794680.html

下一篇:《兩個簡單的Loading圖示》

*****************************************

繼續閱讀