目錄
引言
JavaScript的函數(shù)參數(shù)到底傳的是個(gè)啥?
有三種看法:
傳值
傳引用
基礎(chǔ)類型傳值 對(duì)象類型傳引用
傳值?
首先我們來(lái)看看是不是傳值
// 例子1
function change_list(orig_list) {
new_list = orig_list;
new_list.push('new');
return new_list;
}
orig_list = ['old'];
new_list = change_list(orig_list);
console.log('orig list: ' + orig_list);
console.log('new list: ' + new_list);
打印如下:
orig list: old,new
new list: old,new
由于orig_list都和new_list都發(fā)生了變化 因此
JavaScript不是傳值 更像是傳引用
傳引用?
那到底是不是傳引用呢? 我們來(lái)看看下面的這個(gè)例子
// 例子2
function inc(n) {
n = n + 1;
console.log('[in] n = ' + n);
}
n = 1;
inc(n);
console.log('[out] n = ' + n);
打印如下:
[in] n = 2
[out] n = 1
由于inc函數(shù)里和函數(shù)外的n并不一致 因此
JavaScript不全是傳引用 有時(shí)也傳值
基礎(chǔ)類型傳值 對(duì)象類型傳引用?
看到這里機(jī)智的你 已經(jīng)發(fā)現(xiàn)了"正確"的答案: 基礎(chǔ)類型傳值 對(duì)象類型傳引用
關(guān)于JavaScript類型的更多介紹請(qǐng)參考JavaScript學(xué)習(xí) 之 類型
按照上面的兩個(gè)例子 這個(gè)答案看起來(lái)確實(shí)是對(duì)的 那么是不是真的是這樣呢? 來(lái)看下面的例子
// 例子3
function change_me(orig_list) {
new_list = orig_list;
if (new_list.length < 3) {
new_list = [1000];
} else {
new_list = new_list.push(1000);
}
}
var orig_list = [1];
change_me(orig_list);
console.log(orig_list);
orig_list = [1, 2, 3];
change_me(orig_list);
console.log(orig_list);
按照上述答案 此時(shí)orig_list是對(duì)象類型所以傳引用 那么在調(diào)用change_me之后 期望的打印結(jié)果如下
// 期望的打印結(jié)果
[ 1000 ]
[ 1, 2, 3, 100]
那么實(shí)際的打印結(jié)果是否如期望的那樣呢 使用babel-node執(zhí)行該文件后 實(shí)際的打印結(jié)果如下
// 實(shí)際的打印結(jié)果
[ 1 ]
[ 1, 2, 3, 100]
關(guān)于babel-node的更多介紹請(qǐng)參考JavaScript學(xué)習(xí) 之 版本
同時(shí)是對(duì)象類型 為什么會(huì)這樣呢?
第一次像是傳值
第二次像是傳引用
看來(lái)這種解釋也是不對(duì)的 那JavaScript的參數(shù)到底是傳得啥呢? 我已經(jīng)暈了
傳共享!
正確的表述應(yīng)該是:
傳共享(call-by-sharing)
當(dāng)然 也可以說(shuō)是傳對(duì)象(call-by-object)或傳對(duì)象的共享(call-by-object-sharing)
關(guān)于call-by-sharing的更多解釋請(qǐng)參考這里
但是 什么叫做傳共享 這個(gè)概念完全沒(méi)聽(tīng)過(guò)啊!
首先 來(lái)看看例子1
傳入的orig_list 在push操作之后 函數(shù)外的orig_list也被修改了
接著 再看看例子2
傳入的n 在"n = n + 1"操作之后 函數(shù)里n的值為2 而函數(shù)外n的值仍然為1
最后 來(lái)看看例子3
傳入的orig_list 在賦值新的對(duì)象時(shí) 函數(shù)外的orig_list并沒(méi)有修改 而push操作時(shí) 函數(shù)外的origi_list會(huì)被修改
因此 我們可以這樣理解傳共享
對(duì)對(duì)象進(jìn)行修改時(shí) 調(diào)用者和被調(diào)用者之間共享這個(gè)對(duì)象 表現(xiàn)出來(lái)就像傳引用
對(duì)不可變的基本類型進(jìn)行修改或者給對(duì)象賦值新的對(duì)象時(shí) 調(diào)用者和被調(diào)用者引用的已經(jīng)不是同一個(gè)對(duì)象 表現(xiàn)出來(lái)就像傳值
如果你覺(jué)得拗口, 那我只能說(shuō): 回頭再看一遍例子吧!
小結(jié)
JavScript這種區(qū)分不可變 / 重新賦值和可變對(duì)象的做法 其實(shí)也是很多編程語(yǔ)言采取的一種通用做法
例如Python也有類似JavaScript的參數(shù)傳遞方式 詳見(jiàn)Python函數(shù)參數(shù)是傳值還是傳引用?
這樣設(shè)計(jì)的目的 我認(rèn)為是為了提高效率 包括: 對(duì)象分配和內(nèi)存使用的效率
當(dāng)只是對(duì)可變對(duì)象進(jìn)行修改 那么就不必分配新的對(duì)象 共享同一個(gè)共享 表現(xiàn)出來(lái)就像傳引用
當(dāng)需要修改不可變對(duì)象或者賦值新的對(duì)象 那么不得不分配新的對(duì)象 不再共享同一個(gè)對(duì)象
用一句話描述就是:
引用優(yōu)先 按需分配
不知道機(jī)智的你 是如何理解JavaScript的參數(shù)傳遞的呢? 希望讀者也分享你的觀點(diǎn)和依據(jù) 我們一起討論和完善對(duì)JavaScript參數(shù)傳遞的認(rèn)識(shí)
參考
更多文章, 請(qǐng)支持我的個(gè)人博客