在不总是检查属性和方法的情况下,如何在JavaScript中使用鸭子输入?


11

我知道javascript使用鸭子类型,而且起初我认为与强类型语言(如C#)相比,这会使多态性变得容易。但是现在,我带有参数的函数充满了诸如此类的东西:

if(myObj.hasSomeProperty())

要么

if(myObj.hasSomeMethod())

要么

if(isNumber(myParam))

等等

这对我来说真的很丑。我来自C#背景,发现定义的接口要好得多。

我想知道我是否错误地尝试应用在静态类型的语言中有效的策略,并且在javascript中有更好的方法呢?

我知道我无法检查,但是跟踪javascript运行时错误可能是一场噩梦,因为它们并非总是在代码中实际发生错误的地方发生。


2
我认为您可能只是对动态类型语言的本质感到困惑。您必须习惯于在运行时而不是编译时发生许多错误的思维定势。如果您需要检查每个输入数字的函数中每个参数是否都是数字,则可能会很麻烦(尽管如果将安全性作为首要目标来交付库,则可能会很值得)。对于任何规模的应用程序,我都发现,如果传入错误的类型,则仅允许函数失败是很必要的。相反,更有效的工作重点可能放在构造测试上。

可能有助于进行这些检查以确保类型符合必要的接口要求的地方(例如,检查它们是否具有所需的方法)位于您最核心且使用最广泛的函数中(其中instability = 0的函数)。马丁提供的传出/传出耦合度量)。那应该是一个很小的目标。通常,有很多一次性的局部函数在范围上是隔离的-可能不需要如此全面的运行时检查集。他们没有积累太多的复杂性。

切换到类型脚本。它仍然是鸭子类型,但支持静态类型以在编译时检测许多错误。
CodesInChaos

2
您遇到了鸭式打字的最大难题:它的威力源于其缺点。如果您想使用面向对象的JavaScript,则只需忍受运行时错误,并希望您的单元测试在创建它们后尽快找到它们:-(
Ross Patterson

@RossPatterson OP的问题是动态类型,而不是鸭子类型。TypeScript和Go均为鸭子类型,但避免了OP的问题。鸭子输入的问题是一个不同的问题,即您可以拥有通过鸭子测试但不履行您期望的合同的成员。
CodesInChaos

Answers:


11

在不总是检查属性和方法的情况下,如何在JavaScript中使用鸭子输入?

简单:不必总是检查属性和方法。

在Ruby中,您所说的称为“鸡型”。使用一种动态的鸭子式语言,您只需相信调用方会为您传递一个合适的对象。履行合同的一方是呼叫者的工作。

我知道javascript使用鸭子类型,而且起初我认为与强类型语言(如C#)相比,这会使多态性变得容易。

您在这里混淆了多个正交轴输入。有四个正交的打字轴:

  • 时间:动态类型(在运行时才知道和检查类型)与静态类型(在运行时才知道和检查类型)
  • 什么:鸭子类型(类型是基于行为),结构性类型(类型是基于结构)和名义类型(类型是基于名称
  • 你看得到他们吗?显式类型(必须对类型进行显式注释)与隐式类型(推断出的类型)
  • 强键入与弱键入–您可能已经注意到,我没有给这个标题加上醒目的标题,也没有在括号中给出解释,这是因为与上述七个术语不同,每个术语都有一个通用的精确定义,这两个术语有大约十二个相互矛盾的半广泛使用的模糊定义;理想情况下,您应该完全避免使用这些术语,并且如果必须使用它们,请先精确定义它们

自从您提到C#:它主要是静态类型的,但是支持通过type进行动态类型的输入dynamic,它主要是名义上的类型的,但是匿名类型使用结构化类型,并且语法模式(例如LINQ查询理解语法)可以说是鸭蛋。类型或结构类型,它通常是显式类型,但支持通用类型参数和局部变量的隐式键入(尽管与大多数其他语言相比,局部变量的大小写很奇怪,因为您不能只是将类型排除在外,而必须给它一个明确的伪类型var,换句话说,如果您要使用隐式类型,则必须明确声明。C#是强类型还是弱类型取决于所使用的两个术语的定义,但是请注意,C#中可能存在很多运行时类型错误,尤其是由于数组的协方差不安全。

我知道我无法检查,但是跟踪javascript运行时错误可能是一场噩梦,因为它们并非总是在代码中实际发生错误的地方发生。

调试并非易学的技能。但是,有一些使调试更容易的技术,例如Saff Squeeze是Kent Beck描述的一种技术,它使用测试和重构进行调试:

命中率高,命中率低

回归测试和Saff压缩

三河研究所肯特·贝克

摘要:为了有效地隔离缺陷,请从系统级测试开始,逐步进行内联和修剪,直到您拥有最小的可以证明缺陷的测试为止。


该命中率高的命中率低的链接为我获得了http 500,其中“不再提供页面”为面向人的消息。
joshp

threeriversinstitute.org域似乎已被放弃。
Bart van Ingen Schenau 2015年

啊,该死。而且它甚至都没有存档在WayBack Machine上
约尔格W¯¯米塔格

呼叫者应该如何履行合同的一方?似乎没有办法(用代码)传达参数应该是什么。每个函数的形式为函数fname(objParam,objParam,...)。这是否意味着像javascript这样的语言完全依赖外部文档来传达用法?
军团再临》 2015年

@Legion:文档,良好的命名,常识,作为行为规范的测试,阅读源代码并命名。请注意,这实际上与较弱的类型系统(如C#或Java)没有太大区别:例如,返回值的含义IComparer<T>.Compare(T, T)仅在文档中是清楚的,而在类型中则不明显。而其中的类型java.util.Collections.binarySearch(java.util.List<T>)它说,...
约尔格W¯¯米塔格

1

我知道我无法检查,但是跟踪javascript运行时错误可能是一场噩梦,因为它们并非总是在代码中实际发生错误的地方发生。

确实,典型的做法是不检查。而且,是的,这确实意味着您会收到javascript错误,这些错误会在实际问题的其他位置报告。但实际上,我认为这不是一个大问题。

使用JavaScript时,我会不断测试自己在写什么。在大多数代码中,我有单元测试,每次保存编辑器时,它们都会自动运行。当出乎意料的出问题时,我几乎立刻就知道了。我在一个很小的代码区域中可能犯了错误,因为这几乎总是我碰到的最后一个错误。

当我确实遇到运行时错误时,我至少已经获得了堆栈跟踪,并且在浏览器内发生错误的情况下,我可以转到任何级别的堆栈跟踪并检查变量。通常很容易追溯到不良价值的来源,从而将其追溯到最初的问题。

如果您像我一样,当我主要使用静态类型的语言编写代码时,我在测试之前就编写了较大的代码块,并且我没有练习追溯其来源的值。使用javascript之类的语言进行编程是不同的,您必须使用不同的技能。我怀疑这样的编程似乎更困难,因为这些不是您在使用其他语言(例如C#)时开发的技能。

话虽如此,我认为对于显式类型还有很多话要说。它们非常适合记录文档并尽早发现错误。我认为,将来我们会看到越来越多的采用诸如Flow和Typescript之类的东西,它们会向javascript添加静态类型检查。


0

我认为您在做正确的事,您只需要找到令您满意的样式即可。这里有一些想法:

  • 相反,if(myObj.hasSomeProperty())您可以使用if( myobj.prop !== undefined )。BTW只能在非严格模式下使用,在严格模式下,您必须使用if( typeof myobj.prop !== 'undefined' )

  • 您可以卸载某些类型检查以分离验证器。这样的好处是,一旦接口成熟(例如if( is_valid( myobject ))is_valid以开头的地方),便能够跳过验证if( !DEBUG ) return true;

  • 有时将输入克隆为规范形式是有意义的,在这种情况下,您可以将各种验证目标收集到克隆函数/对象中。对于〔实施例中,my_data = Data( myobj, otherstuff )Data构造可以方便地运行在一个集中的地方所有不同的验证。

  • 您可以使用一些库(在性能方面会有所损失)将类型验证简化为更优雅的内容。即使您从长远来看不会选择这条路线,也可能会感到很舒服,可以让您顺利地适应自己的风格。一些示例包括xtype.jstype-checkvalidator.js等。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.