JavaScript闭包的实际用途是什么?


279

我在努力围绕JavaScript闭包。

通过返回内部函数,我可以访问其直接父级中定义的任何变量。

这对我有什么用?也许我还没有完全明白这一点。我在网上看到的大多数示例没有提供任何真实的代码,只是模糊的示例。

有人可以告诉我现实世界中使用闭包吗?

例如,这是吗?

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();

17
尽力而为+1 :-)开始关闭似乎很艰巨,我知道它们适合我。一旦掌握了这些技巧,您将立即成为更好的编码器。
安迪E

7
我刚刚写了一篇有关JavaScript闭包的博客文章,您可能会发现helfpul。
Skilldrick

@Skilldrick。链接已失效...而且我也发现此实际示例非常有帮助。youtube.com/watch?v=w1s9PgtEoJs
阿披(Abhi)2016年

Answers:


239

我用闭包来做类似的事情:

a = (function () {
    var privatefunction = function () {
        alert('hello');
    }

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();

如您所见,a现在有了一个对象,其中有一个方法publicfunctiona.publicfunction())调用privatefunction,该方法仅存在于闭包内部。您不能privatefunction直接致电(即a.privatefunction()),而只能致电publicfunction()

它是一个最小的示例,但也许您可以看到它的用处?我们用它来强制执行公共/私有方法。


26
啊,如果这是一个闭包,那么我已经在不知情的情况下使用了闭包!我经常将函数放在另一个这样的函数中,然后像返回您的示例一样,通过返回对象文字来公开我需要的任何函数。
alex

1
是的,如您所见,您保留了函数的上下文,因为返回的对象在其中引用了变量(和函数)。因此,您一直在使用它们,只是不知道。
弗朗西斯科·索托

9
从技术上讲,您在浏览器上用Javascript编写的每个函数都是闭包,因为窗口对象已绑定到它。
亚当·根特

9
我知道这是一个古老的问题,但是对我来说,这仍然没有提供足够的答案。为什么不直接调用该函数呢?为什么需要私有功能?
qodeninja 2014年

5
因为即使该示例仅具有功能,它也可能具有无法从外部访问的变量。说:var obj =(function(){var value = 0; return {get:function(){return value;},set:function(val){value = val;}}})(); obj.set(20); obj.get(); => 20等
弗朗西斯科·索托2014年

209

假设您要计算用户单击网页上的按钮的次数
为此,您在onclick按钮事件中触发了一个函数 以更新变量的计数

<button onclick="updateClickCount()">click me</button>  

现在可能有很多方法,例如:

1)您可以使用全局变量和函数来增加计数器

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

但是,陷阱是页面上的任何脚本都可以更改计数器,而无需调用updateClickCount()


2)现在,您可能正在考虑在函数内部声明变量:

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

但是,嘿!每次updateClickCount()调用函数时,计数器都会再次设置为1。


3)关于嵌套函数的思考

嵌套函数可以访问它们“上方”的范围。
在此示例中,内部函数updateClickCount()可以访问父函数中的计数器变量countWrapper()

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

如果您可以updateClickCount()从外部访问该功能,并且还需要找到一种counter = 0仅执行一次而不是每次执行的方法,那么这可能已经解决了对立难题。


4)关闭救援!(自调用功能)

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

自调用功能仅运行一次。它将设置counter为零(0),并返回函数表达式。

这种方式updateClickCount成为一种功能。“很棒”的部分是它可以访问父作用域中的计数器。

这称为JavaScript闭包。它使函数具有“ 私有 ”变量。

counter是由匿名函数的范围内保护,并且只能使用add函数来改变!

关于闭包的更生动的例子:

<script>
        var updateClickCount=(function(){
    	var counter=0;
    
    	return function(){
    	++counter;
    	 document.getElementById("spnCount").innerHTML=counter;
    	}
      })();
    </script>

    <html>
	 <button onclick="updateClickCount()">click me</button>
	  <div> you've clicked 
		<span id="spnCount"> 0 </span> times!
	 </div>
    </html>


参考:https : //www.w3schools.com/js/js_function_closures.asp


49
这是让我说“哦,这就是为什么我要使用闭包!” 的第一个答案。
魔鬼的拥护者

6
你让我很开心:)
JerryGoyal

15
我刚刚阅读了关于闭包的w3schools页面,然后来到这里获取更多信息。这与w3schools页面相同: w3schools.com/js/js_function_closures.asp
tyelford

1
@JerryGoyal您可以使用2个单独的按钮来使其工作吗?我想不出两个变量(函数的副本)怎么办,这似乎消除了一些主要的好处/便利。
泰勒·柯利尔

2
好答案。不过,请注意封闭并不需要成为一个自我调用功能,但它可以是。当一个封闭自我调用(即立即加入()函数后调用),此方法的返回值被立即计算,而不是函数返回和返回值进行计算,一旦函数被调用。闭包实际上可以是另一个函数中的任何函数,并且其关键特征是它可以访问父函数的范围,包括其变量和方法。
克里斯·哈克罗

69

您给出的示例就是一个很好的例子。闭包是一种抽象机制,可让您非常清晰地分离问题。您的示例是将检测(计数调用)与语义(错误报告API)分开的情况。其他用途包括:

  1. 将参数化的行为传递到算法中(经典的高阶编程):

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
  2. 模拟面向对象的编程:

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
  3. 实现奇异的流控制,例如jQuery的事件处理和AJAX API。


3
int?)最后我检查了一下,JavaScript是一种鸭子式语言。也许您在考虑Java?
Hello71

1
@ Hello71:我当时在想JavaScript,但是旧习惯很难改掉。接得好。
Marcelo Cantos 2010年

2
@MarceloCantos似乎您忘记了counter的实现中的逗号。我编辑了您的帖子以进行更正。希望没事:)
Natan Streppel 2014年

2
@Streppel:好收获!我很高兴您能改进我的代码。:-)
Marcelo Cantos 2014年

试图理解#1 ...您如何称呼接近度?
Dave2081 2014年

26

我知道我回答这个问题太迟了,但它可能会帮助仍然在2018年寻找答案的任何人。

Javascript闭包可用于实现调节反跳在您的应用程序中功能。

节流

节流限制了函数在一段时间内被调用的最大次数。如“每100毫秒最多执行一次此功能”中所述。

代码:

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

防弹跳

取消反跳对未调用某个函数的时间设置了限制,直到经过一定时间。就像“仅在经过100毫秒而不调用它时才执行此函数”中一样。

码:

const debounce = (func, delay) => {
  let debouncing
  return function() {
    const context = this
    const args = arguments
    clearTimeout(debouncing)
    debouncing = setTimeout(() => func.apply(context, args), delay)
  }
}

如您所见,闭包帮助实现了两个漂亮的功能,每个Web应用程序都必须提供这种光滑的UI体验功能。

希望对您有所帮助。


18

是的,这是有用的闭包的一个很好的例子。调用warnUser会calledCount在其范围内创建变量,并返回存储在warnForTamper变量中的匿名函数。由于仍有一个闭包正在使用invokeCount变量,因此在函数退出时不会将其删除,因此每次调用warnForTamper()都会增加作用域变量并警告该值。

我在StackOverflow上看到的最常见的问题是,有人想“延迟”使用每次循环时增加的变量,但由于该变量是作用域的,因此每次对该变量的引用都将在循环结束之后进行,从而导致变量的结束状态:

for (var i = 0; i < someVar.length; i++)
    window.setTimeout(function () { 
        alert("Value of i was "+i+" when this timer was set" )
    }, 10000);

这将导致每个警报显示相同的值i,该值增加到循环结束时的值。解决方案是创建一个新的闭包,该变量的单独作用域。这可以使用立即执行的匿名函数来完成,该函数接收变量并将其状态存储为参数:

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 

有趣的-1,我想这不是“ javascript闭包的实际用法”吗?
安迪E

1
我在阅读时发现了一些用处,因此在降级投票前给了+1。
alex

1
@alex:谢谢,我确实注意到了投票。我几乎已经习惯在这里匿名匿名投票了。这只会让我感到烦恼,因为我真的很想知道我说的是不正确还是错误,而且它们往往使您认为您只是被其他一些想要更好地了解自己答案的回答者所否定。幸运的是,我不是报复类型;-)
Andy E

1
我认为这更适合解决JavaScript的坏块范围。您应该能够添加var j = i; 在第一个setTimeout之前,并获得使用该j的警报。另一个解决方法是像这样使用'with':for(var i = 0; i <someVar.length; i ++){with({i:i}){window.setTimeout(function(){alert(“ Value of设置了此计时器后,我是“ + i +””}},100);}}
davidbuttar 2013年

1
@AndyE Funny可能不是正确的词。我只是注意到,人们经常使用自调用函数来解释闭包,就像本页上的许多答案一样。但是setTimeout中的回调函数也是一个闭包。可以将其视为“实际用途”,因为您可以从回调中访问其他一些局部变量。当我学习闭包时,意识到这对我很有用-闭包无处不在,而不仅仅是街机JavaScript模式。
antoine

14

特别是在JavaScript(或任何ECMAScript)语言中,闭包在隐藏功能实现同时仍显示接口时很有用。

例如,假设您正在编写一类日期实用程序方法,并且希望允许用户通过索引查找工作日名称,但又不希望他们能够修改在后台使用的名称数组。

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

请注意,days数组可以简单地存储为dateUtil对象的属性,但是脚本用户可以看到该数组,他们甚至可以根据需要更改它,甚至不需要源代码。但是,由于它由返回日期查找功能的匿名函数包围,因此只能由查找函数访问,因此现在可以防篡改。


2
这听起来可能很愚蠢,但是他们不能只是打开JavaScript文件本身并查看您的实现吗?
itsmichaelwang 2014年

1
@Zapurdead:是的,他们当然可以看到实现,但是如果不直接修改源代码,就不能(偶然或有意地)更改实现。我想您可以将其与Java中受保护的成员进行比较。
maerics 2014年


5

闭包的另一个常见用法是将this方法绑定到特定对象,从而允许在其他地方调用它(例如事件处理程序)。

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

每当发生mousemove事件时, watcher.follow(evt)都会调用。

闭包也是高阶函数的重要组成部分,它通过参数化不相似的部分,允许将非常相似的功能重写为单个高阶函数的多个相似函数。作为一个抽象的例子,

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

变成

fooer = function (x) {
    return function (...) {A x B}
}

其中A和B不是语法单位,而是源代码字符串(不是字符串文字)。

有关具体示例,请参见“ 使用函数简化我的JavaScript ”。


5

在这里,我要说几遍问候。如果创建一个闭包,则可以简单地调用该函数来记录问候语。如果不创建闭包,则必须每次都传递我的名字。

没有关闭(https://jsfiddle.net/lukeschlangen/pw61qrow/3/):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

使用闭包(https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/):

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();

1
我不确定,但是仍然没有关闭,您可以调用var grretBilly = greeting(“ Billy”,“ Bob”); 并调用grretBilly(); 它仍然会做同样的事情?尽管您创建闭包与否,但这不是另一个问题,但是每次传递名称都不是问题。
user2906608 '18

4

如果您对在面向对象的意义上实例化一个类的概念(即创建该类的对象)感到满意,那么您就接近理解闭包了。

这样想:在实例化两个Person对象时,您会知道类成员变量“ Name”在实例之间不共享;每个对象都有自己的“副本”。同样,在创建闭包时,free变量(在上面的示例中为“ namedCount”)绑定到函数的“ instance”。

我认为您的概念上的飞跃受到了warnUser函数返回的每个函数/闭包(除了:这是一个高阶函数)的阻碍,闭包将'callCount'绑定为相同的初始值(0),而在创建闭包时通常将不同的初始化方法传递给高阶函数更有用,就像将不同的值传递给类的构造函数一样。

因此,假设“ callCount”达到某个值时,您想结束用户的会话。您可能需要不同的值,具体取决于请求是来自本地网络还是来自糟糕的互联网(是的,这是一个人为的示例)。为了实现这一点,您可以将named的不同初始值传递给warnUser(即-3或0?)。

文献的部分问题是用于描述它们的术语(“词法范围”,“自由变量”)。不要让它愚弄你,闭包比看起来更简单...表面上看;-)


3

在这里,我有一个简单的闭包概念示例,我们可以在我们的电子商务站点或其他站点中使用它。我在示例中添加了jsfiddle链接。它包含3件小商品清单和1个购物车柜台。

Jsfiddle

//Counter clouser implemented function;
var CartCouter = function(){
	var counter = 0;
  function changeCounter(val){
  	counter += val
  }
  return {
  	increment: function(){
    	changeCounter(1);
    },
    decrement: function(){
    changeCounter(-1);
    },
    value: function(){
    return counter;
    }
  }
}

var cartCount = CartCouter();
function updateCart(){
	document.getElementById('cartcount').innerHTML = cartCount.value();
  }

var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
	productlist[i].addEventListener('click',function(){
  	if(this.className.indexOf('selected')<0){
    		this.className += " selected";
        cartCount.increment();
        updateCart();
    } else{
    	this.className = this.className.replace("selected", "");
      cartCount.decrement();
      updateCart();
    }
  })
}
.productslist{
  padding:10px;
}
ul li{
  display: inline-block;
  padding: 5px;
  border: 1px solid #ddd;
  text-align: center;
  width: 25%;
  cursor: pointer;
}
.selected{
  background-color: #7CFEF0;
  color: #333;
}
.cartdiv{
  position: relative;
  float:right;
  padding: 5px;
  box-sizing: border-box;
  border: 1px solid #f1f1f1;
}
<div>
<h3>
Practical Use of JavaScript Closure consept/private variable.
</h3>
<div class="cartdiv">
    <span id="cartcount">0</span>
</div>
<div class="productslist">
    <ul >
    <li class="item">Product 1</li>
     <li class="item">Product 2</li>
     <li class="item">Product 3</li>
    </ul>

</div>
</div>


2

封闭的使用:

闭包是JavaScript的最强大功能之一。JavaScript允许嵌套函数,并向内部函数授予对外部函数内部定义的所有变量和函数(以及外部函数可以访问的所有其他变量和函数)的完全访问权限。但是,外部函数无法访问内部函数内部定义的变量和函数。这为内部函数的变量提供了某种安全性。同样,由于内部函数可以访问外部函数的范围,因此,如果内部函数设法在外部函数的生存期内生存,则外部函数中定义的变量和函数的生存期将比外部函数本身更长。

范例:

<script>
var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

在上面的代码中,内部函数可以访问外部函数的name变量,除了通过内部函数之外,没有其他方法可以访问内部变量。内部函数的内部变量充当内部函数的安全存储。它们保存“持久”但安全的数据以供内部功能使用。这些功能甚至不必分配给变量或具有名称。在这里阅读详细信息


2

我喜欢Mozilla的function factory 示例

function makeAdder(x) {

    return function(y) {
        return x + y;
    };
}

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);

11
我认为,这种类型的示例无法帮助人们理解闭包或它们的用途。除示例外,您曾经写过几次闭包来返回要加数字的函数?
Mohamad

2

JavaScript模块模式使用闭包。它的漂亮模式使您可以拥有“公共”和“私有”变量。

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();


1

该线程极大地帮助我更好地了解了闭包的工作原理。此后,我进行了一些实验,得出了这个非常简单的代码,它可以帮助其他人了解如何以实际方式使用闭包,以及如何在不同级别使用闭包来维护类似于static和/或全局变量,而不会有被全局变量覆盖或混淆的风险。这是在每个局部按钮的局部级别和全局级别跟踪按钮单击的情况,计算每次按钮单击的次数,从而形成单个图形。注意,我没有使用任何全局变量来执行此操作,这是练习的重点-具有可以应用于任何也对全局有所贡献的按钮的处理程序。

请专家,如果我有任何不良做法,请告诉我!我自己还在学习这些东西。

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">

window.addEventListener("load" , function () {
    /*
    grab the function from the first closure,
    and assign to a temporary variable 
    this will set the totalButtonCount variable
    that is used to count the total of all button clicks

    */
    var buttonHandler = buttonsCount(); 

    /*
    using the result from the first closure (a function is returned) 
    assign and run the sub closure that carries the 
    individual variable for button count and assign to the click handlers 
    */
    document.getElementById("button1").addEventListener("click" , buttonHandler() );
    document.getElementById("button2").addEventListener("click" , buttonHandler() );
    document.getElementById("button3").addEventListener("click" , buttonHandler() );

    // Now that buttonHandler has served its purpose it can be deleted if needs be
    buttonHandler = null;
});



function buttonsCount() {
    /* 
        First closure level 
        - totalButtonCount acts as a sort of global counter to count any button presses
    */
    var totalButtonCount = 0;

    return  function () {
        //second closure level
        var myButtonCount = 0;

        return function (event) {
            //actual function that is called on the button click
            event.preventDefault();
            /*  
               increment the button counts.
               myButtonCount only exists in the scope that is 
               applied to each event handler, therefore acts 
               to count each button individually whereas because 
               of the first closure totalButtonCount exists at 
               the scope just outside, so maintains a sort 
               of static or global variable state 
            */

            totalButtonCount++;
            myButtonCount++;

            /* 
                do something with the values ... fairly pointless 
                but it shows that each button contributes to both 
                it's own variable and the outer variable in the 
                first closure 
            */
            console.log("Total button clicks: "+totalButtonCount);
            console.log("This button count: "+myButtonCount);
        }
    }
}

</script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>
</html>

0

参考:闭包的实际用法

在实践中,闭包可以创建优雅的设计,允许自定义各种计算,延迟调用,回调,创建封装范围等。

数组的sort方法的一个示例,它接受sort-condition函数作为参数:

[1, 2, 3].sort(function (a, b) {
    ... // sort conditions
});

映射函数作为数组的映射方法,该函数根据功能参数的条件映射新数组:

[1, 2, 3].map(function (element) {
   return element * 2;
}); // [2, 4, 6]

通常,使用定义几乎无限搜索条件的功能参数来实现搜索功能很方便:

 someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });

另外,我们可能会注意到将函数用作例如forEach方法,该方法将函数应用于元素数组:

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

一个函数将应用于参数(在应用中应用于参数列表,在调用中应用于定位参数):

(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

延迟通话:

var a = 10;
    setTimeout(function () {
      alert(a); // 10, after one second
    }, 1000);

回调函数:

var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // callback, which will be called deferral ,
  // when data will be ready;
  // variable "x" here is available,
  // regardless that context in which,
  // it was created already finished
  alert(x); // 10
};

创建封装作用域以隐藏辅助对象:

var foo = {};
(function (object) {
  var x = 10;
  object.getX = function _getX() {
    return x;
  };
})(foo);
alert(foo.getX());// get closured "x" – 10

0

我们在前端JavaScript中编写的许多代码都是基于事件的-我们定义一些行为,然后将其附加到用户触发的事件(例如单击或按键)上。我们的代码通常作为回调附加:一个单独的函数,可响应事件执行。现在,size12,size14和size16是可以将正文文本的大小分别调整为12、14和16像素的函数。我们可以将它们附加到按钮上(在本例中为链接),如下所示:

function makeSizer(size) {
    return function() {
    document.body.style.fontSize = size + 'px';
    };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

小提琴


尽管此代码可以回答问题,但提供有关如何和/或为什么解决问题的其他上下文将提高​​答案的长期价值。
唐老鸭

1
在我看来,该示例可以通过标准函数在不关闭的情况下实现。我试图发现不能没有闭合来实现的东西一个例子
扎克·史密斯

0

闭包是创建的有用方法 ,按需增加序列:

    var foobar = function(i){var count = count || i; return function(){return ++count;}}

    baz = foobar(1);
    console.log("first call: " + baz()); //2
    console.log("second call: " + baz()); //3

差异总结如下:

匿名函数定义函数

不能用作方法可以用作对象的方法

仅存在于定义的范围内存在于定义的对象内

只能在定义的范围内调用可以在代码中的任何位置调用

可以重新分配新值或删除不能删除或更改

参考文献


0

在给定的样本中,封闭变量“ counter”的值受到保护,并且只能使用给定的函数(递增,递减)进行更改。因为它是封闭的,

var MyCounter= function (){
    var counter=0;
    return {
    	increment:function () {return counter += 1;},
        decrement:function () {return counter -= 1;},
        get:function () {return counter;}
    };
};

var x = MyCounter();
//or
var y = MyCounter();

alert(x.get());//0
alert(x.increment());//1
alert(x.increment());//2

alert(y.increment());//1
alert(x.get());// x is still 2


0

解释JavaScript闭包的实际用途-

当我们在另一个函数中创建一个函数时,我们正在创建一个闭包。闭包很强大,因为它们能够读取和操作其外部功能的数据。每当调用函数时,都会为该调用创建一个新的作用域。在函数内部声明的局部变量属于该范围,并且只能从该函数访问它们。函数完成执行后,通常会破坏作用域。

此类功能的一个简单示例是:

function buildName(name) { 
    const greeting = "Hello, " + name; 
    return greeting;
}

在上面的示例中,函数buildName()声明一个局部变量greeting并将其返回。每个函数调用都会使用新的局部变量创建一个新的作用域。函数执行完后,我们无法再次引用该作用域,因此将其垃圾回收。

但是,当我们有一个指向该范围的链接时呢?

让我们看下一个函数:

function buildName(name) { 
    const greeting = "Hello, " + name + " Welcome "; 
    const sayName = function() {
        console.log(greeting); 
    };
    return sayName; 
}

const sayMyName = buildName("Mandeep");
sayMyName();  // Hello, Mandeep Welcome

此示例中的函数sayName()是一个闭包。sayName()函数具有自己的局部作用域(带有变量welcome),并且还可以访问外部(封闭的)函数的作用域。在这种情况下,来自buildName()的变量greeting。

在执行buildName之后,在这种情况下不会破坏作用域。sayMyName()函数仍然可以访问它,因此不会被垃圾回收。但是,除了闭包之外,没有其他方法可以从外部范围访问数据。闭包充当全局上下文和外部范围之间的门户。


0

我正在尝试学习语言,我认为我创建的示例是实际的用例。您可以运行代码段并在控制台中查看结果。我们有两个分别拥有不同数据的用户。他们每个人都可以看到实际状态并进行更新。

function createUserWarningData(user) {
  const data = {
    name: user,
    numberOfWarnings: 0,
  };

  function addWarning() {
    data.numberOfWarnings = data.numberOfWarnings + 1;
  }

  function getUserData() {
    console.log(data);
    return data;
  }

  return {
    getUserData: getUserData,
    addWarning: addWarning,
  };
}

const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");

//USER 1
user1.getUserData(); // returning data user object
user1.addWarning(); // add one warning to specific user
user1.getUserData(); // returning data user object

//USER2
user2.getUserData(); // returning data user object
user2.addWarning(); // add one warning to specific user
user2.addWarning(); // add one warning to specific user
user2.getUserData(); // returning data user object

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.