JavaScript是否有未定义的行为(类似于C),还是由规范和确定性完全定义的?
请注意,我正在丢弃实施错误和规范差异。我也放弃了Math.random()
和Date.now()
等内容。
是否存在一段JavaScript代码,其行为并非完全由JavaScript规范决定,因此具有“未定义的行为”?
答案 0 :(得分:8)
我找到了一些例子,引用of ECMAScript Language Specification(强调我的):
在某些实现中,外部代码可能能够检测到差异 在各种非数字值之间,但这种行为依赖于实现;对于ECMAScript代码,所有NaN值都无法区分。
如果使用多个参数调用toFixed方法,则行为未定义(参见第15节)。
如果使用多个参数调用toExponential方法,则行为未定义(参见第15节)。
如果使用多个参数调用toPrecision方法,则行为未定义(参见第15节)。
当使用少于两个参数调用UTC函数时,行为依赖于实现。
答案 1 :(得分:8)
规范中有很多内容明确留给实现。特别是当涉及到Host Objects时,可能会有许多怪癖。与宿主对象无关的示例:
全局对象的[[Prototype]]和[[Class]]内部属性的值是依赖于实现。
15.1.2.2 parseInt (string , radix)
[如果有效数字过多] mathInt可能是依赖于实现的近似数学整数值,由Z表示为基数-R表示法。
15.3.4.2 Function.prototype.toString
返回依赖于实现的表示函数。
几乎所有日期解析/字符串算法都是依赖于实现的,其中包括toLocaleString
,toString
,parse
和Date
构造函数。
15.4.4.11 Array.prototype.sort (comparefn) - 可能是最好的例子:
如果comparefn未定义且对于此数组的元素不是一致的比较函数,则sort的行为为实现定义。
[...]如果proto不为null且存在整数j使得满足以下所有条件,那么sort的行为是实现定义:
- obj稀疏(15.4)
- 0≤j< LEN
如果obj是稀疏的并且满足以下任一条件,则sort的行为也是实现定义:
- obj的[[Extensible]]内部属性为false。
- obj的任何数组索引属性,其名称是小于len的非负整数,是[[Configurable]]属性为false的数据属性。
如果obj的任何数组索引属性(其名称是小于len的非负整数)是一个访问者属性,或者是[[Writable]]属性的数据属性,则sort的行为也是实现定义是假的。
最有利的是:
执行依赖于实现的调用序列[...]
15.5.4.9 String.prototype.localeCompare (that)
以实现定义的方式
比较两个字符串
15.5.4.11 String.prototype.replace [在替换符号中,如果数字大于组数],结果为实现定义。
我将在此处停止列出,您可以通过规范进行搜索。其他值得注意的地方可能是toLocaleString
方法,或Math
方法返回的依赖于实现的近似值。
答案 2 :(得分:6)
我找到了
Array.sort(compareFunction);
在compareFunction行为不正确的情况下(即返回相同输入的一致结果)。
如果comparefn未定义且对于此数组的元素不是一致的比较函数(见下文),则sort的行为是实现定义的。
答案 3 :(得分:1)
任何调用C风格的未定义行为以响应任何输入的程序都不适合与不可靠的输入一起使用。虽然有很多情况下ECMAScript规范没有指定精确的行为,但它并没有给予实现相同的自由来否定C编译器对未定义行为的时间和因果关系定律。
答案 4 :(得分:0)
首先,定义非正式称为JavaScript的语言的ECMA-262标准使用了术语“依赖于实现”和“定义为实现”,而从未定义这些术语的含义。实际上,所有这些行为都是不确定的。没有给出应该发生什么的要求。崩溃或行为异常的实现符合标准。如果它记录了该行为,则肯定是这样:崩溃或不稳定的行为就是实现所定义的。相比之下,ISO C标准正式定义了诸如“未指定行为”,“实现定义的行为”和“未定义的行为”之类的术语。这些词无论出现在哪里实际上都意味着什么。
其次,ECMA-262标准对实施限制不加重视。那并不意味着他们不在那里。例如,给定特定实现中的Javascript程序可以具有任何递归深度吗?有多少个函数参数?词汇范围的深度有多深?它可以分配任意数量的对象吗?当然不是吧? “极限”一词在ECMA-262 2018中甚至没有出现,只是作为函数参数名称。例如,该文档并未说明ECMAScript的兼容实现必须允许函数具有64个参数。因此,有理由认为,实现必须支持在函数上抛出任何数量的参数。如果我们使用一个具有一千万个参数的函数,并且实现崩溃,则说明它不符合要求; ECMA-262在任何地方均未声明允许此类故障。如果C编译器由于函数中的一千万个参数而崩溃,我们可以直截了当地指出这是程序中的一个问题:它超出了标准中记录的最小实现限制,因此无法严格遵守(正式术语)。这是行为未定义的情况:它涉及一个不可移植的程序,该程序对此标准没有任何要求(不需要实现来处理那么多函数参数)。