JavaScript中的返回函数有什么用途?

3
我有一个非常简单的例子,它在没有返回函数的情况下可以正常工作,在使用返回函数时会失败:
 function hello() {
    alert('hello');
}

function hi() {
    return function() {
        alert('hi');
    }
}

我知道通过执行 hi()(); 我可以运行第二个函数,但我很少在代码中看到这样的写法。那么如何在不使用此方法的情况下返回函数呢?因为我经常看到人们这样做。


1
你在这里究竟想要实现什么?hi()函数运行良好,它返回一个完全按照设计的函数。你可以像你展示的那样直接使用hi()()执行返回的函数,或者像处理从任何函数返回的值一样处理返回的函数。那么,你的问题到底是什么? - David
我想知道如何在不使用()();的情况下返回此函数,因为我经常看到其他人这样做。 - David G
1
你已经在返回函数了。这就是为什么 hi()() 能够实现它的功能。hi() 返回一个函数。hi()() 返回并随后执行一个函数。你可以对返回的函数做任何想做的事情,包括执行它。如果你只想返回它而不立即执行它,那么只需调用 hi() 并将返回值分配给一个变量即可。 - David
3个回答

12

返回函数可以从第一次调用的函数中缓存一些变量,然后您可以稍后在返回函数上执行某些操作。

例如,

function hi(lastName) {
    return function(firstName) {
        alert('hi ' + firstName + lastName);
    }
}

var chen = hi("Chen");
chen("Jumper");
chen("Dennis");

5
为了充分利用它,您需要组合三个要素:数据类型、函数执行(或在某些源中称为调用)以及作用域和闭包。
我将尝试提供最简单的视角:
首先,您需要理解JavaScript中返回值和数据类型的问题。
JavaScript中的任何表达式都会计算出以下数据类型之一,并成为另一个表达式的一部分,以进行进一步的计算(其中我观察到最常见的是分配到变量中,其次是作为函数的返回值返回): number, string, boolean, object, undefined - 和 - function。 (还有一些伪类型-如InfinityNaN,但现在不管它,稍后再谷歌它。)
JavaScript中的每个表达式都有一个返回值。 每个表达式。 真的。 有时该值只是undefined,但这也是一个返回值。 但是,说每个表达式都有一个返回值- 这包括函数定义。两者之间没有太大区别:
function foo(){}

并且

foo = function(){}

(为了保持答案的实用性,我将避免讨论这两种形式之间的区别。)
这导致下一个主题-在JavaScript中执行函数。
在上面的示例中,返回的值是一个函数引用,您可以使用()运算符稍后执行它。是的-运算符()是一个运算符,它执行引用后面的代码,并向其提供参数堆栈,其中包含()中提供的值。
在这种情况下,在声明foo函数并将其保留在变量foo中之后,您可以随时调用foo() - 这是因为foo是一个变量,它保存对函数的引用,稍后可以执行-只要变量foo没有被覆盖另一个值或被同名的更局部变量隐藏即可。
强调一下:调用foo()首先评估foo的含义,然后将其作为带有空参数堆栈的函数操作(因为它是foo(),而不仅仅是foo)。 在这种意义上,您可以评估foo()的含义,然后将其作为函数进行操作-就像在您的原始示例foo()()中一样。你知道吗?您可以继续进行此操作:foo()()()foo()()(),但是请注意,一旦您尝试对要操作的评估表达式()不是函数引用,您将获得异常。
这就是为什么您可以看到类似于的代码
 obj[ handler ](42);

或更糟。
 obj[ getHandlerName(handler) ] (42);

(为了简短,我只解释第一个) - 它首先评估obj是什么,然后假设它是一个对象并评估obj[handler],然后假设它是一个函数并尝试将其执行为obj [handler](42) - 将42作为单个参数传递。请注意,handler是一个变量名,可以包含任何值,在这种情况下,它将作为字符串尝试(或使用它的toString()方法的返回值 - 因为[]也是一个运算符,但那是另一篇文章的主题)。

这个参数堆栈是由函数的执行隐含的,并且没有关于提供给函数和函数实际期望的参数数量或类型的限制。

在执行期间传递给函数的所有参数都可以使用隐含参数arguments访问,它是一个伪数组构造 - 最好简单地描述为参数堆栈...为什么是伪数组?它不响应Array提供的任何API,除了属性length和索引器访问(即arguments [0],arguments [1]等)。

函数可以为它希望处理的参数定义变量名,这些变量名将对应于参数变量名中的0索引位置arguments [n]

现在,如果一个函数是一种数据类型,它可以返回并稍后执行。在那种情况下,没有太大的区别

function a(){
   return function b(){}
}

并且

function a(){
  function b(){}
  return b; //pay attention - no braces = don't execute b, return the func-ref.
}

and

function a(){
   var b = function(){}
   return b;
}

实际上,最后一个问题被认为是不规范的,下一个问题将帮助您了解原因。

第二个问题 - 作用域和闭包。

在JavaScript中,变量的作用域是定义它们的函数。(对于来自其他语言的程序员来说,这很令人困惑,因为在其他语言中,变量仅限于它们被定义的块 - 这种限制不适用于JavaScript。只有函数体才具有作用域的目的)。

从这个意义上讲,最顶层,也就是“根”或“主” - 是最高级别,在那里定义全局变量。这意味着在根上定义的变量和函数可供执行的所有代码访问,并且因此充当全局变量。

闭包是指执行函数在其中定义其他函数,并“公开”其中一个或多个函数 - 通过将它们分配给全局变量,或通过返回一个函数引用,稍后由调用定义函数的代码保留。

现在,由于如上所述 - 每次执行函数都会定义至少一个名为arguments的变量 - 除了它定义的参数变量名称之外,它还可以访问任何定义在其中的函数的任何代码 - 或由定义在其中的函数的执行定义的代码(或由执行它的函数定义的函数的执行定义的代码,或由执行...好吧,你懂了)。

这意味着:

var G = "global"
function a(x,y){
   var h = "local to closure a"
     , z = function(){ 
               var k = "local to this function"
               //code here can access G
               //code here can access k
               // - and x and y - and - h and z!
           }
     ;
   return z;
}

这就为一些非常有趣的用法打开了空间 - 比如回调函数可以访问本地变量,这些变量保存了由参数传递给while检索回调z时定义的私有状态。

我希望这样能够更简单地将一些东西放在一起 :)


1

自执行函数和回调函数都是常见的方法。

在这里找到可用的示例: http://jsfiddle.net/ezmilhouse/9BbGC/

// self executing

function hi( text ) {
    return (function(text) {
        alert(text);
    })(text);
}

hi('ho');

// callback

function ho(callback){
    callback();
}

ho(function(){
   alert('ha');
});

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