JavaScript闭包是如何工作的?
您如何向了解JavaScript闭包所包含的概念(例如函数、变量等)但不了解闭包本身的人解释JavaScript闭包JavaScript闭包是如何工作的?,javascript,function,variables,scope,closures,Javascript,Function,Variables,Scope,Closures,您如何向了解JavaScript闭包所包含的概念(例如函数、变量等)但不了解闭包本身的人解释JavaScript闭包 我在维基百科上看到过Give,但不幸的是它没有帮助。闭包是一对: 函数,以及 对该函数外部作用域(词法环境)的引用 词法环境是每个执行上下文(堆栈帧)的一部分,是标识符(即局部变量名)和值之间的映射 JavaScript中的每个函数都维护对其外部词汇环境的引用。此引用用于配置调用函数时创建的执行上下文。此引用使函数内部的代码能够“查看”函数外部声明的变量,而不管函数何时何地被调用
我在维基百科上看到过Give,但不幸的是它没有帮助。闭包是一对:
JavaScript中的每个函数都维护对其外部词汇环境的引用。此引用用于配置调用函数时创建的执行上下文。此引用使函数内部的代码能够“查看”函数外部声明的变量,而不管函数何时何地被调用 如果一个函数被一个函数调用,而另一个函数又被另一个函数调用,那么就会创建一个指向外部词汇环境的引用链。此链称为范围链 在以下代码中,
internal
与调用foo
时创建的执行上下文的词法环境形成闭包,在变量secret
上关闭:
函数foo(){
const secret=Math.trunc(Math.random()*100)
返回函数内部(){
log(`密码是${secret}.`)
}
}
const f=foo()/`secret`不能直接从`foo'外部访问`
f()//检索'secret'的唯一方法是调用'f`
TLDR
闭包是函数与其外部词法(即编写的)环境之间的链接,这样,在该环境中定义的标识符(变量、参数、函数声明等)从函数内部可见,而不管函数何时或从何处调用
详细信息
在ECMAScript规范的术语中,闭包可以说是通过引用每个函数对象来实现的,它指向定义函数的对象
通过内部方法调用函数时,函数对象上的引用将复制到新创建的(堆栈帧)的外部环境引用中
在以下示例中,函数f
在全局执行上下文的词法环境中关闭:
function f() {}
在下面的示例中,functionh
关闭functiong
的词法环境,这反过来又关闭全局执行上下文的词法环境
function g() {
function h() {}
}
如果内部函数由外部函数返回,那么外部词法环境将在外部函数返回后继续存在。这是因为如果最终调用内部函数,则外部词汇环境需要可用
在以下示例中,函数j
关闭函数i
的词法环境,这意味着在函数i
完成执行后很久,变量x
从函数j
内部可见:
函数i(){
变量x='mochacchino'
返回函数j(){
log('从函数j中打印x的值:',x)
}
}
常数k=i()
setTimeout(k,500)//在500毫秒后调用k(即j)
闭包很难解释,因为它们用于使某些行为正常工作,而每个人直觉上都希望这些行为正常工作。我发现解释它们的最好方法(以及我了解它们的方式)是想象没有它们的情况:
const makePlus=函数(x){
返回函数(y){returnx+y;};
}
常数plus5=makePlus(5);
控制台日志(plus5(3))代码>JavaScript中的每个函数都维护到其外部词汇环境的链接。词法环境是范围内所有名称(如变量、参数)及其值的映射
因此,每当您看到函数
关键字时,该函数内的代码都可以访问函数外声明的变量
函数foo(x){
var-tmp=3;
功能条(y){
console.log(x+y+(++tmp));//将记录16
}
巴(10);
}
富(2),代码>闭包是内部函数可以访问外部函数中变量的地方。这可能是关于闭包的最简单的一行解释。这是一种尝试,旨在澄清在其他一些答案中出现的关于闭包的几个(可能的)误解
- 闭包不仅仅是在返回内部函数时创建的。事实上,为了创建闭包,封闭函数根本不需要返回。您可以将内部函数指定给外部作用域中的变量,或者将其作为参数传递给另一个函数,在该函数中可以立即调用或稍后随时调用。因此,封闭函数的闭包可能会在调用封闭函数时立即创建,因为无论何时调用内部函数,在封闭函数返回之前或之后,任何内部函数都可以访问该闭包李>
- 闭包不引用其作用域中变量旧值的副本。变量本身是闭包的一部分,因此访问其中一个变量时看到的值是访问该变量时的最新值。这就是为什么在循环内部创建的内部函数可能很棘手,因为每个函数都可以访问相同的外部变量,而不是在创建或调用函数时获取变量的副本
- 闭包中的“变量”包括函数中声明的任何命名函数。它们还包括函数的参数。闭包还可以访问其包含闭包的变量
var i;
function foo(x) {
var tmp = 3;
i = function (y) {
console.log(x + y + (++tmp));
}
}
foo(2);
i(3);
/*
* When a function is defined in another function and it
* has access to the outer function's context even after
* the outer function returns.
*
* An important concept to learn in JavaScript.
*/
function outerFunction(someNum) {
var someString = 'Hey!';
var content = document.getElementById('content');
function innerFunction() {
content.innerHTML = someNum + ': ' + someString;
content = null; // Internet Explorer memory leak for DOM reference
}
innerFunction();
}
outerFunction(1);
var isVotedUp = false;
var isVotedDown = false;
function voteUp_click() {
if (isVotedUp)
return;
else if (isVotedDown)
SetDownVote(false);
else
SetUpVote(true);
}
function voteDown_click() {
if (isVotedDown)
return;
else if (isVotedUp)
SetUpVote(false);
else
SetDownVote(true);
}
function SetUpVote(status) {
isVotedUp = status;
// Do some CSS stuff to Vote-Up button
}
function SetDownVote(status) {
isVotedDown = status;
// Do some CSS stuff to Vote-Down button
}
function princess() {
var adventures = [];
function princeCharming() { /* ... */ }
var unicorn = { /* ... */ },
dragons = [ /* ... */ ],
squirrel = "Hello!";
/* ... */
return {
story: function() {
return adventures[adventures.length - 1];
}
};
}
var littleGirl = princess();
littleGirl.story();
var create = function (x) {
var f = function () {
return x; // We can refer to x here!
};
return f;
};
// 'create' takes one argument, creates a function
var g = create(42);
// g is a function that takes no arguments now
var y = g();
// y is 42 here
var db = (function() {
// Create a hidden object, which will hold the data
// it's inaccessible from the outside.
var data = {};
// Make a function, which will provide some access to the data.
return function(key, val) {
if (val === undefined) { return data[key] } // Get
else { return data[key] = val } // Set
}
// We are calling the anonymous surrounding function,
// returning the above inner function, which is a closure.
})();
db('x') // -> undefined
db('x', 1) // Set x to 1
db('x') // -> 1
// It's impossible to access the data object itself.
// We are able to get or set individual it.
// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
var _count = 0; // not accessible outside this function
var sequencer = function () {
return _count++;
}
return sequencer;
}
var fnext = makeSequencer();
var v0 = fnext(); // v0 = 0;
var v1 = fnext(); // v1 = 1;
var vz = fnext._count // vz = undefined
function foo (initValue) {
//This variable is not destroyed when the foo function exits.
//It is 'captured' by the two nested functions returned below.
var value = initValue;
//Note that the two returned functions are created right now.
//If the foo function is called again, it will return
//new functions referencing a different 'value' variable.
return {
getValue: function () { return value; },
setValue: function (newValue) { value = newValue; }
}
}
function bar () {
//foo sets its local variable 'value' to 5 and returns an object with
//two functions still referencing that local variable
var obj = foo(5);
//Extracting functions just to show that no 'this' is involved here
var getValue = obj.getValue;
var setValue = obj.setValue;
alert(getValue()); //Displays 5
setValue(10);
alert(getValue()); //Displays 10
//At this point getValue and setValue functions are destroyed
//(in reality they are destroyed at the next iteration of the garbage collector).
//The local variable 'value' in the foo is no longer referenced by
//anything and is destroyed too.
}
bar();
function playingInBrothersRoom (withToys) {
// We closure toys which we played in the brother's room. When he come back and lock the door
// your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
var closureToys = withToys || [],
returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.
var brotherGivesToyBack = function (toy) {
// New request. There is not yet closureToys on brother's hand yet. Give him a time.
returnToy = null;
if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.
for ( countIt = closureToys.length; countIt; countIt--) {
if (closureToys[countIt - 1] == toy) {
returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
break;
}
}
returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
}
else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
returnToy = 'Behold! ' + closureToys.join(', ') + '.';
closureToys = [];
}
else {
returnToy = 'Hey, lil shrimp, I gave you everything!';
}
console.log(returnToy);
}
return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
askBrotherForClosuredToy = playingInBrothersRoom(toys);
// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined
// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there
function the_closure() {
var x = 4;
return function () {
return x; // Here, we look back inside the_closure for the value of x
}
}
var myFn = the_closure();
myFn(); //=> 4
console.log('CLOSURES DONE RIGHT');
var arr = [];
function createClosure(n) {
return function () {
return 'n = ' + n;
}
}
for (var index = 0; index < 10; index++) {
arr[index] = createClosure(index);
}
for (var index in arr) {
console.log(arr[index]());
}
console.log('CLOSURES DONE WRONG');
function createClosureArray() {
var badArr = [];
for (var index = 0; index < 10; index++) {
badArr[index] = function () {
return 'n = ' + index;
};
}
return badArr;
}
var badArr = createClosureArray();
for (var index in badArr) {
console.log(badArr[index]());
}
function make_calculator() {
var n = 0; // this calculator stores a single number n
return {
add: function(a) {
n += a;
return n;
},
multiply: function(a) {
n *= a;
return n;
}
};
}
first_calculator = make_calculator();
second_calculator = make_calculator();
first_calculator.add(3); // returns 3
second_calculator.add(400); // returns 400
first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000
function sing(person) {
var firstPart = "There was " + person + " who swallowed ";
var fly = function() {
var creature = "a fly";
var result = "Perhaps she'll die";
alert(firstPart + creature + "\n" + result);
};
var spider = function() {
var creature = "a spider";
var result = "that wiggled and jiggled and tickled inside her";
alert(firstPart + creature + "\n" + result);
};
var bird = function() {
var creature = "a bird";
var result = "How absurd!";
alert(firstPart + creature + "\n" + result);
};
var cat = function() {
var creature = "a cat";
var result = "Imagine That!";
alert(firstPart + creature + "\n" + result);
};
fly();
spider();
bird();
cat();
}
var person="an old lady";
sing(person);
function cookMeal() { /* STUFF INSIDE THE FUNCTION */ }
function sing(person) { /* STUFF INSIDE THE FUNCTION */ }
var person="an old lady";
sing(person);
fly();
spider();
bird();
cat();
var a = 42;
function b() { return a; }
var parent = function() {
var name = "Mary"; // secret
}
var parent = function() {
var name = "Mary";
var child = function(childName) {
// I can also see that "name" is "Mary"
}
}
var parent = function() {
var name = "Mary";
var child = function(childName) {
return "My name is " + childName +", child of " + name;
}
return child; // child leaves the parent ->
}
var child = parent(); // < - and here it is outside
child("Alice") => "My name is Alice, child of Mary"