我的一位朋友在一些Javascript代码中发现了一些有趣的行为,我决定进一步调查。
比较
(function (x) {return x*x;}) > [1,2,3]
在大多数主要浏览器(Firefox,Chrome,Opera和Safari)中返回true
,在IE9中返回false
。对我来说,除了undefined
之外,没有其他比较的逻辑结果,因为没有办法说函数大于数组。
在ECMA脚本标准中读到这一点,它说>
在对象上使用时的实际参数是在参数上调用ToNumber内部操作的结果。一些实验和进一步阅读告诉我,这与应用(Number) arg
等类型转换不同。阅读规范,我很难弄清楚这里发生了什么。
任何人都可以告诉我这里真正发生的事情吗?
答案 0 :(得分:60)
在IE< 9中,.toString
(function (x) {return x*x;})
提供了
"(function (x) {return x*x;})"
在chrome中,它给出了:
"function (x) {return x*x;}"
如果你比较:
"function (x) {return x*x;}" > "1,2,3" // true
"(function (x) {return x*x;})" > "1,2,3" // false
这与比较实际相同:
"f" > "1"
"(" > "1"
与比较相同:
102 > 49
40 > 49
这就是我们从函数和数组比较到简单数字比较的方式:)
答案 1 :(得分:54)
>
的操作数不一定转换为数字。 abstract relational comparison algorithm使用提示 ToPrimitive
调用Number
,但ToPrimitive
仍可能返回一个字符串(在函数和数组的情况下,它确实)。
所以你最终比较了两个字符串。调用toString
on function objects的结果不是由规范定义的,尽管大多数主要引擎返回函数的源代码(或其某种形式,并且格式不同)。调用toString
on arrays的结果与join
相同。
所以你很可能最终会这样做:
"function (x) {return x*x;}" > "1,2,3"
由于该功能的字符串的确切形式可能因浏览器而异(并注意Esailija's investigations - 看起来IE9保持外部()
,Chrome不会),它不是太令人惊讶,结果可能会有所不同。
答案 2 :(得分:5)
让我们深入了解ECMA规范。我已经包含了部分编号,因此您可以参考。
11.8.2大于运算符(>)
生产RelationalExpression:RelationalExpression> ShiftExpression的计算方法如下:
- 让lref成为评估RelationalExpression的结果。
- 让lval成为GetValue(lref)。
- 让rref成为评估ShiftExpression的结果。
- 让rval为GetValue(rref)。
- 设r为执行抽象关系比较 rval<左边的Lval等于false。 (看到 11.8.5)。
醇>
其中重要的部分是抽象关系比较。定义如下:
11.8.5抽象关系比较算法
首先在对象上调用toPrimitive
函数。虽然如果可以的话,这会偏向于返回Numbers,但也可以派生字符串。一旦发生这种情况,将检查以下内容:
一个。如果py是px的前缀,则返回false。 (如果q可以是,则字符串值p是字符串值q的前缀 连接p和其他一些String r的结果。请注意任何 String是它自己的前缀,因为r可能是空字符串。)
湾如果px是py的前缀,则返回true。
℃。设k是最小的非负整数,使得px内位置k处的字符与py 内位置k处的字符不同。 (必须有这样的k,因为String都不是另一个的前缀。)
d。设m是整数,它是px内位置k处字符的代码单位值。即设n是整数,它是py内位置k处的字符的代码单元值。 F。如果m < n,返回true。否则,返回false。
这意味着将检查String中与第一个字符不同的第一个字符。正如Esailija所指出的,IE的toString()
函数返回的字符串与其他浏览器的字符串稍有不同,导致发生了不同的比较。
浏览器之间的这种差异似乎是有效的,如下所述:
15.2.4.4 Object.prototype.valueOf()
调用valueOf方法时,将执行以下步骤:
- 设O是调用ToObject传递此值作为参数的结果。
- 如果O是使用宿主对象(15.2.2.1)调用Object构造函数的结果,那么a。返回O或其他值,例如 最初传递给构造函数的宿主对象。具体 返回的结果是实现定义的。
- 返回O。
醇>
答案 3 :(得分:2)
IE和其他浏览器都将对两个对象使用相同的字符串比较。产生差异的原因是IE会将函数转换为输入的文字字符串:
(function (x) {return x*x;})
其他浏览器(在Firefox上测试)将输出自己编译的函数解释:
function (x) {
return x * x;
}
由于IE的函数表示的第一个字符是(
,它高于1
,它将返回false。由于f
低于1
,因此其他浏览器将返回true。