javascript闭包_什么是JavaScript闭包? 请用简单的英语。

javascript闭包

by Samer Buna

通过Samer Buna

什么是JavaScript闭包? 请用简单的英语。 (What’s a JavaScript closure? In plain English, please.)

Every function in JavaScript has a closure. And this is one of the coolest features of the JavaScript language. Because without closures, it would be hard to implement common structures like callbacks or event handlers.

JavaScript中的每个函数都有一个闭包。 这是JavaScript语言最酷的功能之一。 因为没有闭包,将很难实现诸如回调或事件处理程序之类的通用结构。

You create a closure whenever you define a function. Then when you execute functions, their closures enable them to access data in their scopes.

每当您定义函数时,就创建一个闭包。 然后,当您执行函数时,它们的闭包使它们能够访问其作用域中的数据。

It’s kind of like when a car is manufactured (defined) it comes with a few functions like start, accelerate, decelerate. These car functions get executed by the driver every time they operate the car. Closures for these functions come defined with the car itself and they close over variables they need to operate.

就像汽车制造(定义)时一样,它具有一些功能,例如startacceleratedecelerate 。 驾驶员在每次操作汽车时都会执行这些汽车功能。 这些功能的关闭由汽车本身定义,它们关闭了需要操作的变量。

Let’s narrow this analogy to the accelerate function. The function definition happens when the car is manufactured:

让我们将此类比喻简化为accelerate功能。 功能定义在汽车制造时发生:

function accelerate(force) {
  // Is the car started?
  // Do we have fuel?
  // Are we in traction control mode?
  // Many other checks...
  // If all good, burn more fuel depending on 
  // the force variable (how hard we’re pressing the gas pedal)
}

Every time the driver presses the gas pedal, this functions gets executed. Note how this function needs access to a lot of variable to operate, including its own force variable. But more importantly, it needs variables outside of its scope that are controlled by other car functions. This is where the closure of the accelerate function (which we get with the car itself) comes in handy.

驾驶员每次踩油门踏板都会执行此功能。 请注意,此函数如何需要访问很多变量才能运行,包括其自身的force变量。 但更重要的是,它需要超出其范围的变量,这些变量由其他汽车功能控制。 在这里, accelerate功能(我们通过汽车本身获得)的关闭非常方便。

Here’s what the accelerate function’s closure promised to the accelerate function itself:

这是accelerate功能关闭对accelerate功能本身的承诺:

Ok accelerate, when you get executed, you can access your force variable, you can access the isCarStarted variable, you can also access the fuelLevel variable, and the isTractionControlOn variable. You can also control the currentFuelSupply variable that we’re sending to the engine.

accelerate ,执行后,可以访问force变量,可以访问isCarStarted变量,还可以访问fuelLevel变量和isTractionControlOn变量。 您还可以控制我们要发送给引擎的currentFuelSupply变量。

Note that the closure did not give the accelerate function fixed values for these variables, but rather permission to access those values at the time the accelerate function is executed.

请注意,闭包没有为这些变量提供accelerate功能固定值,而是在执行加速功能时允许访问这些值。

Closures are closely related to function scopes, so understanding how these scopes work will help you understanding closures. In short, the most important thing to understand about scopes is that when you execute a function, a private function scope is created and used for the process of executing that function.

闭包与函数作用域密切相关,因此了解这些作用域如何工作将有助于您理解闭包。 简而言之,要了解作用域,最重要的事情是,当您执行一个函数时,会创建一个私有函数作用域并将其用于执行该函数的过程。

Then these function scopes get nested when you execute functions from within functions (which you’ll do all the time).

然后,当您从函数内部执行函数时,这些函数作用域就会嵌套(您将一直这样做)。

A closure is created when you define a function — not when you execute it. Then, every time you execute that function, its already-defined closure gives it access to all the function scopes available around it.

闭包是在定义函数时创建的,而不是在执行时创建的。 然后,每次执行该函数时,其已定义的闭包便使其可以访问围绕该函数可用的所有函数范围。

In a way, you can think of scopes as temporary (the global scope is the only exception to this), while you can think of closures themselves as permanent.

在某种程度上,您可以将范围视为临时的(全局范围是唯一的例外),而您可以将闭包本身视为永久的。

To truly understand closures and the role they play in JavaScript, you first need to understand a few other simple concepts about JavaScript functions and their scopes.

为了真正理解闭包及其在JavaScript中的作用,您首先需要了解有关JavaScript函数及其作用域的其他一些简单概念。

Before we get started, note that I’ve also created an interactive lab for this, which you can work through here.

在开始之前,请注意,我还为此创建了一个交互式实验室,您可以在此处进行操作

1-通过值引用分配功能 (1 — Functions are assigned by value reference)

When you put a function in a variable like this:

当您将函数放在这样的变量中时:

function sayHello() {
  console.log("hello");
};
var func = sayHello;

You are assigning the variable func a reference to the function sayHello, not a copy. Here, func is simply an alias to sayHello. Anything you do on the alias you will actually be doing on the original function. For example:

您正在为变量func分配对函数sayHello的引用,而不是副本。 在这里, func只是sayHello的别名。 您对别名所做的任何事情实际上都将在原始函数上进行。 例如:

func.answer = 42;
console.log(sayHello.answer); // prints 42

The property answer was set directly on func and read using sayHello, which works.

属性answer直接在func上设置 并使用有效的sayHello阅读。

You can also execute sayHello by executing the func alias:

您还可以通过执行func别名来执行sayHello

func() // prints "hello"

2-范围有生命 (2 — Scopes have a lifetime)

When you call a function, you create a scope during the execution of that function. Then that scope goes away.

调用函数时,将在该函数执行期间创建作用域。 然后那个范围消失了。

When you call the function a second time, you create a new different scope during the second execution. Then this second scope goes away as well.

当您第二次调用该函数时,您将在第二次执行期间创建一个新的不同范围。 然后,第二个范围也消失了。

function printA() {
  console.log(answer);
  var answer = 1;
};
printA(); // this creates a scope which gets discarded right after
printA(); // this creates a new different scope which also gets discarded right after;

These two scopes that were created in the example above are different. The variable answer here is not shared between them at all.

在上面的示例中创建的这两个范围是不同的。 他们之间根本没有共享这里的可变answer

Every function scope has a lifetime. They get created and they get discarded right away. The only exception to this fact is the global scope, which does not go away as long as the application is running.

每个功能范围都有生命周期。 他们被创造了并且被立即丢弃了。 唯一的例外是全局范围,只要应用程序正在运行,它就不会消失。

3-封闭涵盖多个范围 (3 — Closures span multiple scopes)

定义函数时,将创建一个闭包 (When you define a function, a closure gets created)

Unlike scopes, closures are created when you define a function, not when you execute it. Closures also don’t go away after you execute that function.

与作用域不同,闭包是在定义函数时创建的,而不是在执行函数时创建的。 执行该函数后,闭包也不会消失。

You can access the data in a closure long after a function is defined and after it gets executed as well.

您可以在定义函数以及执行完函数后很长时间访问闭包中的数据。

A closures encompasses everything the defined function can access. This means the defined function’s scope, and all the nested scopes between the global scope and the defined function scope plus the global scope itself.

闭包包含已定义函数可以访问的所有内容。 这意味着已定义函数的作用域,以及全局作用域和已定义函数作用域之间的所有嵌套作用域以及全局作用域本身。

var G = 'G';
// Define a function and create a closure
function functionA() {
  var A = 'A'
  
  // Define a function and create a closure
  function functionB() {
    var B = 'B'
    console.log(A, B, G);
  }
  
  functionB();  // prints A, B, G
  // functionB closure does not get discarded
  A = 42;
  functionB();  // prints 42, B, G
}
functionA();

When we define functionB here, its created closure will allow us to access the scope of functionB plus the scope of functionA plus the global scope.

当我们在此处定义functionB ,其创建的闭包将使我们能够访问functionB的范围以及functionA的范围以及全局范围。

Every time we execute functionB, we can access variables B, A, and G through its previously created closure. However, that closure does not give us a copy of these variables but rather a reference to them. So if, for example, the value of the variable A gets changed at some point after the closure of functionB is created, when we execute functionB after that, we’ll see the new value, not the old one. The second call to functionB prints 42, B, G because the value of variable A was changed to 42 and the closure gave us a reference to A, not a copy.

每次执行functionB ,我们都可以访问变量BAG 通过其先前创建的关闭。 但是,这种关闭并不能为我们提供这些变量的副本,而只是提供了对它们的引用。 因此,例如,如果变量A的值在创建functionB的关闭后的某个时刻发生更改,那么在此之后执行functionB时,我们将看到新值,而不是旧值。 第二次调用functionB打印42, B, G 因为变量A的值更改为42,并且闭包为我们提供了对A的引用,而不是副本。

不要将闭包与范围混淆 (Don’t confuse closures with scopes)

It’s common for closures to be confused with scopes, so let’s make sure not to do that.

闭包与作用域混淆是很常见的,因此请确保不要这样做。

// scope: global
var a = 1;
void function one() {
  // scope: one
  // closure: [one, global]
  var b = 2;
  
  void function two() {
    // scope: two
    // closure: [two, one, global]
    var c = 3;
    
    void function three() {
      // scope: three
      // closure: [three, two, one, global]
      var d = 4;
      console.log(a + b + c + d); // prints 10
    }();
  }();  
}();

In the simple example above, we have three functions and they all get defined and immediately invoked, so they all create scopes and closures.

在上面的简单示例中,我们有三个函数,并且它们都被定义并立即被调用,因此它们都创建了作用域和闭包。

The scope of function one() is its body. Its closure gives us access to both its scope and the global scope.

函数one()的范围是它的主体。 它的关闭使我们可以访问其范围和全局范围。

The scope of function two() is its body. Its closure gives us access to its scope plus the scope of function one()plus the global scope

函数two()的范围是它的主体。 它的关闭使我们可以访问其范围以及函数one()的范围以及全局范围

And similarly, the closure of function three() gives us access to all scopes in the example. This is why we were able to access all variables in function three().

同样,函数three()的关闭使我们可以访问示例中的所有作用域。 这就是为什么我们能够访问函数three()所有变量的原因。

But the relation between scopes and closures is not always simple like this. Things become different when the defining and invoking of functions happen in different scopes. Let me explain that with an example:

但是作用域和闭包之间的关系并不总是像这样简单。 当函数的定义和调用发生在不同的范围内时,事情就会变得不同。 让我用一个例子解释一下:

var v = 1;
var f1 = function () {
  console.log(v);
}
var f2 = function() {
  var v = 2;
  f1(); // Will this print 1 or 2?
};
f2();

What do you think the above example will print? The code is simple, f1() prints the value of v, which is 1 on the global scope, but we execute f1() inside of f2(), which has a different v that’s equal to 2. Then we execute f2().

您如何看待上面的示例? 代码很简单, f1()在全局范围内输出v的值v ,但我们在f1()内部执行f1() ,而f2() v等于2。然后执行f2()

Will this code print 1 or 2?

此代码将打印1还是2?

If you’re tempted to say 2, you’ll be surprised. This code will actually print 1. The reason is, scopes and closures are different. The console.log line will use the closure of f1(), which is created when we define f1(), which means the closure of f1() gives us access to only the scope of f1() plus the global scope. The scope where we execute f1() does not affect that closure. In fact, the closure of f1() will not give us access to the scope of f2() at all. If you remove the global v variable and execute this code, you’ll get a reference error:

如果您想说2,您会感到惊讶。 该代码实际上将打印1。原因是,作用域和闭包是不同的。 console.log行将使用f1()的闭包,该闭包是在定义f1()时创建的,这意味着f1()的闭包使我们只能访问f1()的范围以及全局范围。 我们执行f1()的范围不会影响该关闭。 实际上,关闭f1()根本无法使我们访问f2()的范围。 如果删除全局v变量并执行此代码,则会得到参考错误:

var f1 = function () {
  console.log(v);
}
var f2 = function() {
  var v = 2;
  f1(); // ReferenceError: v is not defined
};
f2();

This is very important to understand and remember.

了解和记住这一点非常重要。

4-闭包具有读写访问权限 (4 — Closures have read and write access)

Since closures give us references to variables in scopes, the access that they give us means both read and write, not just read.

由于闭包为我们提供了对范围中变量的引用,因此闭包给我们的访问权限意味着读取和写入,而不仅仅是读取。

Take a look at this example:

看一下这个例子:

function outer() {
  let a = 42;
function inner() {
    a = 43;
  }
inner();
  console.log(a);
}
outer();

The inner() function here, when defined, creates a closure that gives us access to the variable a. We can read and modify that variable, and if we do modify it, we will be modifying the actual a variable in the outer() scope.

当定义了inner()函数时,它会创建一个闭包,使我们可以访问变量a 。 我们可以读取和修改该变量,如果要进行修改,则将在outer()范围内修改实际a变量。

This code will print 43 because we used the inner() function closure to modify the outer() function variable.

这段代码将显示43,因为我们使用inner()函数闭包修改了outer()函数变量。

This is actually why we can change global variables everywhere. All closures give us both read and write access to all global variables.

这就是为什么我们可以在任何地方更改全局变量的原因。 所有闭包均使我们对所有全局变量都具有读取和写入访问权限。

5-闭包可以共享作用域 (5 — Closures can share scopes)

Since closures give us access to nested scopes at the time we define functions, when we define multiple functions in the same scope, that scope is shared among all created closures, and of course, because of this, the global scope is always shared among all closures.

由于闭包使我们能够在定义函数时访问嵌套范围,因此当我们在同一范围内定义多个函数时,该范围在所有创建的闭包之间共享,当然,因此,全局范围始终在所有范围内共享关闭。

function parent() {
  let a = 10;
  
  function double() {
    a = a+a;
   console.log(a);
  };
  
  function square() {
    a = a*a;
   console.log(a);
  }
  
  return { double, square }
}
let { double, square } = parent();
double(); // prints 20
square(); // prints 400
double(); // prints 800

In the example above, we have a parent() function with variable a set to 10. We define two functions in this parent() function’s scope, double() and square(). The closures created for double() and square() both share the scope of the parent() function. Since both double() and square() change the value of a, when we execute the last 3 lines, we double a (making a = 20), then square that doubled value (making a = 400), then double that squared value (making a = 800).

在上面的示例中,我们有一个parent()函数,其变量a设置为10。我们在此parent()函数的范围内定义了两个函数double()square() 。 为double()square()创建的闭包都共享parent() 函数的范围。 由于这两种double()square()改变的值a ,当我们执行的最后3行,我们双a (使a = 20),则平方该倍值(使a = 400),那么两倍平方值(使a = 800)。

一项最终测试 (One final test)

Let’s now check your understanding of closures so far. Before you execute the following code, try to guess what it will print:

现在,让我们检查一下到目前为止对闭包的理解。 在执行以下代码之前,请尝试猜测它将显示什么:

let a = 1;
const function1 = function() {
  console.log(a);
  a = 2
}
a = 3;
const function2 = function() {
  console.log(a);
}
function1();
function2();

I hope you got that right and I hope these simple concepts will help you to truly understand the significant role function closures play in JavaScript.

我希望您做对了,我希望这些简单的概念将帮助您真正理解函数闭包在JavaScript中扮演的重要角色。

Thanks for reading.

谢谢阅读。

Learning React or Node? Checkout my books:

学习React还是Node? 结帐我的书:

翻译自: https://www.freecodecamp.org/news/whats-a-javascript-closure-in-plain-english-please-6a1fc1d2ff1c/

javascript闭包

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值