在尝试学习JavaScript闭包时,我有点困惑。
从我在网上收集的内容来看,关闭是......
在另一个函数中声明一个函数,并且该内部函数可以访问其父函数的变量,即使该父函数已经返回。
以下是最近项目中的一小部分脚本示例。它允许div中的文本通过按钮上下滚动。
var pageScroll = (function() {
var $page,
$next,
$prev,
canScroll = true,
textHeight,
scrollHeight;
var init = function() {
$page = $('#secondary-page');
// reset text
$page.scrollTop(0);
textHeight = $page.outerHeight();
scrollHeight = $page.attr('scrollHeight');
if (textHeight === scrollHeight) { // not enough text to scroll
return false;
};
$page.after('<div id="page-controls"><button id="page-prev">prev</button><button id="page-next">next</button></div>');
$next = $('#page-next');
$prev = $('#page-prev');
$prev.hide();
$next.click(scrollDown);
$prev.click(scrollUp);
};
var scrollDown = function() {
if ( ! canScroll) return;
canScroll = false;
var scrollTop = $page.scrollTop();
$prev.fadeIn(500);
if (scrollTop == textHeight) { // can we scroll any lower?
$next.fadeOut(500);
}
$page.animate({ scrollTop: '+=' + textHeight + 'px'}, 500, function() {
canScroll = true;
});
};
var scrollUp = function() {
$next.fadeIn(500);
$prev.fadeOut(500);
$page.animate({ scrollTop: 0}, 500);
};
$(document).ready(init);
}());
此示例是否使用闭包?我知道它在函数中有函数,但有没有使用保留的外部变量?
我是否在不知情的情况下使用它们?
由于
如果我把它放在$(document).ready(init);
语句下面,这会成为一个闭包吗?
return {
scrollDown: scrollDown
};
那么,如果我想让文本从JavaScript中的任何其他地方向下滚动,我可以做到
pageScroll.scrollDown();
答案 0 :(得分:12)
每当你定义一个函数时,该函数将“包围”它所定义的范围链。当执行该函数时,范围链在其体内可用。当在另一个函数内定义函数时,作用域链包括:
闭包的真正价值在于返回一个内部函数,该函数捕获了一个不会改变的封闭变量,如下所示:
function test (value){
return function(){
alert(value); //using enclosed argument
}
}
var t1 = test("first");
var t2 = test("second");
t1();//alerts "first"
t2();//alerts "second"
闭包的一个危险是它们将引用包含在封闭的属性中,而不是它们的值的快照,因此请注意在循环内创建一个封闭函数,如下所示:
function makeArray (){
var result=[]
for (var i =0; i < 3; ++i){
result.push(function(){
alert("function #" +i); //using enclosed i
})
}
return result;
}
var fnArray =makeArray();
fnArray[0]();//alerts "function #3"
fnArray[1]();//alerts "function #3"
fnArray[2]();//alerts "function #3"
在循环中,您需要找到一种方法来使您的封闭变量保持不变。下面是使用嵌套闭包将重用计数器复制到单次使用参数的示例:
function makeArray (){
var result=[]
var wrapFunction=function (counter){
return function(){
alert("function #" +counter); //using enclosed counter
}
}
for (var i =0; i < 3; ++i){
result.push(wrapFunction(i))
}
return result;
}
var fnArray =makeArray();
fnArray[0]();//alerts "function #0"
fnArray[1]();//alerts "function #1"
fnArray[2]();//alerts "function #2"
答案 1 :(得分:3)
看看Dmitry A. Soshnikov关于ECMA-262('javascript')的博客系列。
Post 6 http://dmitrysoshnikov.com/ecmascript/chapter-6-closures/是关于闭包的,如果您阅读之前的帖子,您应该对它有很好的把握。
短篇小说是;函数声明或放置在函数(声明或表达式)中的函数表达式将父函数作用域添加到其作用域链中。因此,它可以访问范围链中每个函数中包含的所有变量(以全局范围结束)。但到目前为止,我们并没有谈论关闭。
'闭包'的概念在您通过返回对函数的引用创建对此类包含函数 范围链的引用时出现,或者通过在范围链中更高的对象上添加对它的引用作为属性。 [这样你通过调用函数(通过该引用)可以修改不可访问范围内的变量 hidden 。]
此引用还将导致所述scopechain中包含的所有变量至少具有一个引用(来自每个范围),这意味着范围链中存在的所有变量都不能被垃圾收集。这是一个所谓的内存泄漏(除非你实际上计划使用变量!)。
答案 2 :(得分:2)
我认为你没有使用任何闭包,因为你没有从你的外部函数返回内部函数,所以这些变量在被调用时总是在范围内。换言之,必须从init()
内调用scrollDown()
,scrollUp()
和pageScroll()
。如果pageScroll()
返回其三个内部函数中的任何一个,然后您在pageScroll()
之外调用它们,则使用$page
,$next
,$prev
等变量将是闭包。我想。
答案 3 :(得分:1)
我不是Javascript的专家,但是从我在这里读到的,是的,你有封闭。
请注意,例如,所有内部函数都共享变量$ page。 init分配给它,稍后滚动函数更新它。所以原则上内部函数是外部函数的闭包。外部函数将init传递给document.ready,因此在一些Javascript-ish方式中,它返回一个闭包。
也就是说,我还要说这段代码是函数式编程的一个非常糟糕的例子,因为纯函数不应该有副作用(比如分配给共享变量)。然而,它有封闭。
答案 4 :(得分:1)
您附加了onclick
元素#page-next
元素的一个函数scrollDown
,它保留了对象canScroll
的变量pageScroll
的引用。所以是的,你正在使用闭包。
事实上,您的示例中有4个闭包:发送到$next.click
的函数,发送到$prev.click
的函数,发送到$page.animate
的函数和发送到$(document).ready
的函数{1}}。
答案 5 :(得分:1)
是的,你确实有一个闭包,看看这个简单的例子
<script type="text/javascript">
var t1=null;
function test(){
alert('Test');
var x = 10;
t1 = function(){
alert('x='+ x);
};
};
function test2(){
t1();
}
</script>
<a href="javascript:test();">Test me 1 </a>
<a href="javascript:test2();">Test me 2</a>
在这种情况下,闭包可以访问其父变量,我们的函数't1'访问私有声明的var x;正如你所说'内部函数可以访问其父函数的变量,即使在父函数返回后也是如此。'当你单击'Test me 2'时,你会得到一个x = 10的警报,即使那些函数test()已经返回了,这是否有意义。
答案 6 :(得分:1)
是的,示例代码中有许多闭包。混淆可能来自对您的定义中的闭包要求过于严格的要求。虽然闭包 可以在闭包的生命周期内访问其绑定的自由变量,但闭包不是必需,以延长这些变量的生命周期,使其超出正常环境将该函数视为闭包。
例如,假设我们有一个函数findAllPeople
,它在列表中找到所有具有名字的人,该名字作为函数的参数提供。我们可以创建一个内部函数,它接受一个人并根据提供的名称测试他们的名字。该内部函数将有两个自由变量 - 即要测试的人和名称。要测试的名称绑定到提供给findAllPeople
的参数。因此,内部函数关闭超过该自由变量。内部函数在执行findAllPeople
时被创建,使用和丢弃的事实并没有在findAllPeople
之外的任何地方使用,这与内部函数是闭包这一事实无关。 / p>
答案 7 :(得分:0)
是的,确定有关闭。例如,您使用在外部作用域中声明的$page, $next
等,例如init
(使用它们)。
答案 8 :(得分:0)
今天不要带我的Javascript:Good Parts一书工作......
我认为简短的答案是“否”,因为定义的这一部分:“即使在父函数返回后也是如此。”但我不是百分百肯定。
但这可能会有所帮助。
Javascript的好剪辑:好的部分
http://www.youtube.com/watch?v=hQVTIJBZook&feature=player_embedded
答案 9 :(得分:0)
var func = (function(){
var txt = 'Hi There!'
return function(){
alert(txt);
}
})();
func(); //alert 'Hi There', eventhough the outter function has exit