if/else 语句中的函数声明?

IT技术 javascript function scope
2021-01-12 21:43:01

如何处理函数声明?

var abc = '';
if (1 === 0) {
  function a() {
    abc = 7;
  }
} else if ('a' === 'a') {
  function a() {
    abc = 19;
  }
} else if ('foo' === 'bar') {
  function a() {
    abc = 'foo';
  }
}
a();
document.write(abc); //writes "foo" even though 'foo' !== 'bar'

此示例在 Chrome 和 Firefox 中生成不同的输出。Chrome 输出foo而 FF 输出19

4个回答

当提出这个问题时,ECMAScript 5 (ES5) 很流行。在 ES5 的严格模式下,函数声明不能​​嵌套在if内,如问题所示。在非严格模式下,结果是不可预测的。不同的浏览器和引擎针对如何处理块内的函数声明实施了自己的规则。

截至 2018 年,许多浏览器都支持 ECMAScript 2015 (ES2015),以至于现在允许在 blocks 中声明函数在 ES2015 环境中,块内的函数声明将限定在该块内。问题中的代码将导致未定义函数错误,因为该函数a仅在if语句范围内声明,因此不存在于全局范围内。

如果你需要有条件地定义一个函数,那么你应该使用函数表达式

@Jesse——规范允许扩展,Mozilla 选择在块中允许FunctionStatement(ECMA-262 没有描述)。因此,这不是错误,而是规范允许的故意扩展。这当然不是语法错误,我所知道的任何 UA 在遇到它时都不会抛出错误,他们只是根据他们将其视为 FunctionDeclaration 还是 FunctionStatement 来对其进行不同的处理。
2021-04-02 21:43:01
@RobG:不,答案是正确的,根据 ECMAScript-262 规范,第一个示例是不合法的,因为只有语句可以出现在块中并且函数声明不是语句(扩展不是标准的,也不是可移植的)。
2021-04-03 21:43:01
the first example in the question is not legal syntax错误的陈述,请阅读 davin 的评论(这是正确的)。函数语句是 Mozilla 浏览器的合法扩展。
2021-04-05 21:43:01
@Tina,我需要更新或删除此答案。ES2015 允许在块内进行函数声明。
2021-04-05 21:43:01
终于有人猜对了。做得好!可能值得一提的是谁实现了什么(即几乎每个人都遵循 ES 推荐,除了 SpiderMonkey)。
2021-04-09 21:43:01

来自http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/

在 javascript 中,你有函数声明:

function foo() {
}

和函数表达式

var foo = function() {
}

引自http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting

“函数声明和函数变量总是被 JavaScript 解释器移动('提升')到它们的 JavaScript 范围的顶部”。

所以在你的第一个例子中发生的事情是 , 的函数声明function a()被提升到 Javascript 范围的顶部,从而产生 'foo' 即使 if 评估为 false

将其var foo视为普通的 Javascript 语句,它仅在您的 javascript 运行时执行,与 不同function foo(),这就是以下内容有效的原因:

alert(foo());

function foo() {
   return 'gw ganteng';
}

在这里,function foo()由解析器解析,放入foo()当前范围,然后再尝试调用alert(foo())

http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/

在 JavaScript 执行中,有 Context(ECMA 5 将其分解为 LexicalEnvironment、VariableEnvironment 和 ThisBinding)和 Process(一组要按顺序调用的语句)。当进入执行范围时,声明会影响 VariableEnvironment。它们与声明(例如退货)不同,并且不受其流程规则的约束。

是的,我认为这里的关键是函数与表达式。
2021-03-15 21:43:01
@IgnacioVazquez-Abrams 考虑到提升和执行流程,应该以最后的声明为准。
2021-03-24 21:43:01
您是否有任何参考资料指定了行为应该是什么?
2021-03-27 21:43:01

ECMA-262 v5 要求实现在第一次进入任何新的全局或函数级执行上下文时注册所有函数和变量声明。从技术上讲,Chrome 正在这里做这件事,因为它在执行之前查看elsethen阻止并注册a()不幸的是,它产生了最不可读的结果。

FF 等待直到它评估 if 语句,然后才会评估并将函数和变量声明添加到当前上下文。顺便提一句。两个浏览器都在 catch 和 finally 子句中这样做。

这实际上只是两个不同的 ECMA 实现处理一个不应该存在的特性的问题。手头的场景说明了为什么函数声明不应该在控制流语句中。

不能在{}外访问函数声明

if (true) {
  function sayHi() {
    alert("hii");
  }
  sayHi(); //accessible
}

sayHi(); //error, not accessible since out of the block

如果要定义条件函数,请使用函数表达式,例如

let sayHi;
if (true) {
  sayHi = function(){
    alert("hii");
  }
  sayHi(); //accessible
}

sayHi(); //accessible
上面的代码不会产生错误,而是提醒“hii”。铬91
2021-03-21 21:43:01
可能是浏览器增强?
2021-04-09 21:43:01