這幾天遇到js參數傳遞方式的問題,深切探究一番,將所得結果總結於此
常見的幾種傳遞方式
傳值調用(call by value)
在傳值調用中實際參數被求值,其值被綁定到函數中對應的變量上(通常是把值複製到新內存區域)。在函數返回後調用者作用域裏的曾傳給函數的任何東西都不會變。
傳引用調用(call by reference)
在“傳引用調用”求值中,傳遞給函數的是它的實際參數的隱式引用(即實際參數的地址)而不是實參的拷貝。通常函數能夠修改這些參數(比如賦值),而且改變對於調用者是可見的。
傳共享對象調用(call by sharing)
在傳共享對象調用中,傳遞給函數的是實參所指向引用對象的地址,而不是實參的地址,即傳遞共享對象。故在函數中修改引用對象時,實參的值也會跟着變化,而如果是重新給新參賦值後,再進行任何修改都不會影響到外面的實參了。
JS中的傳值方式
Number--傳值調用
let a = 1;
function add(val) {
val += 1;
console.log(`val=${val}`); // 2
}
add(a);
console.log(`a=${a}`); // 1
String--傳值調用
let a = 'hahaha';
function change(val) {
val = 'heiheihei';
console.log(`val=${val}`); // heiheihei
}
change(a);
console.log(`a=${a}`); // hahaha
Boolean--傳值調用
Symbol--傳值調用
let a = Symbol('prop');
function change(val) {
val = Symbol(2);
console.log(`val=${val.toString()}`); // Symbol(2)
}
change(a);
console.log(`a=${a.toString()}`); // Symbol(prop)
null--傳值調用
undefined--傳值調用
Object--傳共享對象調用
let obj = {
a: 1
}
function change(obj) {
obj.a = 2;
console.log(obj); // {a: 2}
obj = {
a: 3
}
console.log(obj); // {a: 3}
}
change(obj);
console.log(obj); // {a: 2}
從上面的代碼可以看出,在函數中對參數所指向的對象進行修改時會影響到外面的實參,但對函數參數重新賦值時,不會影響到實參,故js引用類型的傳值方式爲call by sharing
深入探究
變量存儲方式
在C#
中類型有兩種:值類型和引用類型,它們之間的卻別在於實際數據存儲的位置(如上圖)。值類型的變量和實際數據都存儲在堆棧中;而引用類型則只有變量存儲在堆棧中,變量存儲着實際數據的地址,實際數據存儲在與地址相對應的託管堆中。
C#中的值傳遞
- 正常情況下,值類型按值傳遞
- 正常情況下,應用類型按共享對象的方式傳遞(call by sharing),但string類型除外(由於string的不可變性,它是按值傳遞的)
- 通過使用ref或out關鍵字,值類型和引用類型都可以按引用傳遞
總結
綜上所述(證明題2333),值傳遞和引用傳遞的參數類型既可以是值類型又可以是引用類型,但是call by sharing的參數類型只能是引用類型。
個人理解:JS中,null賦值的變量時對象,但是,變量指向的引用地址爲空,故在函數中修改參數的值,對實參不會又任何影響,所以是按值傳遞。string類型應該跟C#按值傳遞的原因一致。
參考:
維基百科
JS中的值是按值傳遞,還是按引用傳遞呢
《Learning hard C#學習筆記》第10章