返回未定义或在无效函数input时抛出错误?

我知道这是重复的,但是在这里的安抚者根本不满足我:

JS库最佳实践:返回undefined或抛出错误的functioninput错误?

我想谈谈一些被指出的事情,还有我不清楚的地方。

首先我想介绍一个例子,我个人宁愿抛出一个错误,然后返回undefined。

function sum(a, b) 

假设消费者传递了一个string,因为他传递了一个输​​入框的直接值,最终用户input了一个非数字的东西。

如果我作为sum的作者在stringinput时返回了undefined,那么即使dev在某个时刻input了一个string,也不会发生任何事情,他也不会关心,因为那是可以预料的。 但在这种情况下,如果我抛出一个错误,开发人员会意识到,这实际上是一个必须处理的边缘情况,因为毕竟,没有人想要在他们的程序中的错误。

所以基本上,为什么不通过实际抛出错误使开发者意识到边缘案例呢?

这是对上面提到的问题的一个评论,几乎正是我所要求的,但没有人回答:

“但是因为我犯了一个错误,而不是默默地死亡,所以这样做对于那些没有花时间去阅读文档的人来说,在debugging的时候能节省几个小时吗?

还有一点就是在上面接受的赞美之中:

“捕捉exception比testing返回值要慢很多,所以如果一个错误经常发生,那么exception就会慢很多。”

我能想到的唯一的情况是这个qoute适用于I / O或者networking的东西,其中input总是以正确的格式,但是例如用户不存在那个id。

在这样的情况下,我明白为什么抛出一个错误会减慢进程。 但是,又如何构成一个只包含纯同步函数的math库呢?

检查input而不是检查输出更聪明吗? (确保input不会运行,并检查是否返回undefined)

我真的很困惑,实际上源于types检查,因为我确实来自C#世界,认为图书馆应该像预期的那样按照确切的方式使用,而不是无论如何都是仁慈和工作的。

我认为对于一个面向对象的语言来说,尽pipe这是不好的做法,而null的发明者却认为它是十亿美元的错误,但是通常返回null。

function语言已经解决了这个问题之前OO甚至存在一个Maybetypes。

在编写函数时,可以使用包含成功或失败的Maybe的变体,Scott Wlaschin称这种以铁路为导向的编程,而Promise是一种以铁路为导向的编程。

在OO中使用Maybe的问题是你没有联合types 。 在F#中你的代码看起来像这样:

 let x = // 9/0 throws so divideBy returns None the caller of divideBy can //decide what to do this this. match (divideBy 9 0) with | Some result -> //process result | None -> //handle this case 

当在F#中匹配某个东西时,如果你忘记了一个case(不处理None ),你将会得到一个编译时错误。 在OO中,你将不会遇到运行时错误或安静地失败。 C#中的改进可能伴随着编译器在尝试访问可为空的types时提示您,但只处理if not null ,并不强制您提供其他的。

所以在JavaScript中我会build议使用Promise或返回一个结果对象。 承诺是本地的现代浏览器和nodejs。 在浏览器中,当你不处理失败的promise(控制台中的错误和源中未捕获的拒绝)时,它将在控制台中大声呼喊。 在将来; 对于nodejs; 它会导致你的进程停止,因为一个未处理的exception。

 //example with promises const processNumber = compose([ //assuming divideBy returns a promise that is rejected when dividing by zero divideBy(9) ,plus(1) ,minus(2) ,toString ]) // 9/0 returns rejected promise so plus,minus and toString are never executed processNumber(0) .then( success => //do something with the success value ,fail => //do something with the failure ); //example of result type: const Success = {} ,Failure = {} ,result = (type) => (value) => (type === Failure) //Failure type should throw when trying to get a value out of it ? Object.create({type:type,error:value}, { value: { configurable: false, get: function() { throw "Cannot get value from Failure type" } } }) : ({ type:type ,value:value }) ; //convert a function (T->T) to (result T->result T) const lift = fn => arg => { //do not call funcion if argument is of type Failure if(arg.type === Failure){ return arg; } try { const r = fn(arg.value); //return a success result return result(Success)(r); } catch (e) { //return a failure result return result(Failure)(e); } }; //takes a result and returns a result const processNumber = compose( [ //assuming divideBy throws error when dividing by zero divideBy(9) ,plus(1) ,minus(2) ,toString ].map( //lift (T->T) to (result T -> result T) x => lift(x) ) ); const r = processNumber(result(Success)(0));//returns result of type Failure if(r.type === Failure){ //handle failure } else { //handle r.value } 

你可以返回null或抛出,但OO中越来越多的人开始意识到这不是处理事情的最好方法 。 投掷是一个副作用,所以使function不纯(更不纯的function越难保持你的代码)。

Null不是反映可能失败的函数的好types。 你不知道为什么它没有返回预期的types,现在不得不做出假设,为什么,在代码中做出假设使得你的代码难以维护。