在无效函数输入时返回未定义或抛出错误?
Posted
技术标签:
【中文标题】在无效函数输入时返回未定义或抛出错误?【英文标题】:Return undefined or throw error upon invalid function input? 【发布时间】:2018-04-28 01:30:26 【问题描述】:我知道这是重复的,但这里的 anwser 根本不能满足我:
JS library best practice: Return undefined or throw error on bad function input?
我想谈谈一些被指出的事情以及我仍然不清楚的事情。
首先,我想展示一个我个人宁愿抛出错误然后返回未定义的示例。
function sum(a, b)
假设消费者传递了一个字符串,因为他传递了输入框的直接值,而最终用户输入的不是数字。
如果我作为sum
的作者在输入字符串时返回了 undefined,那么即使开发人员在某个时候输入了一个字符串,也不会发生任何事情,他也不会在意,因为这是意料之中的.但是在这种情况下,如果我抛出一个错误,开发人员会意识到这实际上是一个必须处理的边缘情况,因为毕竟没有人希望他们的程序出现错误。
那么基本上,为什么不通过实际抛出错误来让开发人员意识到边缘情况呢?
这是对上述问题的评论,这几乎正是我要问的问题,但还没有人回答:
“但是由于我需要多花 1 分钟来抛出错误而不是默默地死去,对于那些没有花时间阅读文档的人来说,这不会节省数小时的调试时间吗?”
上面接受的anwser中的另一点:
“捕获异常比测试返回值慢很多,所以如果错误是常见的,那么异常会慢很多。”
我能想到的唯一情况是这个 qoute 适用于 I/O 或网络内容,其中输入始终采用正确的格式,但例如具有该 ID 的用户不存在。
在这种情况下,我明白为什么抛出错误会减慢进程。但同样,只包含纯同步函数的数学库呢?
检查输入而不是检查输出不是更聪明吗? (确保输入能够正常工作,而不是运行函数并检查是否返回了 undefined)
我的很多困惑实际上源于类型检查,因为我确实来自 C# 世界,并认为应该按照预期的确切方式使用库,而不是仁慈地工作。
【问题讨论】:
【参考方案1】:我认为对于 OO 语言来说,返回 null 是很常见的,即使这是不好的做法,并且 null 的发明者将其称为 billion dollar mistake。
函数式语言在 OO 出现之前就通过 Maybe 类型解决了这个问题。
在编写函数时,您可以使用包含 Success 或 Failure 的 Maybe 变体,Scott Wlaschin 称之为 railway orientated programming,Promises 是一种面向铁路的编程。
在 OO 中使用 Maybe 的问题是您没有 union 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# 中匹配某些内容时,如果您忘记大小写(不处理 None
),您将收到 编译时间 错误。在 OO 中,您不会并且会遇到运行时错误或安静地失败。当您尝试访问可空类型时,C# 的改进可能会伴随编译器警告您,但它只处理 if not null
,它不会强制您提供 else。
所以在 javascript 中,我建议使用 Promises 或返回结果对象。 Promise 是现代浏览器和 nodejs 原生的。在浏览器中,当您不处理失败的承诺(控制台中的错误和源中未捕获的拒绝时中断)时,它会在控制台中对您大喊大叫。将来;对于nodejs;它将导致您的进程停止,就像未处理的异常一样。
//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 或 throw,但 OO 中越来越多的人开始意识到这是 not the best way 来处理事情。抛出是一种副作用,因此会使函数不纯(不纯的函数越多,维护代码就越困难)。
Null 不是反映可能失败的函数的好类型。您不知道为什么它未能返回预期的类型,现在必须对原因做出假设,在代码中做出假设会使您的代码更难维护。
【讨论】:
在诸如数学函数之类的承诺会严重过度杀伤的时候呢?此外,C# 中的可能看起来很棒。我只是希望他们能赶上 @ThatBrianDude 在 Java 和 C# 中,返回 null 可用于表示出现问题,抛出异常表示出现问题。我认为最好为 JavaScript 中的同步函数返回Result
类型,它就像 Maybe
但不是 Some
和 None
它有 Success
和 Failure
其中 Success
是实际值Failure
是错误。在此处处理 js 示例:github.com/amsterdamharu/lib/blob/master/src/index.js#L36-L125 但必须明天或更晚才能完成。以上是关于在无效函数输入时返回未定义或抛出错误?的主要内容,如果未能解决你的问题,请参考以下文章
为啥在尝试读取记录集字段时未定义访问 vba 抛出子或函数?