Gecko的JavaScript解释器引擎语义是什么?

7

编辑

考虑到下面的答案回应关于参考ECMAScript 语言规范 - 11.13.2 复合赋值

考虑为什么这些,

javascript:
   o="";  o = o + (o+=1)    ; alert(o);
   o="";  o =     (o+=1) + o; alert(o);

它们并不相同。从左到右的脚本评估存在时间语义问题(参考:ECMA规范 - 加法运算符)。其中一个后果是,+运算符不一定是可交换的。

这也可以通过以下方式看出:

javascript:
   o=1;  o = o + (o+=1)    ; alert(o);
   o=1;  o =     (o+=1) + o; alert(o);

或者

javascript:
   o=" _ ";  o = o + (o+=1)    ; alert(o);
   o=" _ ";  o =     (o+=1) + o; alert(o);

懒惰求值范例,由于我错误地并不适当地使用它,因此导致以下问题,也是我个人工作方式的不良特征之一。

原始帖子

以下考虑可能已经得到解决,但似乎没有。如果有,请提供讨论链接。

Gecko Javascript运行时引擎的正式指示性语义是一个谜。 经验测试是枯竭的,不能穷尽。

  • 是否有可靠的正式规范或官方参考,准确定义了Gecko如何解释Javascript?

参考文献ECMAScript Language Specification似乎不足,尽管为创造这样的脚本提供了信任,例如,

javascript: alert( function(){return {}}().life=42 )

由于绑定值时这些构造的含义,因此需要进行翻译。

  • 是否存在明确的范例来描述JavaScript代码中对象和实例的评估?

这将澄清按需调用(或使用)的概念,即值、引用、推断、名称等的相关性或非相关性。JavaScript是一个原型解释器,这给下面一些问题带来了隐含的含义。

以下代码预期结果是什么:

javascript: o={n:0}; f=function(){o.n+=1; return 10};
   alert([
      o.n,            f(),
      o.n,       o.n+=f(),
      o.n, eval('o.n+=f()'), 
      o.n, eval('o.n+='+f()),
      o.n,
   ].join(",\t"));

你觉得预测结果(正确的)容易吗?

这个问题有些反问,因为它是通过使用eval来强制和强调不同解释方式的微妙差异而故意设计的。这个脚本的评估(以及下面的附注)是否可以通过ECMAScript语言规范或其他文档来解决?

(顺带一提:

javascript: ra=[];
   alert([
      ra, ra[ra.length]=" partially defined.",
      ra, ra.push("\n RA is not shown"),
      ra, ra.reverse()[42],
   ].join(",\t\t"));

显示如下:

RA未显示,部分定义。, 部分定义。, 
RA未显示,部分定义。, 2, 
RA未显示,部分定义。, 

其中ra的部分评估与o.n的不相似!

以下内容比使用o.n更简单:

javascript: o=""; f=function(){o+=1; return 0};
   alert([
      o,          f(),
      o,       o+=f(),
      o, eval('o+=f()'), 
      o, eval('o+='+f()),
      o,
   ].join(",\t"));

它显示:

, 0、1、10、10、100、100、10010、10010

考虑以下脚本:

javascript:
   asn="\t\t and so now,\t o.n is "; nl="\n\n";
   o={}; f=function(){o.n+=1; return 10};
   alert(["Using:\n",window.navigator.userAgent,
   nl,"The function f() is:\n ",f,
   nl,"What the!!?!? \t\t\t\t\t\t\t initially \t\t o.n is ",          o.n = 0,
 nl,"Called as a procedure: \t\tf() is ", f(),                   asn, o.n,
nl,"but, instead of 12 \t\to.n+=f() is ", o.n+=f(),              asn, o.n,
     nl,"however eval'd\t\to.n+=f() is ", eval("o.n+="+f()),     asn, o.n,
    "!\n\nIt makes no functional difference if, instead of o.n, o['n'] is used.",
    "\nThe expected o.n evaluation sequence is 0, 1, (+1+10=) 12, (+1+10=) 23.",
    "\n_____ _____ _____ _____ _____ _____ _____ _____^^ missing in result",
  ].join(""));

Gecko引擎输出:
使用: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.3) Gecko/20100423 Ubuntu/10.04 (lucid) Firefox/3.6.3
函数f()是: function () { o.n += 1; return 10; }
What the!!?!? 最初 o.n为0
作为一个过程调用: f()是10 现在, o.n为1
但是,o.n+=f()是11 现在, o.n为11 而不是12
然而eval'd o.n+=f()是22 现在, o.n为22!
如果使用o['n']代替o.n,这没有任何功能上的区别。 预期的o.n评估顺序是0, 1, (+1+10=) 12, (+1+10=) 23。

我不是语言专家,但这似乎非常合理。o.n每次都会被单独评估,从而为您提供当前存储在o的字段n中的整数 - 该字段稍后将被覆盖,先前存储在其中的整数不会发生变化。第二个示例将几个对同一对象的引用放入结果数组中,并在此过程中对其进行了更改,因此当您查看最终结果时,您会多次查看同一对象。 - user395760
1
不确定为什么你多次提到“Gecko”(实际上是Mozilla的SpiderMonkey)。为什么你没有在Chrome和Internet Explorer中进行测试呢? - Nayuki
1个回答

2

JavaScript的执行模型是急切评估(按值调用),在ECMA标准中有明确规定。所有这些问题都可以通过仔细阅读标准来解决。例如,为什么上面的结果是11而不是12的原因在标准的11.13.2中清楚地说明了。在调用f()之前,o.n会被评估,而不是在步骤2指定之后。


非常感谢 - 让我思考了一下 - 请查看原帖之前的编辑。 - Ekim
1
允许副作用的所有语言都有类似的问题。一些语言,如C和C++,甚至到了说评估顺序是未定义的程度,从而允许实现根据给定结果的变化而变化。JavaScript不能这样做,因此明确指定了顺序。无论如何,在具有副作用的过程性语言中,应避免使副作用可见的结构,例如o = o + (o+=1)或更传统的o = o + ++o。 - chuckj
甚至可以使用((o=1)+(++o))而不是((++o)+(o=1)) - Ekim
1
是的,在C或受C影响的语言中,所有这些东西都应该避免使用。它们在Java、C#和JavaScript中是标准化的,在C和C++中未定义,并且在所有这些语言中都很丑陋。 - chuckj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接