為什麼ECMAScript不原生支持重載?

ECMAScript 6弄了那麼多語法糖,為什麼卻不把重載給引入進去,作為一個現代函數式編程語言,函數重載仍然要使用argument來模擬實現很不優雅,沒人提案嗎?


謝邀

語言特性存在取捨的問題,支持重載的話有得有失。因為 JavaScript 是動態類型的語言,它的變數類型在運行時才決定,如果支持重載的話,只能支持根據參數個數的重載,但是如果支持了根據參數個數的重載,那麼又和參數默認值的特性存在衝突。

比如說:

class Foo{
bar(x, y=1){
return x+y;
}
bar(x){
return 2*x;
}
bar(...more){
//do sth.
}
bar(x, ...more){
//do sth.
}
}
let f = new Foo();
f.bar(1); //如果支持重載的話,調用哪一個函數?

當然你可以說我們定義一套優先順序規則,比如計算匹配度,取匹配度最高的函數。但是那樣的話會使得規則無法用簡單的方法描述,從而使用起來比較複雜,增加了代碼使用的難度和維護成本。

因為缺少靜態類型系統,JavaScript 調試中調用的時候參數如果是變數,很難做到完美的靜態檢查,這樣的話,再引入重載特性,會對程序的可維護性造成很嚴重的問題。

以上是我個人的理解。支持重載帶來的問題不只是這一點,但我覺得這一點是最嚴重的,所以列出這個。有不同看法,歡迎討論。

不過話說回來,我倒是挺期待 ECMAScript 能支持操作符重載,回頭可以考慮提一個方案:

class Complex{
constructor(re, im = 0){
this.re = re;
this.im = im;
}
Symbol.operator.add(operand){
if(typeof operand === "number") operand = new Complex(operand);
return new Complex(this.re + operand.re, this.im + operand.im);
}
}
...


蟹妖,ES6有參數默認值以後,重載需求滿足了一大半,另一小半不好搞,因為不是強類型,參數個數一樣的時候不能按類型區分。


這位題主,你所謂的原生實現,我理解應該就是說js編譯器來實現的重載。

先上結論,非不為也,實為不能也。

js沒有靜態類型,只有動態類型,因此編譯器是不知道參數的類型信息的,因為每一個參數的類型只有運行的時候才能被確定,編譯器又不會運行,自然就不清楚到底是什麼類型。

這就導致一個問題,我們的編譯器沒法按照參數類型進行重載,只能按照個數,然而這個就沒啥用了。。。還有參數個數相同類型不同的,你還是需要在運行時去區分然後找到對應的實現。

因此ecma的函數重載是在運行時實現的,你需要自己判斷類型,這是因為類型是在運行時確定的,不是在編譯時確定的。

ecma 函數參數本質上就是個數組。

可以參照v8 實現:

3086 V8_DEPRECATE_SOON("Use maybe version",
3087 Local& Call(Handle& recv, int argc,
3088 Handle& argv[]));
3089 V8_WARN_UNUSED_RESULT MaybeLocal& Call(Local& context,
3090 Handle& recv, int argc,
3091 Handle& argv[]);

https://v8docs.nodesource.com/io.js-3.0/d5/d54/classv8_1_1_function.html#a468a89f737af0612db10132799c827c0

可見arg就是一個數組。

所以你以為type script是解決什麼問題的?

就是在加上這個靜態類型系統,然後你就有重載了。


因為它就是不支持,想很多沒這功能的語言一樣,沒什麼好討論的,這就是一個語言設計的選擇。

但沒有重載之名,但有重載之實,不是么?


在JS里可以很容易的實現重載啊,我只提示這麼多,剩下的你自己想吧

let f = Function.reload([
[Number, String, function(n, s) {}, String],
[Object, Array, function(o, a) {}, Array],
])


有時候真不覺得重載是個好實現。 尤其是在弱類型語言中 簡直災難


ECMAScript 不支持重載,這個問題應該分為兩個方面說明:

- 不滿足設計思想
- 不可實現

## 不滿足設計思想

ECMAScript 本來就是弱類型語言,函數重載概念多用於強類型語言的,通過區分參數類型來實現函數重載。
這點對於 ECMAScript 來說,如果要實現重載就需要有一套類型系統。這是不符合 ECMAScript 的設計初衷的。

## 不可實現

函數重載多實現在強類型靜態語言(需要編譯的語言,如 c++),其實現是編譯成不同的函數名的函數。

void test(int a)

編譯成彙編的函數名為

_test_int

void test(int a,int b)

編譯成彙編的函數名為

_test_int_int

在你使用 test(1) 時編譯器自動調用替換為 _test_int

在你使用 test(1,2) 時編譯器自動調用替換為 _test_int_int

而在 ECMAScript 的設計中,無論你採用的是

function test(a){}
function test(a,b){}

還是這樣的:

test = function (a){}
test = function (a,b){}

函數 test 都變成最後一次賦值/實現。所以就 ECMAScript 這個語言角度來說,沒法實現重載。

究其本質來說,目前絕大數解釋性語言無法做到執行前分析所有代碼關係(如靜態語言那樣),自動匹配,只能實現比較笨的方法:以最後一次賦值/實現為準。

如果動態語言實現一套執行前分析所有代碼關係的機制,確實可行,如 typescript 做的就是,當然,他也算是一個新的語言。但是會犧牲掉性能,代碼簡潔性,還是背離了設計初衷,甚至於無法兼容之前的代碼。

如果非要使用函數重載,建議使用 typescript

點贊加關注,乾貨持續分享中


重載的本質不外乎是面向對象編程範式中實現面向介面編程的一種方式, JS是動態類型語言啊,它本身的DuckType 就已經決定了天生支持面向介面編程。So,這個沒多大意義,直接寫需要的介面就好了,沒必要追求這個概念。建議從動態類型語言的角度去看你這個」重載「的概念~~


沒必要啊。函數重載就是體現在參數上,js的參數隨意傳,還要啥重載


一言以蔽之。

所謂重載,即在編譯期根據對象類型或個數來決定函數(方法)調用。

我覺得題主應該懂了。


js中function是繼承object的,所以他也是object.的一種,他的多次賦值會覆蓋掉原來的值,類似於var obj={};obj={a:1}所以obj的值一定為{a:1}.function這是一樣


推薦閱讀:

這種所有圖片一直佔據100%寬度的響應式是如何做的?
有沒優雅的寫法,讓nodejs的回調+循環不那麼操蛋?
如何評判這篇文章說MVVM是一幫沒學好分層的搞出來的?
怎麼理解rxjs?
項目中如何有效率的開發js?

TAG:前端開發 | JavaScript | Nodejs | ECMAScript | ECMAScript2015 |