Javascript 闭包函数参数
Javascript closures function parameters?
Code 属于 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
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'
父作用域无权访问其子作用域变量
function parent() {
function child() {
var childAsset = 'Mp3 Player'
}
//parent can't get childAsset
console.log(childAsset);
}
parent(); // ReferenceError childAsset not defined
同级作用域无权访问彼此的作用域变量
function childOne() {
var childOneAsset = 'Iphone'
}
function childTwo() {
console.log(childOneAsset);
}
childTwo(); // ReferenceError childOneAsset not defined
好的,回到OP提到的功能。 让我们尝试用更好的名称重新制作这个函数。 我正在向第一个示例函数添加另一个变量以显示一个点。
以下是在以下示例中调用getFirstName('Michael')
时发生的 4 件事:
- 在此函数中,变量
firstName
设置为"迈克尔" - var
nameIntro
将其设置为值"此名人是" - var
unusedString
设置为值"此字符串将被垃圾回收" - 声明
introduceCelebrity
函数 返回函数
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');
你可能已经知道了。
以下是一些需要注意的有趣事项:
getFirstName
函数除了设置其值外,对firstName
或nameIntro
不执行任何操作。 所以那里没有魔法。- 子函数
introduceCelebrity
引用这两个变量。如前所述,它可以做到这一点,因为子作用域可以访问父作用域变量。这是关闭的第一步。 - 然后返回(但不执行)
introduceCelebrity
函数,大概是为了我们可以稍后调用它。 这是关闭的第二步。 - 由于
introduceCelebrity
引用父范围变量,并且我们返回整个函数,因此 javascript 运行时会维护指向这些变量的指针,即使在getFirstName
函数返回之后也是如此。 - 由于该指针存在,因此垃圾回收器不会保留这些变量。 如果这些指针不存在,垃圾回收器将清理这些内存地址,并且这些值将无法访问。
unusedString
变量未在子函数中引用,因此它是垃圾回收的,不再可用。
因此,让我们再次查看代码:
function getFirstName (firstName) {
var nameIntro = "This celebrity is ";
function introduceCelebrity (theLastName) {
return nameIntro + firstName + " " + theLastName;
}
return introduceCelebrity;
}
var mjName = getFirstName('Michael');
当这段代码执行时,我们基本上是这样做的:
var mjName = function(theLastName) {
return nameIntro + firstName + " " + theLastName;
}
这有什么特别之处? 关闭在哪里?
因为我们的getFirstName
函数已经执行,我们可能会认为整个事情已经随着它的局部变量或资产一起消失了。 这是不正确的。
我们通过引用子函数内的父范围变量并返回子函数来创建闭包。 所以实际上,上面代码的新范围实际上看起来更像这样:
var nameIntro = "This celebrity is ";
var firstName = "Michael"
var mjName = function(theLastName) {
return nameIntro + firstName + " " + theLastName;
}
看看我们现在如何获得nameIntro
和firstName
? 那是因为我们创建了闭包。
所以现在我们称之为mjName
:
mjName('Jackson'); // 'This celebrity is Michael Jackson'
我们得到了预期的结果。
等等,最后一件事!
为了真正说明这一点,让我们将我们的示例与稍作修改的示例进行比较。
请注意,原始函数是嵌套的。 闭包仅发生在嵌套函数中。
function getFirstName (firstName) {
var nameIntro = "This celebrity is ";
function introduceCelebrity (theLastName) {
return nameIntro + firstName + " " + theLastName;
}
return introduceCelebrity;
}
var mjName = getFirstName('Michael');
让我们尝试删除该嵌套:
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
这行得通吗?
不,不会。 因为同级作用域无法访问彼此的变量。
那么我们怎么能在没有关闭的情况下让它工作呢?
-
getFirstName
必须返回一个包含变量的对象或数组 - 我们必须将
getFirstName('Michael')
设置为全局变量mjName
调用
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'
被评估为celebrityName ("Michael")("Jackson");
步骤:
- celebrityName ("Michael") 返回函数 lastName(theLastName)
- ("Jackson") 被传递给函数 lastName
- 函数lastName(theLastName)在执行时打印字符串
左到右的参数从外部到内部称为方法。
有了这个调用
var mjName = celebrityName ("Michael");
创建一个自定义函数,该函数将firstName
变量绑定到 "Michael"
。此函数由 celebrityName()
返回给您。
再次调用该返回的函数时,也会绑定lastName
,从而生成输出。
如果要绑定另一个名字,则必须再次调用celebrityName()
。
var michaelName = celebrityName( "Michael" );
var davidName = celebrityName( "David" );
michaelName( "Jackson" ); // yields "This celebrity is Michael Jackson"
davidName( "Duchovny" ); // yields "This celebrity is David Duchovny"
- 函数参数中的数据与指定变量之间的任何性能差异
- AngularJS:我可以跳过函数参数回调吗
- 函数未将值作为参数传递
- JS:检查URL中的参数,然后迭代一个参数为var的函数
- 为什么不'我们在javascript中使用函数参数的数据类型
- 你好,这是测试用例,我必须在函数中传递n个参数
- JavaScript - 多参数函数,它是多个图像库的字符串
- 如何从两个参数函数返回随机整数
- 以无点风格在Ramda中编写一个无参数函数
- JS:将单参数函数转换为可链接函数
- 正则表达式类似于Javascript中的参数函数
- 将 $' 值传递给替换的关联参数函数
- "这个“;在参数函数中
- 如何向jquery插件发送参数函数
- 将参数函数Node.js从一个js传递到另一个js
- 如何在javascript参数函数中传递PHP post方法字符串
- 装饰 Javascript Promise.then 以便参数函数接收附加参数
- 正在分析setInterval ID'是的's参数函数
- 如何根据一个参数函数计算年龄
- 对象参数/函数和/或三元运算符混淆