5

在一个在线课程中,Kyle Simpson 说下面的代码演示了在 javascript 中提升的必要性,因为如果不提升,“其中一个函数总是被声明得太晚”。

a(1)  // 39

function a(foo){
  if (foo > 20) return foo
    return b(foo+2)
}

function b(foo){
  return c(foo) + 1
}

function c(foo){
  return a(foo*2)
}

但这工作得很好。

var a = function(foo){
  if (foo > 20) return foo
    return b(foo+2)
}

var b = function(foo){
  return c(foo) + 1
}

var c = function(foo){
  return a(foo*2)
}

a(1) // 39

那么故事是什么?抛开调用的方便和位置不谈,有什么需要吊装的情况吗?

4

3 回答 3

4

除了调用的方便和放置之外,没有任何需要提升的情况。

只要确保在使用它们之前声明所有函数。

注意:在某些浏览器中,function a(){}创建一个有名字的函数,avar a = function(){}没有(被认为是匿名函数)。调试时使用函数名。你也可以这样做var b = function a(){}

于 2016-04-25T05:40:05.023 回答
4

我关于非提升 JS 无法支持相互递归的说法只是出于说明目的的猜想。它旨在帮助理解语言需要了解范围内可用的变量。这不是精确语言行为的处方。

像提升这样的语言特性——实际上不存在提升,它只是在编译期间、执行之前提前在作用域环境中声明的变量的隐喻——是一个如此基本的特征,以至于不能轻易推断出何时与语言的其他特征分开。

而且,仅在 JS 中不可能完全检验这个假设。OP 中的代码段仅处理部分等式,即它使用函数表达式而不是函数声明来避免函数提升。

我用来比较的语言是 C,例如,它需要在 .h 头文件中声明函数签名,以便编译器知道函数的外观,即使它还没有“看到”它。没有它,编译器就会窒息。从某种意义上说,这是一种手动吊装。C 这样做是为了进行类型检查,但人们可以想象这种需求的存在是出于其他原因。


另一种思考方式是 JS 是否是一种编译语言,在它执行之前已经发现了所有内容,或者它是否是一次自上而下的解释。

如果 JS 是自上而下解释的,并且它得到了一个a()函数的定义,该函数在其中引用了一个b()它还没有看到的,那可能是一个问题。如果该调用表达式被非惰性处理,那么引擎此时无法弄清楚b()调用将是什么,因为b()尚未处理。有些语言是惰性的,有些是非惰性的。

实际上,JS 在执行之前首先被编译,因此引擎在运行任何函数之前已经发现了所有函数(也称为“提升”)。JS 也将表达式视为惰性,因此一起解释了为什么相互递归可以正常工作。

但是如果 JS 没有提升和/或不是惰性的,可以想象 JS 引擎将无法处理相互递归,因为a()和之间的循环引用b()实际上意味着两者之一总是被声明为“为时已晚”。

这就是我在书中的全部意思。

于 2016-04-25T13:38:47.210 回答
-1

第二个代码块工作正常,因为您是a(1)在所有函数初始化后调用的。尝试以下块:

var a = function(foo){
  if (foo > 20) return foo
    return b(foo+2)
}

var b = function(foo){
  return c(foo) + 1
}

a(1);

var c = function(foo){
  return a(foo*2)
}

这将给出一个错误Uncaught TypeError: c is not a function,因为function assigned to c没有提升。这就是你需要吊装的原因。

因为如果您在第一个代码块中声明函数,所有函数都将被提升,您可以a在代码中的任何位置调用。在其他情况下,情况并非如此。

于 2016-04-25T05:37:47.937 回答