프론트 엔드 학습 과정에서 필연적으로 많은 문제에 직면하게 되므로 오늘은 초보자의 관점에서 두 가지 질문에 대해 이야기해 보겠습니다.
클로저란 무엇입니까?
클로저의 기능은 무엇입니까?
사실, 클로저는 JavaScript를 배울 때 어디에나 있으므로 이를 인식하고 받아들일 수만 있으면 됩니다. 클로저는 사용할 새로운 구문이나 패턴을 학습해야 하는 도구가 아닙니다. 클로저는 어휘 범위를 기반으로 코드를 작성하는 자연스러운 결과입니다. 코드를 작성할 때 의도적으로 클로저를 생성할 필요는 거의 없습니다.
이 때 이미 많은 친구들이 속으로 중얼거리고 있을 거라 생각합니다. 이 어휘 범위는 무엇입니까? 당황하지 말고 천천히 들어보세요. 한마디로 어휘 범위는 어휘 단계에서 정의되는 범위입니다. 즉, 어휘 범위는 코드를 작성할 때 변수와 블록 수준 범위를 배치하는 위치에 따라 결정되므로 어휘 분석기가 코드를 처리할 때(대부분의 경우) 범위는 변경되지 않습니다. ——"당신이 모르는 자바스크립트"
먼저 예를 들어보겠습니다:
function test(){ var arr = [] for(var i=0;i<10;i++){ arr[i]=함수(){ console.log(i); } } 도착 도착 } var myArr = 테스트() // myArr[0]() // myArr[1]() // ... for(var j = 0; j < 10; j++){ myArr[j]() } //지루함을 피하기 위해 여기서는 두 번째 루프를 사용하여 테스트 함수의 첫 번째 루프에서 함수를 호출하고 10개의 결과를 출력합니다.
먼저 이 코드를 분석해 보겠습니다. 이 코드가 실행되면 상식적으로 0부터 9까지 10개의 숫자를 순서대로 인쇄하지만 for 루프는 실행하는 데 시간이 걸리지 않습니다(마이크로초 단위로 무시됨). );} 이때 배열에 있는 함수는 실행되지 않습니다. var myArr = test()가 테스트 함수를 호출하면 for 루프의 실행 시간이 무시되므로 이때 i는 이미 10이므로 무엇입니까? 인쇄된 것은 10점 만점에 10점입니다.
이쯤 되면 누군가가 '이것이 우리가 이야기할 클로저와 무슨 관계가 있지?'라고 물을 것이라고 생각합니다. 그렇다면 이 코드를 약간 수정하여 누산기로 바꾸면 어떻게 구현할 수 있을까요?
나는 이때에 '그게 간단하지 않니?'라고 말할 거물들이 있을 것이라고 믿습니다.
var 정의를 let 정의로 변경하여 첫 번째 for 루프가 블록 수준 범위가 된 다음 누산기가 될 수 있도록 합니다. 물론 문제는 없지만
오늘 우리가 이야기할 내용은 ES5에서 누산기를 구현하는 방법입니다. 그러면 다음 코드를 살펴보겠습니다.
function test(){ var arr = [] for(var i=0;i<10;i++){ (함수(j){ arr[j]=함수(){ console.log(j); } })(나) } 도착 도착 } var myArr = 테스트() for(var j = 0; j < 10; j++){ myArr[j]() }
주의 깊은 친구들은 이것이 루프 내의 함수 본문을 자체 실행 함수로 변경하는 것임을 확실히 알 수 있지만 이때 출력 결과는 0부터 9까지 10개의 숫자를 순차적으로 출력하는 것이며 여기에는 클로저 패키지가 포함됩니다. 이 코드를 실행하기 시작하면 두 번째 for 루프가 10번 호출됩니다. 각 자체 실행 함수가 실행될 때 자체 실행 함수의 AO 객체가 생성됩니다. 속성 이름은 j입니다. 일반적으로 실행 함수가 실행된 후 해당 AO 객체가 소멸되어야 합니다. 그러나 myarr[j]()가 실행되면 범위 체인의 최상위에 있는 arr[j]의 AO 객체가 삭제됩니다. 이제 속성 이름 j를 찾았지만 찾지 못했습니다. 범위 체인을 검색하여 자체 실행 함수의 AO 개체에서 찾았습니다. 따라서 자체 실행 함수가 끝나면 해당 AO가 발생합니다. 객체는 가비지 수집 메커니즘에 의해 재활용되지 않습니다. 그렇지 않으면 myarr[j] ()가 실행될 때 오류가 보고되고 클로저가 형성됩니다.
함수 a(){를
살펴보겠습니다.
함수 b(){ 변수 bbb = 234 console.log(aaa); } 변수 aaa = 123 return b // b는 a에서 태어났지만 저장되었습니다.} 바르글로브 = 100 var 데모 = a()먼저 사전 컴파일을 사용하여
전역 GO 객체를 정의하고 전역 변수 선언을 찾아 GO의 속성 이름으로 사용합니다
.
함수 선언의 경우 함수 이름이 GO 개체의 속성 이름으로 사용되고 값이 함수 본문에 할당됩니다. 이때는 GO{ glob: undefed--->100; deco: undefine; a: fa(){} }; 그런 다음 AO{ aaa: undefed--->123;b: fb() 를 생성해야 합니다. function a {} }, 그리고 마지막으로 함수 a에서 함수 b를 사전 컴파일하여 b { b: undefed--->234}의 AO를 생성합니다. 이때 범위 체인의 순서는 1입니다. 함수 b의 AO 개체; 2. 함수 a의 AO 객체 3. 전역 GO 객체. 함수 b에서 aaa를 인쇄하면 범위 체인의 맨 위에서 시작합니다. 함수 b의 AO 객체에 aaa가 없으면 범위 체인을 따라 아래로 검색하여 두 번째 수준 함수 a의 AO를 찾습니다. .aaa의 값을 123으로 구하고 그 결과를 출력하는 것이 목적입니다.
프리컴파일 관점에서 분석하지 않았다면, 이때 aaa가 오류를 보고해야 한다고 생각할 것입니다. var deco = a()가 실행되면 a 함수의 실행이 끝나면 그에 해당하는 AO 객체가 나타납니다. 상식적인 분석에 따르면, 데모를 실행하면 스코프 체인은 b의 AO 객체와 GO 객체를 생성해야 합니다. 이때 b의 AO 객체만 있고 a의 AO 객체는 없습니다. aaa의 값이 출력되어서는 안되는데, 이때 aaa의 값이 123으로 a의 AO 객체가 소멸되지 않았다는 뜻인데 왜 그럴까요? 그 이유는 여기서 클로저가 생성되기 때문입니다. var deco = a()의 실행이 완료되면 가비지 수집 메커니즘이 "형제님, 함수 실행을 마친 것 같습니다. 실행 중인 메모리를 나에게 해제할 수 있습니까?"라고 묻습니다. 그런데 이때 함수 a는 무기력하게 고개를 저을 수밖에 없었고, 형님, 실행을 완료했는지 잘 모르겠습니다. 실행한 후 b를 생성했는데 b가 내 통제 대상이 아니어서 그렇습니다. b.가 호출되었는지 확실하지 않으므로 실행을 완료했는지 잘 모르겠습니다. 가비지 수집 메커니즘을 모르므로 완료되지 않은 것을 재활용하지 않습니다. 오류를 보고해야 하므로 이때 The AO 객체는 재활용되지 않습니다.
이 두 가지 예를 통해 여러분은 이미 클로저에 대한 일반적인 이해를 가지셨으리라 믿습니다. 다음으로 클로저의 기능에 대해 이야기해 보겠습니다.
클로저의 기능
은 공용 변수를 구현하는 것입니다
- . 예를 들어,
- 전역 변수의 오염을 방지하기 위해 속성의
- 누산기(3.js)를
- 캡슐화, 사유화 및 모듈식 개발을 달성하기 위해
- 할 수 있습니다.
- 캐시
3.js).
변수 개수 = 0 함수 추가() { 반환 횟수++ } console.log(추가()); console.log(추가()); console.log(add());
이것은 상대적으로 일반적인 축적 코드이지만, 인턴쉽 중이나 직장에서 회사에서 누산기를 모듈형 코드로 캡슐화하도록 요구하는 경우, 모듈 전역 변수 정의를 가능한 한 피하려고 노력하지만, 전역 변수를 정의하지 않고 어떻게 이를 달성할 수 있습니까? 이때 클로저를 사용할 수 있습니다.
함수 추가() { 변수 개수 = 0 함수 a() { ++카운트 console.log(count); } 반환하다 } var res = 추가() 해상도() 해상도() //add 함수가 종료된 후 add의 AO 객체는 소멸되지 않습니다. 왜냐하면 add 함수가 실행된 후 반환된 a는 호출 여부를 알지 못한 채 클로저를 형성하므로 사용하지 않고도 모듈에 캡슐화할 수 있기 때문입니다. 전역 변수.
현재로서는 클로저에 대해 피상적으로만 이해하고 있습니다. 클로저에 대한 후속 기사를 시청해 주셔서 감사합니다. 바로잡고, 함께 발전해 나가세요.