etc./StackOverFlow

JavaScript 클로저는 어떻게 작동합니까?

청렴결백한 만능 재주꾼 2021. 9. 22. 13:27
반응형

질문자 :Community Wiki


JavaScript 클로저가 구성되어 있는 개념(예: 함수, 변수 등)에 대한 지식이 있지만 클로저 자체를 이해하지 못하는 사람에게 JavaScript 클로저를 어떻게 설명하시겠습니까?

Wikipedia에 제공된 Scheme 예제를 보았지만 불행히도 도움이 되지 않았습니다.



답변자 : Community Wiki


클로저는 다음의 쌍입니다.

  1. 함수와
  2. 해당 함수의 외부 범위에 대한 참조(어휘 환경)

어휘 환경은 모든 실행 컨텍스트(스택 프레임)의 일부이며 식별자(즉, 로컬 변수 이름)와 값 사이의 맵입니다.

JavaScript의 모든 함수는 외부 어휘 환경에 대한 참조를 유지합니다. 이 참조는 함수가 호출될 때 생성되는 실행 컨텍스트를 구성하는 데 사용됩니다. 이 참조를 사용하면 함수가 호출되는 시기와 위치에 관계없이 함수 내부의 코드가 함수 외부에 선언된 변수를 "볼" 수 있습니다.

함수가 다른 함수에 의해 호출된 함수에 의해 호출된 경우 외부 어휘 환경에 대한 참조 체인이 생성됩니다. 이 체인을 범위 체인이라고 합니다.

다음 코드에서 inner foo 가 호출될 때 생성된 실행 컨텍스트의 어휘 환경으로 클로저를 형성하고 변수 secret 닫습니다 .

 function foo() { const secret = Math.trunc(Math.random()*100) return function inner() { console.log(`The secret number is ${secret}.`) } } const f = foo() // `secret` is not directly accessible from outside `foo` f() // The only way to retrieve `secret`, is to invoke `f`

다시 말해, JavaScript에서 함수는 비공개 "상태 상자"에 대한 참조를 전달하며, 이 참조는 해당 함수(및 동일한 어휘 환경 내에서 선언된 다른 함수)만 액세스할 수 있습니다. 이 상태 상자는 함수 호출자에게 보이지 않으므로 데이터 숨김 및 캡슐화를 위한 우수한 메커니즘을 제공합니다.

그리고 기억하십시오. JavaScript의 함수는 변수(일급 함수)처럼 전달될 수 있습니다. 즉, 이러한 기능과 상태의 쌍은 C++에서 클래스의 인스턴스를 전달하는 방법과 유사하게 프로그램 주위로 전달될 수 있습니다.

JavaScript에 클로저가 없으면 더 많은 상태를 함수 간에 명시적으로 전달해야 하므로 매개변수 목록이 더 길어지고 코드가 더 복잡해집니다.

따라서 함수가 항상 비공개 상태에 액세스할 수 있도록 하려면 클로저를 사용할 수 있습니다.

...그리고 자주 우리 상태를 함수와 연관시키고 싶어합니다. 예를 들어, Java 또는 C++에서 개인 인스턴스 변수와 메소드를 클래스에 추가하면 상태를 기능과 연관시키는 것입니다.

C 및 대부분의 다른 공용 언어에서 함수가 반환된 후 스택 프레임이 파괴되기 때문에 모든 지역 변수에 더 이상 액세스할 수 없습니다. JavaScript에서 다른 함수 내에서 함수를 선언하면 외부 함수의 지역 변수는 반환 후에도 계속 액세스할 수 있습니다. 이런 식으로 위의 코드에서 secret foo 에서 반환된 후에도 inner 함수 개체에서 계속 사용할 수 있습니다.

클로저 사용

클로저는 함수와 연결된 개인 상태가 필요할 때마다 유용합니다. 이것은 매우 일반적인 시나리오이며 기억하십시오. JavaScript는 2015년까지 클래스 구문이 없었으며 여전히 private 필드 구문이 없습니다. 폐쇄는 이러한 요구를 충족시킵니다.

프라이빗 인스턴스 변수

다음 코드에서 toString 함수는 자동차의 세부 정보를 닫습니다.

 function Car(manufacturer, model, year, color) { return { toString() { return `${manufacturer} ${model} (${year}, ${color})` } } } const car = new Car('Aston Martin','V8 Vantage','2012','Quantum Silver') console.log(car.toString())

함수형 프로그래밍

다음 코드에서 inner fnargs 모두에 대해 닫힙니다.

 function curry(fn) { const args = [] return function inner(arg) { if(args.length === fn.length) return fn(...args) args.push(arg) return inner } } function add(a, b) { return a + b } const curriedAdd = curry(add) console.log(curriedAdd(2)(3)()) // 5

이벤트 지향 프로그래밍

다음 코드에서 onClick BACKGROUND_COLOR 변수를 닫습니다.

 const $ = document.querySelector.bind(document) const BACKGROUND_COLOR = 'rgba(200,200,242,1)' function onClick() { $('body').style.background = BACKGROUND_COLOR } $('button').addEventListener('click', onClick)
 <button>Set background color</button>

모듈화

다음 예에서 모든 구현 세부 정보는 즉시 실행되는 함수 표현식 안에 숨겨져 있습니다. 함수 ticktoString 은 작업을 완료하는 데 필요한 비공개 상태 및 함수를 닫습니다. 클로저를 통해 코드를 모듈화하고 캡슐화할 수 있습니다.

 let namespace = {}; (function foo(n) { let numbers = [] function format(n) { return Math.trunc(n) } function tick() { numbers.push(Math.random() * 100) } function toString() { return numbers.map(format) } n.counter = { tick, toString } }(namespace)) const counter = namespace.counter counter.tick() counter.tick() console.log(counter.toString())

실시예 1

이 예제는 로컬 변수가 클로저에서 복사되지 않는다는 것을 보여줍니다: 클로저는 원래 변수 자체에 대한 참조를 유지합니다. 외부 함수가 종료된 후에도 스택 프레임이 메모리에 살아 있는 것과 같습니다.

 function foo() { let x = 42 let inner = function() { console.log(x) } x = x+1 return inner } var f = foo() f() // logs 43

실시예 2

다음 코드에서 세 가지 메서드 log , incrementupdate 모두 동일한 어휘 환경에서 닫힙니다.

createObject 가 호출될 때마다 새로운 실행 컨텍스트(스택 프레임)가 생성되고 완전히 새로운 변수 x 와 새로운 함수 세트( log 등)가 생성되어 이 새로운 변수를 닫습니다.

 function createObject() { let x = 42; return { log() { console.log(x) }, increment() { x++ }, update(value) { x = value } } } const o = createObject() o.increment() o.log() // 43 o.update(5) o.log() // 5 const p = createObject() p.log() // 42

실시예 3

var 사용하여 선언된 변수를 사용하는 경우 닫고 있는 변수를 이해하도록 주의하십시오. var 를 사용하여 선언된 변수는 호이스트됩니다. letconst 의 도입으로 인해 현대 JavaScript에서 문제가 훨씬 적습니다.

다음 코드에서 루프를 돌 때마다 새로운 inner 함수가 생성되어 i 를 닫습니다. var i 가 루프 외부에서 호이스트되기 때문에 이러한 모든 내부 함수는 동일한 변수에 대해 닫힙니다. 즉, i (3)의 최종 값이 세 번 인쇄됩니다.

 function foo() { var result = [] for (var i = 0; i < 3; i++) { result.push(function inner() { console.log(i) } ) } return result } const result = foo() // The following will print `3`, three times... for (var i = 0; i < 3; i++) { result[i]() }

최종 포인트:

  • JavaScript에서 함수가 선언될 때마다 클로저가 생성됩니다.
  • function 를 반환하는 것은 클로저의 고전적인 예입니다. 외부 함수가 실행을 완료한 후에도 외부 함수 내부의 상태가 반환된 내부 함수에 암시적으로 사용 가능하기 때문입니다.
  • eval() 을 사용할 때마다 클로저가 사용됩니다. eval 하는 텍스트는 함수의 지역 변수를 참조할 수 있으며, 비 엄격 모드에서는 eval('var foo = …') 을 사용하여 새 지역 변수를 만들 수도 있습니다.
  • new Function(…) ( Function 생성자 )을 사용하면 어휘 환경에서 닫히지 않습니다. 대신 전역 컨텍스트에서 닫힙니다. 새 함수는 외부 함수의 지역 변수를 참조할 수 없습니다.
  • 자바 스크립트 클로저의 상단에 전역 객체를 차례로 등등의 외부 범위에 대한 참조를 유지하고, 함수 선언의 시점에서 모든 방법을 범위에 대한 참조 (NOT 사본)를 유지하는 것과 같다 범위 체인.
  • 함수가 선언되면 클로저가 생성됩니다. 이 클로저는 함수가 호출될 때 실행 컨텍스트를 구성하는 데 사용됩니다.
  • 함수가 호출될 때마다 새로운 지역 변수 세트가 생성됩니다.

연결



답변자 : Community Wiki


JavaScript의 모든 함수는 외부 어휘 환경에 대한 링크를 유지합니다. 어휘 환경은 해당 값과 함께 범위 내의 모든 이름(예: 변수, 매개변수)의 맵입니다.

function 키워드를 볼 때마다 해당 함수 내부의 코드는 함수 외부에 선언된 변수에 액세스할 수 있습니다.

 function foo(x) { var tmp = 3; function bar(y) { console.log(x + y + (++tmp)); // will log 16 } bar(10); } foo(2);

함수 bar 가 매개변수 x 와 변수 tmp 16 을 기록할 것입니다 foo 의 어휘 환경에 존재합니다.

foo 의 어휘 환경과의 링크와 함께 함수 bar 는 클로저입니다.

클로저를 생성하기 위해 함수가 반환 될 필요는 없습니다. 단순히 선언 덕분에 모든 함수는 둘러싸는 어휘 환경을 닫고 클로저를 형성합니다.

 function foo(x) { var tmp = 3; return function (y) { console.log(x + y + (++tmp)); // will also log 16 } } var bar = foo(2); bar(10); // 16 bar(10); // 17

위의 함수는 또한 16을 기록합니다. 왜냐하면 bar 안의 코드는 더 이상 직접 범위에 있지 않더라도 x 와 변수 tmp 참조할 수 있기 때문입니다.

tmp bar 의 클로저 내부에 매달려 있기 때문에 증가할 수 있습니다. bar 를 호출할 때마다 증가합니다.

클로저의 가장 간단한 예는 다음과 같습니다.

 var a = 10; function test() { console.log(a); // will output 10 console.log(b); // will output 6 } var b = 6; test();

JavaScript 함수가 호출되면 새 실행 컨텍스트 ec 가 생성됩니다. 함수 인수 및 대상 객체와 함께 이 실행 컨텍스트는 호출 실행 컨텍스트의 어휘 환경에 대한 링크도 수신합니다. 즉, 외부 어휘 환경(위의 예에서 ab 모두)에서 선언된 변수를 다음에서 사용할 수 있습니다. ec .

모든 함수는 외부 어휘 환경에 대한 링크를 가지고 있기 때문에 모든 함수는 클로저를 생성합니다.

변수 자체 는 복사본이 아니라 클로저 내에서 볼 수 있습니다.



답변자 : Community Wiki


머리말: 이 답변은 질문이 다음과 같을 때 작성되었습니다.

오래된 Albert가 말했듯이 "6살짜리에게 설명할 수 없다면 스스로 이해하지 못하는 것입니다." 글쎄요. 27살짜리 친구에게 JS 클로저를 설명하려 했지만 완전히 실패했습니다.

아무도 내가 6살이고 그 주제에 이상하게 관심이 있다고 생각할 수 있습니까?

나는 첫 번째 질문을 문자 그대로 받아들이려고 시도한 유일한 사람 중 하나라고 확신합니다. 그 이후로 질문은 여러 번 변경되었으므로 내 대답은 이제 믿을 수 없을 정도로 어리석고 적절하지 않은 것처럼 보일 수 있습니다. 일부 사람들에게는 이야기의 일반적인 아이디어가 여전히 재미있기를 바랍니다.


저는 어려운 개념을 설명할 때 유추와 은유를 좋아합니다. 그래서 제가 직접 이야기를 해보겠습니다.

옛날 옛적에:

공주가 있었다...

 function princess() {

그녀는 모험으로 가득 찬 멋진 세상에서 살았습니다. 그녀는 Charming 왕자를 만났고, 유니콘을 타고 그녀의 세계를 일주했고, 용과 싸우고, 말하는 동물을 만났고, 다른 많은 환상적인 것들을 만났습니다.

 var adventures = []; function princeCharming() { /* ... */ } var unicorn = { /* ... */ }, dragons = [ /* ... */ ], squirrel = "Hello!"; /* ... */

그러나 그녀는 항상 자신의 지루한 집안일과 어른들의 세계로 돌아가야 했습니다.

 return {

그리고 그녀는 종종 공주로서 최근의 놀라운 모험에 대해 이야기하곤 했습니다.

 story: function() { return adventures[adventures.length - 1]; } }; }

그러나 그들이 보게 될 것은 어린 소녀뿐입니다...

 var littleGirl = princess();

...마법과 환상에 대한 이야기를 전합니다.

 littleGirl.story();

그리고 어른들은 진짜 공주를 알고 있다고 해도 유니콘이나 용을 본 적이 없기 때문에 절대 믿지 않을 것입니다. 어른들은 자신은 어린 소녀의 상상 속에만 존재한다고 말했다.

그러나 우리는 진정한 진실을 알고 있습니다. 그 안에 공주가 있는 어린 소녀...

...안에 어린 소녀가 있는 정말 공주입니다.



답변자 : Community Wiki


질문을 진지하게 받아들이면, 자바스크립트에 관심이 있는 사람은 그렇게 일반적이지 않지만 일반적인 6세 어린이가 인지적으로 무엇을 할 수 있는지 알아내야 합니다.

아동기 발달: 5-7세에 다음과 같이 나와 있습니다.

자녀는 2단계 지시를 따를 수 있습니다. 예를 들어, 아이에게 "주방에 가서 쓰레기봉투 좀 주세요"라고 말하면 아이는 그 방향을 기억할 수 있을 것입니다.

이 예를 사용하여 다음과 같이 클로저를 설명할 수 있습니다.

주방은 로컬 변수가 있는 trashBags 라는 클로저입니다. 하나의 쓰레기 봉투를 getTrashBag 반환하는 getTrashBag라는 기능이 주방 내부에 있습니다.

다음과 같이 JavaScript로 코딩할 수 있습니다.

 function makeKitchen() { var trashBags = ['A', 'B', 'C']; // only 3 at first return { getTrashBag: function() { return trashBags.pop(); } }; } var kitchen = makeKitchen(); console.log(kitchen.getTrashBag()); // returns trash bag C console.log(kitchen.getTrashBag()); // returns trash bag B console.log(kitchen.getTrashBag()); // returns trash bag A

클로저가 흥미로운 이유를 설명하는 추가 사항:

  • makeKitchen() 을 호출할 때마다 trashBags 사용하여 새 클로저가 생성됩니다.
  • trashBags 변수는 각 주방 내부에 로컬이며 외부에서 액세스할 수 없지만 getTrashBag 속성의 내부 함수는 액세스할 수 있습니다.
  • 모든 함수 호출은 클로저를 생성하지만 클로저 내부에 접근할 수 있는 내부 함수가 클로저 외부에서 호출될 수 없다면 클로저를 유지할 필요가 없습니다. getTrashBag 함수를 사용하여 객체를 반환하면 여기에서 수행됩니다.


답변자 : Community Wiki


밀짚맨

버튼을 몇 번 클릭했는지 알고 세 번째 클릭마다 뭔가를 해야 합니다...

상당히 분명한 솔루션

 // Declare counter outside event handler's scope var counter = 0; var element = document.getElementById('button'); element.addEventListener("click", function() { // Increment outside counter counter++; if (counter === 3) { // Do something every third time console.log("Third time's the charm!"); // Reset counter counter = 0; } });
 <button id="button">Click Me!</button>

이제 이것은 작동하지만 카운트를 추적하는 것이 유일한 목적인 변수를 추가하여 외부 범위를 잠식합니다. 어떤 상황에서는 외부 응용 프로그램이 이 정보에 액세스해야 할 수 있으므로 이 방법을 사용하는 것이 좋습니다. 그러나 이 경우 세 번째 클릭의 동작만 변경하므로 이 기능을 이벤트 처리기 내부에 포함하는 것이 좋습니다.

이 옵션을 고려하십시오

 var element = document.getElementById('button'); element.addEventListener("click", (function() { // init the count to 0 var count = 0; return function(e) { // <- This function becomes the click handler count++; // and will retain access to the above `count` if (count === 3) { // Do something every third time console.log("Third time's the charm!"); //Reset counter count = 0; } }; })());
 <button id="button">Click Me!</button>

여기서 몇 가지 사항에 유의하십시오.

위의 예에서는 JavaScript의 클로저 동작을 사용하고 있습니다. 이 동작을 사용하면 모든 함수가 생성된 범위에 무기한 액세스할 수 있습니다. 이를 실제로 적용하기 위해 즉시 다른 함수를 반환하는 함수를 호출합니다. 반환하는 함수가 내부 카운트 변수에 액세스할 수 있기 때문에(위에서 설명한 클로저 동작으로 인해) 결과적으로 개인용 범위가 사용됩니다. 기능 ... 그렇게 간단하지 않습니까? 희석하자...

간단한 한 줄 클로저

 // _______________________Immediately invoked______________________ // | | // | Scope retained for use ___Returned as the____ | // | only by returned function | value of func | | // | | | | | | // vvvvvv var func = (function() { var a = 'val'; return function() { alert(a); }; })();

반환된 함수 외부의 모든 변수는 반환된 함수에서 사용할 수 있지만 반환된 함수 개체에서 직접 사용할 수는 없습니다...

 func(); // Alerts "val" func.a; // Undefined

알겠어? 따라서 기본 예제에서 count 변수는 클로저 내에 포함되어 있으며 이벤트 핸들러에서 항상 사용할 수 있으므로 클릭할 때마다 상태를 유지합니다.

또한,이 전용 변수 상태는 모두 판독과의 개인 범위의 변수에 할당 완벽하게 액세스 할 수 있습니다.

당신은 간다; 이제 이 동작을 완전히 캡슐화하고 있습니다.

전체 블로그 게시물 (jQuery 고려 사항 포함)



답변자 : Community Wiki


클로저는 모든 사람이 직관적으로 어쨌든 작동할 것으로 기대하는 일부 동작을 작동시키는 데 사용되기 때문에 설명하기 어렵습니다. 나는 그들을 설명하는 가장 좋은 방법(그리고 내가 그들이 하는 일을 배운 방법)이 없는 상황을 상상하는 것이라고 생각합니다.

 const makePlus = function(x) { return function(y) { return x + y; }; } const plus5 = makePlus(5); console.log(plus5(3));

JavaScript 가 클로저를 알지 못한다면 어떻게 될까요? 마지막 줄의 호출을 메서드 본문(기본적으로 함수 호출이 수행하는 작업)으로 바꾸면 다음과 같은 결과를 얻을 수 있습니다.

 console.log(x + 3);

x 의 정의는 어디에 있습니까? 현재 범위에서 정의하지 않았습니다. 유일한 해결책은 수 있도록하는 것입니다 plus5 주위의 범위를 가지고 (또는 오히려, 부모의 범위). 이런 식으로 x 는 잘 정의되고 값 5에 바인딩됩니다.



답변자 : Community Wiki


TLDR

클로저는 함수와 외부 어휘(즉, 작성된) 환경 사이의 링크이므로 해당 환경 내에서 정의된 식별자(변수, 매개변수, 함수 선언 등)는 언제 또는 어디서든 함수 내에서 볼 수 있습니다. 함수가 호출되는 곳.

세부

ECMAScript 사양의 용어에서 클로저는 함수가 정의된 어휘 환경 을 가리키는 모든 함수 개체 [[Environment]]

[[Call]] 메서드를 통해 함수가 호출되면 함수 개체 [[Environment]] 참조가 새로 생성된 실행 컨텍스트 (스택 프레임)의 환경 레코드 의 외부 환경 참조로 복사됩니다.

다음 예에서 함수 f 는 전역 실행 컨텍스트의 어휘 환경을 닫습니다.

 function f() {}

다음 예에서 함수 h 는 함수 g 의 어휘 환경을 닫고, 차례로 전역 실행 컨텍스트의 어휘 환경을 닫습니다.

 function g() { function h() {} }

외부 함수가 내부 함수를 반환하면 외부 함수가 반환된 후에도 외부 어휘 환경이 유지됩니다. 내부 함수가 결국 호출되는 경우 외부 어휘 환경을 사용할 수 있어야 하기 때문입니다.

다음 예제에서 함수 j i 의 어휘 환경을 닫습니다. 즉, 변수 x i 가 실행을 완료한 후에도 오랫동안 j 내부에서 볼 수 있습니다.

 function i() { var x = 'mochacchino' return function j() { console.log('Printing the value of x, from within function j: ', x) } } const k = i() setTimeout(k, 500) // invoke k (which is j) after 500ms

클로저에서 외부 어휘 환경의 변수 자체 는 복사본이 아니라 사용 가능합니다.

 function l() { var y = 'vanilla'; return { setY: function(value) { y = value; }, logY: function(value) { console.log('The value of y is: ', y); } } } const o = l() o.logY() // The value of y is: vanilla o.setY('chocolate') o.logY() // The value of y is: chocolate

외부 환경 참조를 통해 실행 컨텍스트 간에 연결된 어휘 환경 체인은 범위 체인을 형성하고 주어진 기능에서 볼 수 있는 식별자를 정의합니다.

명확성과 정확성을 개선하기 위해 이 답변은 원본에서 크게 변경되었습니다.



답변자 : Community Wiki


좋아요, 6살짜리 폐쇄 팬입니다. 클로저의 가장 간단한 예를 듣고 싶습니까?

다음 상황을 상상해 봅시다. 운전자가 차에 앉아 있습니다. 그 차는 비행기 안에 있습니다. 비행기가 공항에 있습니다. 운전자가 자신의 차 밖에 있는 물건에 접근할 수 있지만 비행기 내부에서는 비행기가 공항을 떠나더라도 폐쇄됩니다. 그게 다야 27세가 되면 더 자세한 설명 이나 아래 예시를 보세요.

다음은 내 비행기 이야기를 코드로 변환하는 방법입니다.

 var plane = function(defaultAirport) { var lastAirportLeft = defaultAirport; var car = { driver: { startAccessPlaneInfo: function() { setInterval(function() { console.log("Last airport was " + lastAirportLeft); }, 2000); } } }; car.driver.startAccessPlaneInfo(); return { leaveTheAirport: function(airPortName) { lastAirportLeft = airPortName; } } }("Boryspil International Airport"); plane.leaveTheAirport("John F. Kennedy");



답변자 : Community Wiki


이것은 다른 답변 중 일부에 나타나는 클로저에 대한 몇 가지 (가능한) 오해를 해결하려는 시도입니다.

  • 내부 함수를 반환할 때만 클로저가 생성되는 것은 아닙니다. 사실, 둘러싸는 함수는 클로저가 생성되기 위해 반환할 필요가 전혀 없습니다. 대신 내부 함수를 외부 범위의 변수에 할당하거나 즉시 또는 나중에 언제든지 호출할 수 있는 다른 함수에 인수로 전달할 수 있습니다. 따라서 바깥쪽 함수의 클로저는 바깥쪽 함수가 반환되기 전이나 후에 내부 함수가 호출될 때마다 내부 함수가 해당 클로저에 액세스할 수 있기 때문에 바깥쪽 함수가 호출되자마자 생성될 것입니다.
  • 클로저는 해당 범위에 있는 변수 의 이전 값 복사본을 참조하지 않습니다. 변수 자체는 클로저의 일부이므로 이러한 변수 중 하나에 액세스할 때 표시되는 값은 액세스한 시점의 최신 값입니다. 이것이 루프 내부에서 생성된 내부 함수가 까다로울 수 있는 이유입니다. 각 함수는 함수가 생성되거나 호출될 때 변수의 복사본을 가져오지 않고 동일한 외부 변수에 액세스할 수 있기 때문입니다.
  • 클로저의 "변수"에는 함수 내에서 선언된 명명된 함수가 포함됩니다. 여기에는 함수의 인수도 포함됩니다. 클로저는 또한 전역 범위까지 포함하는 클로저의 변수에 액세스할 수 있습니다.
  • 클로저는 메모리를 사용하지만 JavaScript 자체가 참조되지 않는 자체 순환 구조를 정리하므로 메모리 누수가 발생하지 않습니다. 클로저와 관련된 Internet Explorer 메모리 누수는 클로저를 참조하는 DOM 속성 값의 연결을 끊는 데 실패하여 생성될 수 있으므로 가능한 순환 구조에 대한 참조를 유지합니다.


답변자 : Community Wiki


얼마 전에 폐쇄에 대해 설명하는 블로그 게시물을 작성했습니다. 폐쇄를 원하는 이유 에 대해 제가 말한 내용은 다음과 같습니다.

클로저는 함수가 영구적인 개인 변수를 갖도록 하는 방법입니다.

그런 의미에서 그들은 함수가 private 속성을 가진 객체처럼 작동하도록 합니다.

전체 게시물:

그래서 이 폐쇄 물건은 무엇입니까?



답변자 : Community Wiki


폐쇄는 간단합니다:

다음의 간단한 예제는 JavaScript 클로저의 모든 주요 사항을 다룹니다. *

다음은 더하고 곱할 수 있는 계산기를 생산하는 공장입니다.

 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

요점: make_calculator 대한 각 호출은 새로운 지역 변수 n 을 생성합니다. 이 make_calculator 반환된 후에도 오랫동안 addmultiply 함수에서 계속 사용할 수 있습니다.

스택 프레임에 익숙하다면 이 계산기가 이상하게 보일 것입니다. make_calculator 반환된 후에도 n 대답은 JavaScript가 "스택 프레임"을 사용하지 않고 대신 "힙 프레임"을 사용한다고 상상하는 것입니다.

외부 함수 ** 에 선언된 변수에 액세스하는 addmultiply 과 같은 내부 함수를 클로저 라고 합니다.

그것이 폐쇄의 거의 전부입니다.



* 예를 들어, 다른 답변에 제공된 "인형을 위한 클로저" 기사의 모든 요점을 다룹니다. 예제 6은 변수가 선언되기 전에 사용할 수 있음을 단순히 보여줍니다. 알고 있으면 좋지만 클로저와 완전히 관련이 없습니다. 또한 (1) 함수가 인수를 로컬 변수(명명된 함수 인수)에 복사하고 (2) 숫자를 복사하면 새 숫자가 생성되지만 객체 참조를 복사하는 점을 제외하고 허용되는 답변의 모든 요점을 다룹니다. 동일한 객체에 대한 또 다른 참조를 제공합니다. 이것들도 알아두면 좋지만 클로저와 완전히 관련이 없습니다. 또한 이 답변 의 예와 매우 유사하지만 조금 더 짧고 덜 추상적입니다. JavaScript가 루프 변수의 현재 값을 내부 함수에 연결하기 어렵게 만든다는 이 답변 이나 이 주석 의 요점은 다루지 않습니다. "연결" 단계는 다음을 포함하는 도우미 함수로만 수행할 수 있습니다. 내부 함수이며 각 루프 반복에서 호출됩니다. (엄밀히 말하면 내부 함수는 플러그인이 아닌 도우미 함수의 변수 복사본에 액세스합니다.) 다시 말하지만, 클로저를 생성할 때 매우 유용하지만 클로저가 무엇인지 또는 작동 방식의 일부는 아닙니다. ML과 같은 기능적 언어에서 클로저가 다르게 작동하기 때문에 추가적인 혼란이 있습니다. 여기서 변수는 저장 공간이 아닌 값에 바인딩되어 다음과 같은 방식(즉, "플러그인" 방식)으로 클로저를 이해하는 사람들의 지속적인 흐름을 제공합니다. 변수가 항상 저장 공간에 바인딩되고 결코 값에 바인딩되지 않는 JavaScript에서는 잘못된 것입니다.

** 이 답변 에서 명확하게 지적한 바와 같이 여러 외부 함수가 중첩되거나 전역 컨텍스트에 있는 경우 모든 외부 함수입니다.



답변자 : Community Wiki


여섯 살짜리 아이에게 어떻게 설명할까요?

어른들이 집을 소유할 수 있는 방법을 알고 있으며, 그들은 그것을 집이라고 부릅니다. 엄마가 아이를 낳으면 아이는 정말 아무것도 소유할 수 없잖아요? 그러나 그 부모는 집을 소유하고 있기 때문에 누군가가 아이에게 "집이 어디니?"라고 물으면 "그 집!"이라고 대답하고 부모의 집을 가리킬 수 있습니다. "폐쇄"는 자녀가 항상(해외에 있더라도) 집이 있다고 말할 수 있는 능력입니다. 실제로 집을 소유한 사람은 부모입니다.



답변자 : Community Wiki


폐쇄를 5세에게 설명할 수 있습니까?*

나는 여전히 Google의 설명 이 매우 잘 작동하고 간결하다고 생각합니다.

 /* * 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);​ 

이 예제는 내부 함수가 반환하지 않더라도 클로저를 생성한다는 증거

*AC# 질문



답변자 : Community Wiki


나는 GOOD/BAD 비교를 통해 더 잘 배우는 경향이 있습니다. 나는 누군가가 마주할 가능성이 있는 작동 코드 다음에 작동하지 않는 코드를 보는 것을 좋아합니다. 나는 비교를 수행하고 내가 생각해낼 수 있는 가장 간단한 설명으로 차이점을 요약하려고 하는 jsFiddle을 구성했습니다.

제대로 된 폐쇄:

 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]()); }
  • 위의 코드에서 createClosure(n) 은 루프의 모든 반복에서 호출됩니다. n 은 새 함수 범위에서 생성된 변수이며 외부 범위에 바인딩된 index 와 동일한 변수가 아님을 강조하기 위해 이름을 지정했습니다.

  • 이렇게 하면 새 범위가 생성되고 n 은 해당 범위에 바인딩됩니다. 이는 각 반복마다 하나씩 10개의 개별 범위가 있음을 의미합니다.

  • createClosure(n) 은 해당 범위 내에서 n을 반환하는 함수를 반환합니다.

  • 각 범위 내에서 n createClosure(n) 이 호출되었을 때의 값에 바인딩되어 있으므로 반환되는 중첩 함수는 항상 createClosure(n) 이 호출되었을 때 n

잘못된 폐쇄:

 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]()); }
  • 위의 코드에서 루프는 createClosureArray() 함수 내에서 이동되었으며 이제 함수는 완성된 배열을 반환하므로 언뜻 보기에는 더 직관적으로 보입니다.

  • 명확하지 않을 수 있는 것은 createClosureArray() 가 루프의 모든 반복에 대해 하나씩이 아니라 이 함수에 대해 하나의 범위만 생성된 후에만 호출된다는 것입니다.

  • 이 함수 내에서 index 라는 변수가 정의됩니다. index 를 반환하는 함수를 배열에 추가합니다. index 는 한 번만 호출되는 createClosureArray 함수 내에서 정의됩니다.

  • createClosureArray() 함수 내에는 하나의 범위만 있었기 때문에 index 는 해당 범위 내의 값에만 바인딩됩니다. index 값을 변경할 때마다 해당 범위 내에서 이를 참조하는 모든 항목에 대해 변경합니다.

  • 배열에 추가된 모든 함수는 첫 번째 예와 같이 10개의 다른 범위에서 10개의 다른 변수 대신 정의된 상위 범위에서 index 최종 결과는 10개의 모든 함수가 동일한 범위에서 동일한 변수를 반환한다는 것입니다.

  • 루프가 완료되고 index 수정이 완료된 후 종료 값은 10이므로 배열에 추가된 모든 함수는 이제 10으로 설정된 index

결과

올바른 폐쇄
n = 0
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9









폐쇄가 잘못되었습니다
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10











답변자 : Community Wiki


폐쇄에 대한 Wikipedia :

컴퓨터 과학에서 클로저는 해당 함수의 비로컬 이름(자유 변수)에 대한 참조 환경과 함께 함수입니다.

기술적으로 JavaScript 에서 모든 함수는 클로저 입니다. 항상 주변 범위에 정의된 변수에 액세스할 수 있습니다.

JavaScript의 범위 정의 구성은 다른 많은 언어와 같은 코드 블록이 아니라 함수이기 때문에 JavaScript에서 일반적으로 클로저 의미하는 것은 이미 실행된 주변 function 에 정의된 비지역 변수로 작업하는 함수 입니다.

클로저는 숨겨진 개인 데이터가 있는 함수를 만드는 데 자주 사용됩니다(항상 그런 것은 아닙니다).

 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.

ems

위의 예는 한 번 실행된 익명 함수를 사용하고 있습니다. 하지만 그럴 필요는 없습니다. 이름을 지정하고(예: mkdb ) 나중에 실행하여 호출될 때마다 데이터베이스 함수를 생성할 수 있습니다. 생성된 모든 함수에는 고유한 숨겨진 데이터베이스 개체가 있습니다. 클로저의 또 다른 사용 예는 함수를 반환하지 않고 다른 목적을 위한 여러 함수를 포함하는 객체를 반환하는 경우입니다. 각 함수는 동일한 데이터에 액세스할 수 있습니다.



답변자 : Community Wiki


클로저 작동 방식을 설명하기 위해 대화형 JavaScript 자습서를 작성했습니다. 폐쇄가 무엇입니까?

다음은 예 중 하나입니다.

 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


답변자 : Community Wiki


아이들은 부모가 떠난 후에도 부모와 나눈 비밀을 항상 기억할 것입니다. 이것이 함수를 위한 클로저입니다.

JavaScript 함수의 비밀은 개인 변수입니다.

 var parent = function() { var name = "Mary"; // secret }

호출할 때마다 지역 변수 "name"이 생성되고 이름이 "Mary"로 지정됩니다. 그리고 함수가 종료될 때마다 변수가 손실되고 이름이 잊혀집니다.

짐작할 수 있듯이 변수는 함수가 호출될 때마다 다시 생성되고 다른 사람이 알지 못하기 때문에 변수가 저장되는 비밀 장소가 있어야 합니다. 비밀의 방(Chamber of Secrets) 이나 스택(Stack) 또는 로컬 스코프(Local Scope) 라고 부를 수 있지만 실제로는 중요하지 않습니다. 우리는 그들이 기억 속 어딘가에 있다는 것을 압니다.

그러나 JavaScript에는 다른 함수 내부에서 생성된 함수가 부모의 지역 변수를 알고 살아있는 동안 유지할 수 있다는 매우 특별한 것이 있습니다.

 var parent = function() { var name = "Mary"; var child = function(childName) { // I can also see that "name" is "Mary" } }

따라서 우리가 부모 함수에 있는 한 비밀 장소에서 비밀 변수를 공유하는 하나 이상의 자식 기능을 만들 수 있습니다.

그러나 슬픈 점은 자식이 부모 함수의 private 변수이기도 하면 부모가 끝날 때 자식도 죽고 비밀도 함께 죽는다는 것입니다.

그러니 살기 위해서는 아이가 너무 늦기 전에 떠나야 한다

 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

그리고 이제 Mary가 "더 이상 달리지" 않더라도 그녀에 대한 기억은 사라지지 않으며 그녀의 아이는 항상 그녀의 이름과 함께 시간을 보내는 동안 공유한 기타 비밀을 기억할 것입니다.

그래서 당신이 아이를 "앨리스"라고 부르면 그녀는 대답할 것입니다.

 child("Alice") => "My name is Alice, child of Mary"

그게 전부입니다.



답변자 : Community Wiki


나는 대답이 왜 여기에서 그렇게 복잡한지 이해하지 못합니다.

폐쇄는 다음과 같습니다.

 var a = 42; function b() { return a; }

예. 당신은 아마 그것을 하루에 여러 번 사용합니다.


클로저가 특정 문제를 해결하기 위한 복잡한 설계 해킹이라고 믿을 이유가 없습니다. 아니요, 클로저는 함수가 선언된 위치(실행되지 않음)의 관점에서 더 높은 범위에서 오는 변수를 사용하는 것에 관한 것입니다.

이제 당신이 더 아름다운 될 수 있습니다 할 다른 답변을 볼 수 있습니다 무엇.



답변자 : Community Wiki


dlaliberte의 첫 번째 점에 대한 예:

내부 함수를 반환할 때만 클로저가 생성되는 것은 아닙니다. 사실, 둘러싸는 함수는 전혀 반환할 필요가 없습니다. 대신 내부 함수를 외부 범위의 변수에 할당하거나 즉시 사용할 수 있는 다른 함수에 인수로 전달할 수 있습니다. 따라서 내부 함수가 호출되자마자 해당 함수에 액세스할 수 있기 때문에 둘러싸는 함수의 클로저는 아마도 바깥쪽 함수가 호출될 당시 이미 존재했을 것입니다.

 var i; function foo(x) { var tmp = 3; i = function (y) { console.log(x + y + (++tmp)); } } foo(2); i(3);


답변자 : Community Wiki


클로저는 내부 함수가 외부 함수의 변수에 액세스할 수 있는 곳입니다. 이것은 아마도 클로저에 대해 얻을 수 있는 가장 간단한 한 줄 설명일 것입니다.



답변자 : Community Wiki


이미 많은 솔루션이 있다는 것을 알고 있지만 이 작고 간단한 스크립트가 개념을 설명하는 데 유용할 수 있다고 생각합니다.

 // 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


답변자 : Community Wiki


당신은 잠을 자고 Dan을 초대합니다. Dan에게 Xbox 컨트롤러 하나를 가져오라고 합니다.

댄은 폴을 초대합니다. Dan은 Paul에게 컨트롤러 하나를 가져오라고 요청합니다. 얼마나 많은 컨트롤러가 파티에 데려왔습니까?

 function sleepOver(howManyControllersToBring) { var numberOfDansControllers = howManyControllersToBring; return function danInvitedPaul(numberOfPaulsControllers) { var totalControllers = numberOfDansControllers + numberOfPaulsControllers; return totalControllers; } } var howManyControllersToBring = 1; var inviteDan = sleepOver(howManyControllersToBring); // The only reason Paul was invited is because Dan was invited. // So we set Paul's invitation = Dan's invitation. var danInvitedPaul = inviteDan(howManyControllersToBring); alert("There were " + danInvitedPaul + " controllers brought to the party.");


답변자 : Community Wiki


클로저 의 저자는 클로저가 필요한 이유와 클로저를 이해하는 데 필요한 LexicalEnvironment에 대해 설명하면서 클로저에 대해 잘 설명했습니다.
요약은 다음과 같습니다.

변수에 액세스했지만 로컬이 아닌 경우에는 어떻게 합니까? 여기처럼:

여기에 이미지 설명 입력

이 경우 인터프리터는 외부 LexicalEnvironment 개체에서 변수를 찾습니다.

프로세스는 다음 두 단계로 구성됩니다.

  1. 첫째, 함수 f가 생성될 때 빈 공간에 생성되지 않는다. 현재 LexicalEnvironment 개체가 있습니다. 위의 경우 창(a는 함수 생성 시 정의되지 않음)입니다.

여기에 이미지 설명 입력

함수가 생성되면 현재 LexicalEnvironment를 참조하는 [[Scope]]라는 숨겨진 속성을 얻습니다.

여기에 이미지 설명 입력

변수를 읽었지만 아무데도 찾을 수 없으면 오류가 생성됩니다.

중첩 함수

함수는 범위 체인이라고도 하는 LexicalEnvironments 체인을 형성하여 서로 중첩될 수 있습니다.

여기에 이미지 설명 입력

따라서 함수 g는 g, f에 액세스할 수 있습니다.

폐쇄

중첩 함수는 외부 함수가 완료된 후에도 계속 작동할 수 있습니다.

여기에 이미지 설명 입력

LexicalEnvironments 마크업:

여기에 이미지 설명 입력

보시다시피 this.say 는 사용자 개체의 속성이므로 사용자가 완료된 후에도 계속 유지됩니다.

그리고 기억한다면 this.say 가 생성될 때 (모든 함수처럼) this.say.[[Scope]] 를 얻습니다. 따라서 현재 사용자 실행의 LexicalEnvironment는 메모리에 남아 있습니다. User의 모든 변수도 속성이므로 일반적으로 정크 처리되지 않고 조심스럽게 보관됩니다.

요점은 내부 함수가 나중에 외부 변수에 액세스하려는 경우 액세스할 수 있도록 하는 것입니다.

요약:

  1. 내부 함수는 외부 LexicalEnvironment에 대한 참조를 유지합니다.
  2. 내부 함수는 외부 함수가 완료된 경우에도 언제든지 변수에 액세스할 수 있습니다.
  3. 브라우저는 이를 참조하는 내부 함수가 있을 때까지 LexicalEnvironment 및 모든 속성(변수)을 메모리에 유지합니다.

이것을 클로저라고 합니다.



답변자 : Community Wiki


JavaScript 함수는 다음 항목에 액세스할 수 있습니다.

  1. 인수
  2. 지역(즉, 지역 변수와 지역 함수)
  3. 다음을 포함하는 환경:
    • DOM을 포함한 전역
    • 외부 함수의 모든 것

함수가 환경에 액세스하면 함수는 클로저입니다.

외부 기능은 필요하지 않지만 여기에서 논의하지 않는 이점을 제공합니다. 해당 환경의 데이터에 액세스하여 클로저는 해당 데이터를 활성 상태로 유지합니다. 외부/내부 함수의 하위 사례에서 외부 함수는 로컬 데이터를 생성하고 결국 종료할 수 있지만 외부 함수가 종료된 후에도 내부 함수가 살아남는 경우 내부 함수는 외부 함수의 로컬 데이터를 유지합니다. 살아있는.

전역 환경을 사용하는 클로저의 예:

스택 오버플로 Vote-Up 및 Vote-Down 버튼 이벤트가 클로저로 구현된다고 상상해보십시오. (간단히 하기 위해 Answer Vote 버튼 배열이 아니라 StackOverflow의 Question Vote 버튼을 말하는 것입니다.)

사용자가 VoteUp 버튼을 클릭하면 voteUp_click 함수는 isVotedDown == true인지 여부를 확인하여 찬성할지 아니면 단순히 반대표를 취소할지 결정합니다. 함수 voteUp_click은 환경에 액세스하기 때문에 클로저입니다.

 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 }

이 네 가지 기능은 모두 환경에 액세스하므로 클로저입니다.



답변자 : Community Wiki


현재 어린 아이들을 가르치고 있는 6세의 아버지로서(그리고 정식 교육을 받지 않은 상대적인 코딩 초보자이므로 수정이 필요함), 수업은 실습 플레이를 통해 가장 잘 유지될 것이라고 생각합니다. 6세 어린이가 폐쇄가 무엇인지 이해할 준비가 되었다면 스스로 할 수 있는 나이가 된 것입니다. 코드를 jsfiddle.net에 붙여넣고 약간의 설명을 하고 고유한 노래를 만들기 위해 그대로 두는 것이 좋습니다. 아래의 설명 텍스트는 아마도 10세 어린이에게 더 적합할 것입니다.

 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);

지침

데이터: 데이터는 사실의 모음입니다. 숫자, 단어, 측정값, 관찰 또는 사물에 대한 설명일 수도 있습니다. 만질 수도, 냄새를 맡을 수도, 맛볼 수도 없습니다. 기록하고, 말하고, 들을 수 있습니다. 컴퓨터를 사용하여 촉각 냄새와 맛 을 만드는 데 사용할 수 있습니다. 코드를 사용하여 컴퓨터에서 유용하게 만들 수 있습니다.

CODE: 위의 모든 글을 code 라고 합니다. 자바스크립트로 작성되었습니다.

자바스크립트: 자바스크립트는 언어입니다. 영어나 프랑스어나 중국어처럼 언어도 있습니다. 컴퓨터 및 기타 전자 프로세서가 이해할 수 있는 언어가 많이 있습니다. JavaScript가 컴퓨터에서 이해되려면 인터프리터가 필요합니다. 러시아어만 하는 교사가 학교에서 수업을 가르치러 온다고 상상해 보십시오. 교사가 "все садятся"라고 말하면 학급에서는 이해하지 못합니다. 그러나 운 좋게도 여러분의 반에 "모두 앉으십시오"라는 의미라고 말하는 러시아 학생이 있습니다. 수업은 컴퓨터와 같고 러시아 학생은 통역사입니다. JavaScript의 경우 가장 일반적인 인터프리터를 브라우저라고 합니다.

브라우저: 웹사이트를 방문하기 위해 컴퓨터, 태블릿 또는 전화에서 인터넷에 연결할 때 브라우저를 사용합니다. 예를 들어 Internet Explorer, Chrome, Firefox 및 Safari가 있습니다. 브라우저는 JavaScript를 이해하고 컴퓨터가 수행해야 하는 작업을 알릴 수 있습니다. JavaScript 명령어를 함수라고 합니다.

기능: JavaScript의 기능은 공장과 같습니다. 내부에 단 하나의 기계만 있는 작은 공장일 수도 있습니다. 또는 각각 다른 작업을 수행하는 많은 기계가 있는 다른 많은 작은 공장을 포함할 수 있습니다. 실제 의류 공장에서는 많은 양의 천과 보빈이 들어가고 티셔츠와 청바지가 나올 수 있습니다. JavaScript 공장은 데이터만 처리하며 바느질, 구멍 뚫기 또는 금속 녹일 수 없습니다. JavaScript 팩토리에서는 데이터가 들어오고 데이터가 나옵니다.

이 모든 데이터는 다소 지루하게 들리지만 실제로는 매우 훌륭합니다. 로봇에게 저녁으로 무엇을 만들지 알려주는 기능이 있을 수 있습니다. 내가 당신과 당신의 친구를 내 집에 초대했다고 가정해 봅시다. 당신은 닭 다리를 가장 좋아하고, 나는 소시지를 좋아하고, 당신의 친구는 항상 당신이 원하는 것을 원하고 내 친구는 고기를 먹지 않습니다.

쇼핑할 시간이 없으므로 함수가 결정을 내리기 위해 냉장고에 무엇이 있는지 알아야 합니다. 각 재료는 조리 시간이 다르며 로봇이 동시에 모든 것을 뜨겁게 제공하기를 원합니다. 우리는 함수에 우리가 좋아하는 것에 대한 데이터를 제공해야 하고, 함수는 냉장고와 '대화'할 수 있고, 함수는 로봇을 제어할 수 있습니다.

함수에는 일반적으로 이름, 괄호 및 중괄호가 있습니다. 이와 같이:

 function cookMeal() { /* STUFF INSIDE THE FUNCTION */ }

/*...*/// 브라우저에서 코드 읽기를 중지합니다.

이름: 원하는 단어로 함수를 호출할 수 있습니다. "cookMeal"의 예는 일반적으로 두 단어를 결합하고 두 번째 단어를 시작 부분에 대문자로 표시하지만 반드시 필요한 것은 아닙니다. 그 안에 공백이 있을 수 없으며 단독으로 숫자가 될 수도 없습니다.

괄호: "괄호" 또는 () 는 JavaScript 기능 공장 문에 있는 편지함 또는 공장에 정보 패킷을 보내기 위한 거리의 우체통입니다. 때때로 우편함은 예를 들어 cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime) 으로 표시될 수 있으며, 이 경우 어떤 데이터를 제공해야 하는지 알 수 있습니다.

{} 처럼 보이는 "중괄호"는 우리 공장의 착색된 창입니다. 공장 내부에서는 밖을 볼 수 있지만 외부에서는 내부를 볼 수 없습니다.

위의 긴 코드 예

우리의 코드는 function 이라는 단어로 시작하므로 그것이 하나라는 것을 압니다! 그런 다음 함수의 이름이 노래 합니다. 그것은 함수가 무엇에 관한 것인지에 대한 내 자신의 설명입니다. 그런 다음 괄호 () . 함수에는 항상 괄호가 있습니다. 때로는 비어 있고 때로는 무언가가 들어 있습니다. 이 단어에는 (person) 이라는 단어가 있습니다. { 와 같은 중괄호가 있습니다. 이것은 sing() 함수의 시작을 표시합니다. 다음과 같이 sing() 의 끝을 표시하는 파트너가 있습니다 }

 function sing(person) { /* STUFF INSIDE THE FUNCTION */ }

따라서 이 함수는 노래와 관련이 있을 수 있으며 사람에 대한 데이터가 필요할 수 있습니다. 내부에는 해당 데이터로 작업을 수행하기 위한 지침이 있습니다.

이제 sing() 함수 다음에 코드의 끝 부분에 다음 줄이 있습니다.

 var person="an old lady";

VARIABLE: var 는 "변수"를 의미합니다. 변수는 봉투와 같습니다. 이 봉투의 외부에는 "사람"이라고 표시되어 있습니다. 내부에는 함수가 필요로 하는 정보가 담긴 종이 한 장이 들어 있으며, 일부 문자와 공백은 "노부인"이라는 문구를 만드는 문자열(문자열이라고 함)처럼 결합되어 있습니다. 우리의 봉투는 숫자(정수라고 함), 명령(함수라고 함), 목록( 배열 이라고 함)과 같은 다른 종류의 것들을 포함할 수 있습니다. 이 변수는 모든 중괄호 {} 외부에 작성되고 중괄호 안에 있을 때 착색된 창을 통해 밖을 볼 수 있기 때문에 이 변수는 코드의 어느 곳에서나 볼 수 있습니다. 우리는 이것을 '전역 변수'라고 부릅니다.

전역 변수 : 사람이 당신이 "젊은 사람"을 "할머니"에서 값을 변경하면, 그 사람이 다시 변경하기로 결정 때까지 젊은이되고 유지 것을 의미, 전역 변수입니다 다른 기능에 코드는 젊은이임을 알 수 있습니다. F12 버튼을 누르거나 옵션 설정을 확인하여 브라우저의 개발자 콘솔을 열고 "사람"을 입력하여 이 값이 무엇인지 확인합니다. person="a young man" 을 입력하여 변경한 다음 "person"을 다시 입력하여 변경되었는지 확인하십시오.

이 후에 우리는 라인을 가지고 있습니다

 sing(person);

이 줄은 개를 호출하는 것처럼 함수를 호출합니다.

"어서 노래해 , 와서 사람을 잡아라 !"

브라우저가 JavaScript 코드를 로드하면 이 행에 도달하면 기능이 시작됩니다. 브라우저가 그것을 실행하는 데 필요한 모든 정보를 가지고 있는지 확인하기 위해 끝에 줄을 넣었습니다.

기능은 동작을 정의합니다. 주요 기능은 노래에 관한 것입니다. 여기에는 노래의 각 구절에 적용되는 사람에 대한 노래에 적용되는 firstPart 라는 변수가 포함되어 있습니다. 콘솔에 firstPart 를 입력하면 변수가 함수에 잠겨 있기 때문에 답을 얻을 수 없습니다. 브라우저는 중괄호의 착색된 창 내부를 볼 수 없습니다.

클로저: 클로저는 큰 sing() 함수 안에 있는 더 작은 함수입니다. 큰 공장 안의 작은 공장. 각각은 자체 중괄호를 가지고 있어 내부의 변수를 외부에서 볼 수 없습니다. 이것이 변수의 이름( creatureresult )이 클로저에서 반복될 수 있지만 다른 값을 갖는 이유입니다. 콘솔 창에 이러한 변수 이름을 입력하면 두 개의 착색된 창에 의해 숨겨져 있기 때문에 값을 얻을 수 없습니다.

클로저는 모두 firstPart 라는 sing() 함수의 변수가 무엇인지 알고 있습니다. 왜냐하면 그들은 착색된 창에서 밖을 볼 수 있기 때문입니다.

폐쇄 후 줄을 온다

 fly(); spider(); bird(); cat();

sing() 함수는 주어진 순서대로 이러한 각 함수를 호출합니다. 그러면 sing() 함수의 작업이 완료됩니다.



답변자 : Community Wiki


좋아요, 6살짜리 아이와 이야기할 때, 저는 아마도 다음과 같은 연상법을 사용할 것입니다.

상상해 보세요. 당신이 집 전체에서 남동생과 자매들과 놀고 있고 장난감을 가지고 돌아다니며 그 중 일부를 형의 방으로 가져왔다고 상상해 보십시오. 잠시 후 형이 학교에서 돌아와 자기 방으로 갔고, 그는 그 안에 잠겼기 때문에 이제 더 이상 거기에 남겨둔 장난감에 직접 접근할 수 없습니다. 하지만 당신은 문을 두드리고 당신의 형제에게 그 장난감을 달라고 요청할 수 있습니다. 이것을 장난감 폐쇄 라고 합니다. 당신의 형제는 당신을 위해 그것을 만들어 냈고 그는 이제 외부 범위에 있습니다.

외풍에 의해 문이 잠겨 있고 안에 아무도 없는 상황(일반 기능 실행) 후 국부적으로 화재가 발생하여 방을 태우고(쓰레기 수집기:D) 새 방을 짓고 지금은 나갈 수 있는 상황과 비교 거기에 다른 장난감(새로운 기능 인스턴스)이 있지만 첫 번째 방 인스턴스에 남아 있던 것과 같은 장난감은 절대 얻지 못합니다.

고급 어린이를 위해 나는 다음과 같은 것을 넣을 것입니다. 완벽하지는 않지만 이것이 무엇인지 느끼게 합니다.

 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

보시다시피, 방에 남아 있는 장난감은 방이 잠겨 있어도 형제를 통해 계속 액세스할 수 있습니다. 여기 그것을 가지고 놀 수 있는 jsbin이 있습니다.



답변자 : Community Wiki


6세 아동에 대한 답변(함수가 무엇인지, 변수가 무엇인지, 데이터가 무엇인지 알고 있다고 가정):

함수는 데이터를 반환할 수 있습니다. 함수에서 반환할 수 있는 데이터의 한 종류는 다른 함수입니다. 새 함수가 반환되면 해당 함수를 만든 함수에 사용된 모든 변수와 인수가 사라지지 않습니다. 대신 해당 상위 함수가 "닫힙니다." 다시 말해, 내부를 볼 수 없으며 반환된 함수를 제외하고 사용된 변수를 볼 수 없습니다. 그 새로운 함수는 그것을 생성한 함수 내부를 되돌아보고 그 내부의 데이터를 볼 수 있는 특별한 능력을 가지고 있습니다.

 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

이를 설명하는 또 다른 정말 간단한 방법은 범위 측면에서입니다.

더 큰 범위 안에 더 작은 범위를 만들 때마다 더 작은 범위는 항상 더 큰 범위에 있는 것을 볼 수 있습니다.



답변자 : Community Wiki


JavaScript의 함수는 C 언어에서와 같이 일련의 명령에 대한 참조일 뿐만 아니라 사용하는 모든 비지역 변수(캡처된 변수)에 대한 참조로 구성된 숨겨진 데이터 구조도 포함합니다. 이러한 두 부분 함수를 클로저라고 합니다. JavaScript의 모든 함수는 클로저로 간주될 수 있습니다.

클로저는 상태가 있는 함수입니다. "this"는 함수에 대한 상태도 제공하지만 함수와 "this"는 별도의 객체라는 점에서 "this"와 다소 유사합니다("this"는 멋진 매개변수이며 영구적으로 바인딩하는 유일한 방법입니다. 함수는 클로저를 생성하는 것입니다). "this"와 함수는 항상 별도로 존재하지만 함수는 클로저와 분리될 수 없으며 언어는 캡처된 변수에 액세스할 수 있는 수단을 제공하지 않습니다.

어휘적으로 중첩된 함수에 의해 참조되는 이러한 모든 외부 변수는 실제로 어휘적으로 둘러싸는 함수의 체인에 있는 지역 변수이고(전역 변수는 일부 루트 함수의 지역 변수로 가정될 수 있음) 함수의 모든 단일 실행은 다음의 새로운 인스턴스를 생성합니다. 지역 변수에 따라 중첩 함수를 반환하는(또는 콜백으로 등록하는 것과 같이 전달하는 것과 같은) 함수의 모든 실행은 새로운 클로저를 생성합니다(실행을 나타내는 참조된 비지역 변수의 고유한 잠재적으로 고유한 집합 포함). 문맥).

또한 JavaScript의 지역 변수는 스택 프레임이 아닌 힙에서 생성되고 참조하는 사람이 없을 때만 소멸된다는 점을 이해해야 합니다. 함수가 반환될 때 지역 변수에 대한 참조는 감소하지만 현재 실행 중에 해당 변수가 클로저의 일부가 되고 여전히 어휘적으로 중첩된 함수에 의해 참조되는 경우에는 여전히 null이 아닐 수 있습니다(참조하는 경우에만 발생할 수 있습니다. 이러한 중첩 함수는 반환되거나 일부 외부 코드로 전송됨).

예:

 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();


답변자 : Community Wiki


아마도 6세 아이들 중 가장 조숙한 아이들을 제외하고는 조금 더 있을 수 있지만, JavaScript에서 클로저 개념을 만드는 데 도움이 된 몇 가지 예가 저를 위해 클릭되었습니다.

클로저는 다른 함수의 범위(변수와 함수)에 접근할 수 있는 함수입니다. 클로저를 만드는 가장 쉬운 방법은 함수 내에서 함수를 사용하는 것입니다. 그 이유는 JavaScript에서 함수는 항상 포함하는 함수의 범위에 액세스할 수 있기 때문입니다.

 function outerFunction() { var outerVar = "monkey"; function innerFunction() { alert(outerVar); } innerFunction(); } outerFunction();

경고: 원숭이

위의 예에서 innerFunction을 차례로 호출하는 outerFunction이 호출됩니다. innerFunction에서 outerVar를 사용할 수 있는 방법에 유의하십시오. 이는 outerVar 값을 올바르게 경고하는 것으로 증명됩니다.

이제 다음을 고려하십시오.

 function outerFunction() { var outerVar = "monkey"; function innerFunction() { return outerVar; } return innerFunction; } var referenceToInnerFunction = outerFunction(); alert(referenceToInnerFunction());

경고: 원숭이

referenceToInnerFunction은 innerFunction에 대한 참조를 단순히 반환하는 outerFunction()으로 설정됩니다. referenceToInnerFunction이 호출되면 outerVar가 반환됩니다. 다시, 위와 같이 innerFunction이 outerFunction의 변수인 outerVar에 액세스할 수 있음을 보여줍니다. 또한, outerFunction 실행이 완료된 후에도 이 액세스 권한을 유지한다는 점은 흥미롭습니다.

그리고 여기에서 상황이 정말 흥미로워집니다. outerFunction을 제거하고 null로 설정하면 referenceToInnerFunction이 outerVar 값에 대한 액세스 권한을 잃는다고 생각할 수 있습니다. 그러나 이것은 사실이 아닙니다.

 function outerFunction() { var outerVar = "monkey"; function innerFunction() { return outerVar; } return innerFunction; } var referenceToInnerFunction = outerFunction(); alert(referenceToInnerFunction()); outerFunction = null; alert(referenceToInnerFunction());

경고: 원숭이 경고: 원숭이

그러나 이것이 어떻게 그렇습니까? outerFunction이 null로 설정된 지금 referenceToInnerFunction이 여전히 outerVar의 값을 알 수 있습니까?

referenceToInnerFunction이 여전히 outerVar의 값에 액세스할 수 있는 이유는 innerFunction을 outerFunction 내부에 배치하여 클로저를 처음 생성했을 때 innerFunction이 outerFunction의 범위(변수 및 함수)에 대한 참조를 범위 체인에 추가했기 때문입니다. 이것이 의미하는 바는 innerFunction에 outerVar를 포함한 모든 outerFunction 변수에 대한 포인터 또는 참조가 있다는 것입니다. 따라서 outerFunction이 실행을 완료했거나 삭제되거나 null로 설정된 경우에도 outerVar와 같은 해당 범위의 변수는 반환된 innerFunction 부분에서 해당 변수에 대한 미해결 참조로 인해 메모리에 남아 있습니다. referenceToInnerFunction. 메모리에서 outerVar 및 나머지 outerFunction 변수를 진정으로 해제하려면 referenceToInnerFunction도 null로 설정하여 이러한 뛰어난 참조를 제거해야 합니다.

///////////

클로저에 대해 주목해야 할 두 가지 다른 사항. 첫째, 클로저는 항상 포함하는 함수의 마지막 값에 액세스할 수 있습니다.

 function outerFunction() { var outerVar = "monkey"; function innerFunction() { alert(outerVar); } outerVar = "gorilla"; innerFunction(); } outerFunction();

경고: 고릴라

둘째, 클로저가 생성될 때, 클로저를 둘러싼 함수의 모든 변수와 함수에 대한 참조를 유지합니다. 고르고 선택하는 것이 아닙니다. 그러나 클로저는 메모리 집약적일 수 있으므로 드물게 사용하거나 최소한 주의해서 사용해야 합니다. 포함하는 함수가 실행을 마친 후에도 많은 변수를 메모리에 보관할 수 있습니다.



답변자 : Community Wiki


Mozilla Closures 페이지 를 가리키기만 하면 됩니다. 내가 찾은 클로저 기본 사항과 실용적인 사용법에 대한 가장 간결하고 간단한 설명입니다. JavaScript를 배우는 모든 사람에게 적극 권장됩니다.

그리고 예, 저는 6세에게도 이 책을 권하고 싶습니다. 6세 어린이가 폐쇄에 대해 배우고 있다면 기사에 제공된 간결하고 간단한 설명을 이해할 준비가 되어 있는 것이 논리적입니다.



출처 : Here


출처 : http:www.stackoverflow.com/questions/111102/how-do-javascript-closures-work">

반응형