Javascript关闭函数参数?

use*_*358 6 javascript closures

代码属于javascriptissexy.com我的问题是为什么调用mjName("杰克逊")返回"这个名人是迈克尔·杰克逊"?是不是在任何外部函数中给出的第二个参数,对js =内部函数参数说?有人能详细解释整个概念吗?

function celebrityName (firstName) {
    var nameIntro = "This celebrity is ";
// this inner function has access to the outer function's variables, including the parameter
    function lastName (theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    return lastName;
}

var mjName = celebrityName ("Michael");
    // At this juncture, the celebrityName outer function has returned.

// The closure (lastName) is called here after the outer function has returned above
// Yet, the closure still has access to the outer function's variables and parameter
mjName ("Jackson"); // This celebrity is Michael Jackson
Run Code Online (Sandbox Code Playgroud)

kur*_*ett 12

OP要求详细解释整个概念.这里的尝试是描述闭包发生所必需的核心元素.

我认为与javascriptissexy这样的例子混淆的部分原因是这些函数的名称并没有清楚地表示它们应该做什么,特别是对于那些刚接触javascript或新编码的人.

让我们从讨论范围开始:

在Javascript中,每个函数都创建自己的本地范围或内存空间.这称为词法范围.此内存空间存储函数参数中的所有变量以及函数体内的声明变量和表达式(在花括号内).

从javascriptissexy的例子中可以看出,我们可以嵌套函数.由于每个函数都创建了自己的局部范围,因此我们需要了解这些范围如何相互关联和相互作用.范围可以有三种不同类型的关系.

我建议您在浏览器开发控制台中测试所有这些代码段:

子范围可以访问其父级(以及祖父母,祖父母等)范围变量

    function parent() {

        var parentAsset = 'The Minivan'

        function child() {

            //child has access to parent asset

            console.log(parentAsset);

        }

        // call child function

        child();
    }

    parent();  // 'The Minivan'
Run Code Online (Sandbox Code Playgroud)

父范围无权访问其子范围变量

    function parent() {

        function child() {

            var childAsset = 'Mp3 Player'

        }

        //parent can't get childAsset

        console.log(childAsset);

    }

    parent();   // ReferenceError childAsset not defined
Run Code Online (Sandbox Code Playgroud)

同级范围无法访问彼此的范围变量

    function childOne() {

        var childOneAsset = 'Iphone'

    }

    function childTwo() {

        console.log(childOneAsset);

    }

    childTwo();  // ReferenceError childOneAsset not defined
Run Code Online (Sandbox Code Playgroud)

好的,回到OP提到的功能.让我们尝试用更好的名字重新制作这个功能.我在第一个示例函数中添加了一个变量来显示一个点.

在下面getFirstName('Michael') 的示例中调用时,会发生以下四种情况:

  1. 在此函数中,变量firstName设置为'Michael'
  2. var nameIntro设置为"此名人是"的值
  3. var unusedString设置为值"此字符串将被垃圾回收"
  4. 该函数introduceCelebrity已声明
  5. 该函数introduceCelebrity返回

    function getFirstName (firstName) {
    
        var nameIntro = "This celebrity is ";
        var unusedString = "This string will be garbage collected";
    
        function introduceCelebrity (lastName) {
            return nameIntro + firstName + " " + lastName;
        }
    
        return introduceCelebrity;
    }
    
    var mjName = getFirstName('Michael');
    
    Run Code Online (Sandbox Code Playgroud)

你可能已经知道了.

以下是一些值得注意的有趣事项:

  • getFirstName函数不做任何处理firstNamenameIntro其他不是设置它们的值.所以那里没有魔力.
  • 子函数introduceCelebrity 引用这两个变量.如前所述,它可以这样做,因为子范围可以访问父范围变量.这是关闭的第一个重要步骤.
  • introduceCelebrity然后返回该函数(但不执行),可能是因为我们可以在以后调用它.这是关闭的第二步.
  • 因为introduceCelebrity引用父作用域变量,并且我们返回整个函数,所以即使在getFirstName函数返回之后,javascript运行时也会维护指向这些变量的指针.
  • 因为该指针存在,所以垃圾收集器单独留下这些变量.如果这些指针不存在,垃圾收集器将通过并清理那些内存地址,这些值将无法访问.
  • 该子unusedString变量未在子函数中引用,因此它被垃圾回收并且不再可用.

那么让我们再看看代码:

function getFirstName (firstName) {

    var nameIntro = "This celebrity is ";

    function introduceCelebrity (theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    return introduceCelebrity;
}

var mjName = getFirstName('Michael');
Run Code Online (Sandbox Code Playgroud)

当这段代码执行时,我们基本上是这样做的:

var mjName = function(theLastName) {
    return nameIntro + firstName + " " + theLastName;
}
Run Code Online (Sandbox Code Playgroud)

这个有什么特别之处?关闭在哪里?

因为我们的getFirstName函数已经执行了,所以我们可能会认为整个事情已经随着局部变量或资产而消失了. 这是不正确的.

我们通过引用子函数内的父作用域变量并返回子函数来创建闭包.实际上,上面代码的新范围实际上看起来更像是这样的:

var nameIntro = "This celebrity is ";

var firstName = "Michael"

var mjName = function(theLastName) {
    return nameIntro + firstName + " " + theLastName;
}
Run Code Online (Sandbox Code Playgroud)

了解现在如何nameIntro以及firstName现在可以使用? 因为我们创造了封闭.

现在我们打电话给mjName:

mjName('Jackson');  // 'This celebrity is Michael Jackson'
Run Code Online (Sandbox Code Playgroud)

我们得到预期的结果.

等等,最后一件事!

为了真正推动这一点,让我们将我们的例子与略微修改的例子进行比较.

请注意原始函数是嵌套的. 闭包只发生在嵌套函数中.

function getFirstName (firstName) {

    var nameIntro = "This celebrity is ";

    function introduceCelebrity (theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    return introduceCelebrity;
}

var mjName = getFirstName('Michael');
Run Code Online (Sandbox Code Playgroud)

让我们尝试删除嵌套:

function getFirstName (firstName) { 
    var nameIntro = "This celebrity is ";
}

function introduceCelebrity (theLastName) {
    return nameIntro + firstName + " " + theLastName;
}

var mjName = getFirstName('Michael');
introduceCelebrity('Jackson');  

// ReferenceError: nameIntro is not defined
Run Code Online (Sandbox Code Playgroud)

这会有用吗?

不,它不会.因为兄弟范围无法访问彼此的变量.

如果没有封闭,我们怎么能让它工作呢?

  1. getFirstName 必须使用我们的变量返回一个对象或数组
  2. 我们必须设置getFirstName('Michael')一个全局变量mjName
  3. 打电话introduceCelebrity('Jackon'),传递我们的价值观mjName

    function getFirstName (firstName) { 
    
        var nameIntro = "This celebrity is ";
    
        return {
            firstName: firstName,
            nameIntro: nameIntro
        }
    }
    
    var mjName = getFirstName('Michael');  // returns our object
    
    function introduceCelebrity (theLastName, firstName, nameIntro) {
        return nameIntro + firstName + " " + theLastName;
    }
    
    introduceCelebrity('Jackson', mjName.firstName, mjName.nameIntro);  
    
    // 'This celebrity is Michael Jackson'
    
    Run Code Online (Sandbox Code Playgroud)


use*_*109 10

该功能被评估为 celebrityName ("Michael")("Jackson");

脚步 :

  1. celebrityName("Michael")返回函数lastName(theLastName)
  2. ("杰克逊")被传递给函数lastName
  3. 函数lastName(theLastName)在执行时打印字符串

从左到右的参数从外部到内部称为方法.