JS 학습의 클로저에 대한 심층적인 이해

高洛峰
풀어 주다: 2016-12-06 11:21:16
원래의
948명이 탐색했습니다.

JS에서 클로저는 상대적으로 이해하기 어려운 부분입니다. 특히 프로그래밍 기초가 없는 사람들에게는 더욱 그렇습니다.

사실 클로저에 관해 주의할 점은 몇 가지밖에 없습니다. 모두 이해한다면 극복하는 것은 어렵지 않습니다. 클로저의 몇 가지 기본 원칙에 대해 이야기해 보겠습니다.

클로저의 개념

클로저는 생성된 함수에서 함수와 범위 객체의 조합입니다. (범위 개체에 대해서는 아래에서 설명합니다.)

좀 더 간단히 말하면 "하나 이상의 함수가 함수에 중첩되어 있는 한 이를 클로저라고 부를 수 있습니다.

function A() {
 var i = 5;
 return function() {
  console.log('i = '+i);
 }
}
 
var a = A();
a(); // i = 5
로그인 후 복사

클로저의 원리

1. 클로저 함수에 의해 외부 함수의 로컬 변수가 호출되는 경우 외부 함수 실행이 완료된 후 즉시 재활용됩니다.

우리는 언어에 관계없이 운영 체제에는 초과 할당 공간을 재활용하여 메모리를 줄이는 가비지 수집 메커니즘이 있다는 것을 알고 있습니다. 함수 호출이 완료되면 함수 내부의 지역 변수가 재활용 메커니즘에 의해 재활용됩니다.

위의 예를 예로 들어보겠습니다. 외부 함수 A가 호출되면 A의 지역 변수 i는 운영 체제에 의해 재활용되며 클로저를 사용하면 존재하지 않습니다. 결과는 더 이상 그렇지 않으며 재활용되지 않습니다. 상상해 보세요. i가 재활용되면 반환된 함수가 정의되지 않은 것으로 인쇄되지 않을까요?

나는 왜 재활용되지 않았나요?

JavaScript가 함수를 실행할 때 범위 객체를 생성하고 함수에 지역 변수를 저장하며(함수의 형식 매개변수도 지역 변수임) 함수에 전달된 변수와 함께 초기화됩니다. .

따라서 A가 호출되면 범위 개체가 생성되고 이를 Aa라고 부르겠습니다. 그러면 이 Aa는 다음과 같아야 합니다. Aa { i: 5 }; A 함수가 함수를 반환한 후 A가 실행됩니다. . Aa 객체는 재활용되어야 하지만 반환된 함수가 Aa의 i 특성을 사용하기 때문에 반환된 함수는 Aa에 대한 참조를 저장하므로 Aa는 재활용되지 않습니다.

그러므로 범위 객체를 이해하면 클로저를 만났을 때 함수 호출이 완료될 때 함수의 지역 변수가 즉시 재활용되지 않는 이유를 이해할 수 있습니다.

또 다른 예:

function A(age) {
 var name = 'wind';
 var sayHello = function() {
  console.log('hello, '+name+', you are '+age+' years old!');
 };
 return sayHello;
}
var wind = A(20);
wind(); // hello, wind, you are 20 years old!
로그인 후 복사

스코프 객체 Ww가 무엇인지 알 수 있나요?

Ww{ age: 'wind' };

2. 외부 함수가 호출될 때마다 이전 클로저가 여전히 존재하며 독립적입니다. 서로의 영향.

3. 동일한 클로저는 마지막 상태를 유지하며, 다시 호출되면 마지막 상태를 기준으로 합니다.

외부 함수가 호출될 때마다 생성되는 범위 개체가 다르다고 생각하면 됩니다. 위의 예에서는 전달하는 매개변수 age가 매번 다르기 때문에 생성되는 개체도 다릅니다. 매번.

외부 함수가 호출될 때마다 새로운 범위 개체가 생성됩니다.

function A() {
 var num = 42;
 return function() { console.log(num++); }
}
var a = A();
a(); // 42
a(); // 43
 
var b = A(); // 重新调用A(),形成新闭包
b(); // 42
로그인 후 복사

이 코드를 사용하면 두 가지를 발견할 수 있습니다. 먼저 a()를 두 번 호출하면 num이 자동으로 변경됩니다. 원래 값을 추가합니다. 이는 동일한 클로저가 마지막 상태를 유지하며 다시 호출될 때 마지막 상태를 기반으로 한다는 것을 의미합니다. 2. b()의 결과는 42이며 이는 새로운 클로저이며 다른 클로저의 영향을 받지 않음을 나타냅니다.

이렇게 생각하면 마치 비눗방울을 불 때마다(외부 함수 호출) 새로운 비눗방울(마개)이 생성될 수 있습니다. 동시에 두 개의 비누 방울은 서로 영향을 미치지 않습니다.

4. 외부 함수에 존재하는 여러 함수 "살고 죽는다"

다음 세 함수는 동시에 선언되며 모두 속성(지역 변수)에 대한 작업을 수행할 수 있습니다. 범위 개체 액세스 및 작업.

var fun1, fun2, fun3;
function A() {
 var num = 42;
 fun1 = function() { console.log(num); }
 fun2 = function() { num++; }
 fun3 = function() { num--; }
}
 
A();
fun1();  // 42
fun2();
fun2();
fun1();  // 44
fun3();
fun1();  //43
 
var old = fun1;
 
A();
fun1();  // 42
old();  // 43  上一个闭包的fun1()
로그인 후 복사

함수는 여러 개의 반환값을 가질 수 없기 때문에 전역 변수를 사용했습니다. A()를 두 번째 호출할 때 새로운 클로저가 생성되는 것을 다시 볼 수 있습니다.

클로저가 루프 변수를 만날 때

클로저에 대해 이야기할 때 클로저가 루프 변수를 만날 때의 상황에 대해 이야기해야 합니다.

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    var item = &#39;item&#39; + i;
    result.push( function() {console.log(item + &#39; &#39; + arr[i])} );
  }
  return result;
}
 
var fnlist = buildArr([1,2,3]);
fnlist[0](); // item2 undefined
fnlist[1](); // item2 undefined
fnlist[2](); // item2 undefined
로그인 후 복사

어떻게 이런 일이 일어날 수 있을까요? 우리가 구상하는 세 가지 출력은 item0 1, item1 2, item2 3이어야 합니다. 반환된 결과 배열에 정의되지 않은 item2가 세 개 저장되어 있는 이유는 무엇입니까?

클로저가 루프 변수를 만나면 루프가 끝난 후 변수 값이 균일하게 저장되는 것으로 나타났습니다. 위의 예를 보면 i가 루프 변수이고 루프가 끝나면 i가 됩니다. i++ 이후 3. Arr[3]에는 값이 없으므로 정의되지 않습니다. 어떤 사람들은 다음과 같이 궁금해할 수 있습니다. item2의 값은 왜 item3이어야 하지 않나요? 마지막 루프, 즉 i = 2일 때 item의 값은 item2이고, i++, i = 3일 때, 루프 조건이 충족되지 않고 이때의 item 값이 결정된다는 점에 유의하세요. 이므로 이때 arr[i]는 arr[3]이고 item은 item2입니다. 이것이 이해할 수 있습니까? 코드를 다음과 같이 변경하면 이해가 됩니다.

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    result.push( function() {console.log(&#39;item&#39; + i + &#39; &#39; + arr[i])} );
  }
  return result;
}
 
var fnlist = buildArr([1,2,3]);
fnlist[1](); // item3 undefined
로그인 후 복사

그렇다면 문제는 어떻게 수정해야 할까요? 코드를 살펴보겠습니다.

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    result.push( (function(n) {
      return function() {
       var item = &#39;item&#39; + n;
       console.log(item + &#39; &#39; + arr[n]);
      }
    })(i));
  }
  return result;
}
 
var fnlist = buildArr([1,2,3]);
fnlist[0](); // item0 1
fnlist[1](); // item1 2
fnlist[2](); // item2 3
로그인 후 복사

자체 실행 기능을 사용하여 i를 바인딩하면 i의 모든 상태가 저장되고 응답은 예상한 것과 동일하게 됩니다.

그래서 앞으로 클로저를 사용할 때 루프 변수를 만나면 이를 바인딩하기 위해 자체 실행 함수를 사용하는 것을 습관적으로 생각해야 합니다.

위 내용은 클로저에 대한 저의 이해입니다. 의견이나 제안 사항이 있으면 댓글 영역에서 더 많은 소통을 할 수 있기를 바랍니다. 서로 감사하고 격려해 주세요.


관련 라벨:
js
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿