javascript闭包_JavaScript闭包教程–带有JS闭包示例代码

javascript闭包

Closures – many of you JavaScript devs have probably heard this term before. When I started my journey with JavaScript, I encountered closures often. And I think they're one of the most important and interesting concepts in JavaScript.

闭包–您中的许多JavaScript开发人员以前可能都听说过这个词。 当我开始使用JavaScript时,经常会遇到关闭。 而且我认为它们是JavaScript中最重要和最有趣的概念之一。

You don't think they're interesting? This often happens when you don’t understand a concept – you don’t find it interesting. (I don’t know if this happens to you or not, but this is the case with me).

你不觉得他们有趣吗? 当您不了解某个概念时,通常会发生这种情况–您不觉得它很有趣。 (我不知道这是否发生在您身上,但我就是这种情况)。

So in this article, I will try to make closures interesting for you.

因此,在本文中,我将尝试使闭包变得有趣。

Before going into the world of closures, let’s first understand lexical scoping. If you already know about it, skip the next part. Otherwise jump into it to better understand closures.

在进入闭包世界之前,让我们首先了解词法作用域 。 如果您已经知道它,请跳过下一部分。 否则,请跳入其中以更好地了解闭包。

词汇范围 (Lexical Scoping )

You may be thinking – I know local and global scope, but what the heck is lexical scope? I reacted the same way when I heard this term. Not to worry! Let’s take a closer look.

您可能在想–我知道本地和全球范围,但是词汇范围到底是什么呢? 听到这个学期时,我的React是一样的。 不要担心! 让我们仔细看看。

It’s simple like other two scopes:

就像其他两个作用域一样简单:

function greetCustomer() {
    var customerName = "anchal";
    function greetingMsg() {
	  console.log("Hi! " + customerName); // Hi! anchal
    }
   greetingMsg();
}

You can see from the above output that the inner function can access the outer function's variable. This is lexical scoping, where the scope and value of a variable is determined by where it is defined/created (that is, its position in the code). Got it?

从上面的输出中可以看到,内部函数可以访问外部函数的变量。 这是词法作用域,其中变量的范围和值由定义/创建的位置(即其在代码中的位置)确定。 得到它了?

I know that last bit might have confused you. So let me take you deeper. Did you know that lexical scoping is also known as static scoping? Yes, that's its other name.

我知道最后一点可能会让您感到困惑。 因此,让我更深入地介绍您。 您是否知道词法作用域也称为静态作用域 ? 是的,这是它的别称。

There is also dynamic scoping, which some programming languages support. Why have I mentioned dynamic scoping? Because it can help you better understand lexical scoping.

还有动态范围 ,某些编程语言支持。 为什么提到动态范围? 因为它可以帮助您更好地理解词法作用域。

Let’s look at some examples:

让我们看一些例子:

function greetingMsg() {
  console.log(customerName);// ReferenceError: customerName is not defined
}

function greetCustomer() {
   var customerName = "anchal";
   greetingMsg();
}

greetCustomer();

Do you agree with the output? Yes, it will give a reference error. This is because both functions don’t have access to each other’s scope, as they are defined separately.

您是否同意输出? 是的,它将给出参考错误。 这是因为这两个函数无法访问彼此的范围,因为它们是分别定义的。

Let’s look at another example:

让我们看另一个例子:

function addNumbers(number1) {
  console.log(number1 + number2);
}

function addNumbersGenerate() {
  var number2 = 10;
  addNumbers(number2);
}

addNumbersGenerate();

The above output will be 20 for a dynamically scoped language. Languages that support lexical scoping will give referenceError: number2 is not defined. Why?

对于动态范围的语言,以上输出将为20。 支持词法作用域的语言会给 referenceError: number2 is not defined 。 为什么?

Because in dynamic scoping, searching takes place in the local function first, then it goes into the function that called that local function. Then it searches in the function that called that function, and so on, up the call stack.

因为在动态作用域中,搜索首先在本地函数中进行,然后才进入调用该本地函数的函数中。 然后,它在调用函数的函数中进行搜索,依此类推,直到调用堆栈。

Its name is self explanatory – “dynamic” means change. The scope and value of variable can be different as it depends on from where the function is called. The meaning of a variable can change at runtime.

它的名字不言自明–“动态”意味着变化。 变量的范围和值可以不同,这取决于调用函数的位置。 变量的含义可以在运行时更改。

Got the gist of dynamic scoping? If yes, then just remember that lexical scoping is its opposite.

有动态范围的要点吗? 如果是,那么请记住词汇范围是相反的。

In lexical scoping, searching takes place in the local function first, then it goes into the function inside which that function is defined. Then it searches in the function inside which that function is defined and so on.

在词法作用域中,搜索首先在局部函数中进行,然后进入定义函数的函数中。 然后,它在定义函数的函数中进行搜索,依此类推。

So, lexical or static scoping means the scope and value of a variable is determined from where it is defined. It doesn’t change.

因此, 词汇静态作用域意味着从定义变量的位置确定变量的范围和值。 它没有改变。

Let’s again look at the above example and try to figure out the output on your own. Just one twist – declare number2 at the top:

让我们再次看一下上面的示例,尝试自己找出输出。 只需一转-在顶部声明数字number2

var number2 = 2;
function addNumbers(number1) {
  console.log(number1 + number2);
}

function addNumbersGenerate() {
  var number2 = 10;
  addNumbers(number2);
}

addNumbersGenerate();

Do you know what the output will be?

您知道输出是什么吗?

Correct – it’s 12 for lexically scoped languages. This is because first, it looks into an addNumbers function (innermost scope) then it searches inwards, where this function is defined. As it gets the number2 variable, meaning the output is 12.

正确–词汇范围语言为12。 这是因为,首先,它查看一个addNumbers函数(最内部的作用域),然后向内搜索,在其中定义了此函数。 当它获得number2变量时,意味着输出为12。

You may be wondering why I have spent so much time on lexical scoping here. This is a closure article, not one about lexical scoping. But if you don’t know about lexical scoping then you will not understand closures.

您可能想知道为什么我在这里花了这么多时间进行词汇界定。 这是一篇闭篇文章,而不是有关词法作用域的文章。 但是,如果您不了解词法作用域,那么您将不会理解闭包。

Why? You will get your answer when we look at the definition of a closure. So let’s get into the track and get back to closures.

为什么? 当我们查看闭包的定义时,您会得到答案。 因此,让我们进入正轨,回到闭包。

什么是闭包? (What is a Closure? )

Let’s look at the definition of a closure:

让我们看一下闭包的定义:

Closure is created when an inner function has access to its outer function variables and arguments. The inner function has access to –

当内部函数可以访问其外部函数的变量和参数时,将创建闭包。 内部功能可以访问–

1. Its own variables.

1.自己的变量。

2. Outer function's variables and arguments.

2.外部函数的变量和参数。

Wait! Is this the definition of a closure or lexical scoping? Both definitions look the same. How they are different?

等待! 这是闭包或词汇作用域的定义吗? 两种定义看起来相同。 它们有何不同?

Well, that's why I defined lexical scoping above. Because closures are related to lexical/static scoping.

好吧,这就是为什么我在上面定义词法作用域的原因。 因为闭包与词汇/静态作用域有关。

Let’s again look at its other definition that will tell you how closures are different.

让我们再次查看其另一个定义,该定义将告诉您闭包有何不同。

Closure is when a function is able to access its lexical scope, even when that function is executing outside its lexical scope.
闭包是指某个函数能够访问其词法范围,即使该函数正在其词法范围之外执行。

Or,

要么,

Inner functions can access its parent scope, even after the parent function is already executed.
内部函数可以访问其父范围,即使已经执行了父函数也是如此。

Confused? Don't worry if you haven't yet gotten the point. I have examples to help you better understand. Let’s modify the first example of lexical scoping:

困惑? 如果您还没有明白这一点,请不要担心。 我有一些例子可以帮助您更好地理解。 让我们修改词法作用域的第一个示例:

function greetCustomer() {
  const customerName = "anchal";
  function greetingMsg() {
    console.log("Hi! " + customerName);
  }
  return greetingMsg;
}

const callGreetCustomer = greetCustomer();
callGreetCustomer(); // output – Hi! anchal

The difference in this code is that we are returning the inner function and executing it later. In some programming languages, the local variable exists during the function’s execution. But once the function is executed, those local variables don’t exist and they will not be accessible.

这段代码的区别在于我们要返回内部函数并在以后执行它。 在某些编程语言中,局部变量在函数执行期间存在。 但是一旦函数执行完毕,这些局部变量将不存在,并且将无法访问。

Here, however, the scene is different. After the parent function is executed, the inner function (returned function) can still access the parent function's variables. Yes, you guessed right. Closures are the reason.

但是,这里的场景有所不同。 执行父函数后,内部函数(返回的函数)仍可以访问父函数的变量。 是的,你猜对了。 停业是原因。

The inner function preserves its lexical scope when the parent function is executing and hence, later that inner function can access those variables.

内部函数在执行父函数时会保留其词法范围,因此以后内部函数可以访问这些变量。

To get a better feel for it, let’s use the dir() method of the console to look into the list of the properties of callGreetCustomer:

为了更好地了解它,让我们使用控制台的dir()方法查看callGreetCustomer属性的callGreetCustomer

console.dir(callGreetCustomer);

From the above image, you can see how the inner function preserves its parent scope (customerName) when greetCustomer() is executed. And later on, it used customerName when callGreetCustomer() was executed.

从上面的图像中,您可以看到执行greetCustomer()时内部函数如何保留其父范围( customerName )。 之后,当执行callGreetCustomer()时,它使用了customerName

I hope this example helped you better understand the above definition of a closure. And maybe now you find closures a bit more fun.

我希望这个例子可以帮助您更好地理解闭包的上述定义。 也许现在您发现关闭更有趣了。

So what next? Let’s make this topic more interesting by looking at different examples.

那接下来呢? 通过查看不同的示例,使这个话题变得更加有趣。

实施中的关闭示例 (Examples of closures in action)

function counter() {
  let count = 0;
  return function() {
    return count++;
  };
}

const countValue = counter();
countValue(); // 0
countValue(); // 1
countValue(); // 2

Every time you call countValue, the count variable value is incremented by 1. Wait – did you think that the value of count is 0?

每次调用countValue ,count变量的值都会增加1。等待–您是否认为count的值为0?

Well, that would be wrong as a closure doesn’t work with a value. It stores the reference of the variable. That’s why, when we update the value, it reflects in the second or third call and so on as the closure stores the reference.

嗯,这是错误的,因为闭包不能与值一起使用。 它存储变量的引用 。 这就是为什么当我们更新值时,它会在第二个或第三个调用中反映出来,依此类推,因为闭包存储了引用。

Feeling a bit clearer now? Let’s look at another example:

现在感觉更清晰了吗? 让我们看另一个例子:

function counter() {
  let count = 0;
  return function () {
    return count++;
  };
}

const countValue1 = counter();
const countValue2 = counter();
countValue1();  // 0
countValue1();  // 1
countValue2();   // 0
countValue2();   // 1

I hope you guessed the right answer. If not, here is the reason. As countValue1 and countValue2, both preserve their own lexical scope. They have independent lexical environments. You can use dir() to check the [[scopes]] value in both the cases.

希望您猜对了答案。 如果没有,这就是原因。 与countValue1countValue2 ,它们都保留了自己的词汇范围。 他们有独立的词汇环境。 在这两种情况下,都可以使用dir()检查[[scopes]]值。

Let’s look at a third example.

让我们看第三个例子。

This one's a bit different. In it, we have to write a function to achieve the output:

这有点不同。 在其中,我们必须编写一个函数来实现输出:

const addNumberCall = addNumber(7);
addNumberCall(8) // 15
addNumberCall(6) // 13

Simple. Use your newly-gained closure knowledge:

简单。 使用您新获得的闭包知识:

function addNumber(number1) {
  return function (number2) {
    return number1 + number2;
  };
}

Now let’s look at some tricky examples:

现在让我们看一些棘手的例子:

function countTheNumber() {
  var arrToStore = [];
  for (var x = 0; x < 9; x++) {
    arrToStore[x] = function () {
      return x;
    };
  }
  return arrToStore;
}

const callInnerFunctions = countTheNumber();
callInnerFunctions[0]() // 9
callInnerFunctions[1]() // 9

Every array element that stores a function will give you an output of 9. Did you guess right? I hope so, but still let me tell you the reason. This is because of the closure's behavior.

每个存储函数的数组元素都将为您提供9的输出。您猜对了吗? 希望如此,但还是让我告诉您原因。 这是由于闭包的行为。

The closure stores the reference, not the value. The first time the loop runs, the value of x is 0. Then the second time x is 1, and so on. Because the closure stores the reference, every time the loop runs it's changing the value of x. And at last, the value of x will be 9. So callInnerFunctions[0]() gives an output of 9.

闭包存储引用 ,而不是值。 循环第一次运行时,x的值为0。然后第二次循环x为1,依此类推。 因为闭包存储引用,所以每次循环运行时,它都会更改x的值。 最后,x的值为9。因此callInnerFunctions[0]()的输出为9。

But what if you want an output of 0 to 8? Simple! Use a closure.

但是,如果您想要0到8的输出怎么办? 简单! 使用闭包。

Think about it before looking at the solution below:

在查看以下解决方案之前,请考虑一下:

function callTheNumber() {
  function getAllNumbers(number) {
    return function() {
      return number;
    };
  }
  var arrToStore = [];
  for (var x = 0; x < 9; x++) {
    arrToStore[x] = getAllNumbers(x);
  }
  return arrToStore;
}

const callInnerFunctions = callTheNumber();
console.log(callInnerFunctions[0]()); // 0
console.log(callInnerFunctions[1]()); // 1

Here, we have created separate scope for each iteration. You can use console.dir(arrToStore) to check the value of x in [[scopes]] for different array elements.

在这里,我们为每次迭代创建了单独的作用域。 您可以使用console.dir(arrToStore)检查[[scopes]]中x的值以获取不同的数组元素。

That’s it! I hope you can now say that you find closures interesting.

而已! 我希望您现在可以说您发现闭包很有趣。

To read my other articles, check out my profile here.

要阅读其他文章,请在此处查看我的个人资料。

翻译自: https://www.freecodecamp.org/news/javascript-closure-tutorial-with-js-closure-example-code/

javascript闭包

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值