다시 채우다:
폐쇄는 JavaScript 언어와 그 기능의 어려움입니다. 많은 고급 응용 프로그램은 구현을 위해 폐쇄에 의존합니다.
폐쇄 기능
클로저에는 세 가지 특성이 있습니다.
1. 기능 중첩 함수
2. 함수는 내부의 외부 매개 변수와 변수를 참조 할 수 있습니다.
3. 가비지 수집 메커니즘에 의해 매개 변수와 변수가 수집되지 않습니다.
폐쇄의 정의 및 장점과 단점
클로저는 다른 함수의 범위에서 변수에 액세스 할 수있는 함수를 나타냅니다. 클로저를 생성하는 가장 일반적인 방법은 한 기능 내에서 다른 함수를 만들고 다른 함수를 통해이 함수의 로컬 변수에 액세스하는 것입니다.
폐쇄의 단점은 상주 메모리이며 메모리 사용량을 증가시키고 부적절한 사용은 쉽게 메모리 누출로 이어질 수 있다는 것입니다.
클로저는 JavaScript 언어의 주요 특징입니다. 폐쇄의 주요 적용은 주로 개인 방법 및 변수 설계입니다.
일반적인 기능이 실행 된 후, 로컬 액티브 객체가 파괴되고 전역 범위 만 메모리에 저장됩니다. 그러나 폐쇄 상황은 다릅니다!
주제에 대해 이야기합니다
1. 폐쇄의 정의?
폐쇄에 대한 몇 가지 정의를 살펴 보겠습니다.
1. Closure는 다른 함수 범위에서 변수에 액세스 할 수있는 권한이있는 함수를 나타냅니다.
2. 기능 객체는 범위 체인을 통해 연결 될 수 있으며 기능 본문 내부의 변수는 함수의 범위에 저장 될 수 있습니다. 이 특성을 '클로저'라고합니다.
3. 내부 함수는이를 정의하는 외부 함수의 매개 변수와 변수에 액세스 할 수 있습니다 (이 및 인수 제외).
JS 폐쇄의 개념을 체계적으로 배우려면 Wulin.com 웹 사이트의 JS 전자 책 열을 참조하여 학습 할 수 있습니다.
정의를 요약합시다
1. 외부 함수 범위에서 변수의 함수에 액세스 할 수 있습니다.
2. 내부 함수에 의해 액세스 된 외부 함수의 변수는 재활용되지 않고 외부 기능의 범위 내에서 저장 될 수 있습니다. 이것은 핵심입니다. 나중에 폐쇄를 마치면 생각해야합니다. 우리는 폐쇄에 의해 참조 된 변수에 중점을 두어야합니다.
간단한 폐쇄를 만듭니다
var sayname = function () {var name = 'jozo'; return function () {alert (name);}}; var say = sayname (); 말하다();다음 두 문장을 해석합시다.
• var says = sayname () : 변수에 저장된 익명의 내부 함수를 반환하고 외부 함수의 변수 이름을 나타냅니다. 쓰레기 수집 메커니즘으로 인해 SayName 함수가 실행 된 후 변수 이름은 파괴되지 않습니다.
• () : 반환 된 내부 함수를 실행하고 여전히 변수 이름 및 출력 'jozo'에 액세스하십시오.
2. 스코프 체인 폐쇄
범위 체인을 이해하는 것도 폐쇄를 이해하는 데 도움이됩니다.
범위의 변수 검색 방법은 매우 친숙해야하지만 실제로는 범위 체인을 따라 위쪽으로 검색하는 것입니다.
함수가 호출 될 때 :
1. 먼저 실행 컨텍스트와 해당 스코프 체인을 만듭니다.
2. 기능의 활성 객체 (활성화 객체)에 인수 및 기타 지정된 매개 변수 값 추가
스코프 체인 : 현재 함수의 활성 객체는 우선 순위가 가장 높으며 외부 함수의 활성 객체가 이어지고 외부 함수의 외부 함수의 활성 객체는 스코프 체인의 끝까지 순서대로 감소합니다. 우선 순위는 변수 검색 순서입니다.
먼저 일반 스코프 체인을 살펴 보겠습니다.
함수 sayname (name) {return name;} var say = sayname ( 'jozo');이 코드는 두 가지 범위를 포함합니다. a. 글로벌 범위; b.sayname 함수 범위, 즉 두 가지 변수 객체 만 있습니다. 해당 실행 환경에 실행되면 가변 객체는 활성 객체가되어 실행 환경의 스코프 체인의 프론트 엔드로 밀려납니다. 즉, 최우선 순위가됩니다. 사진과 대화하십시오 :
이 사진은 JS Advanced Programming Books에서도 구입할 수 있으며 모든 것을 다시 작성했습니다.
SayName () 함수를 만들 때, 가변 객체를 미리 포함하는 스코프 체인, 즉 그림에서 1에 의해 인덱스 된 스코프 체인이 생성되고 내부 [[scope]] 속성에 저장됩니다. SayName () 함수가 호출되면 실행 환경이 생성되고 함수의 [[scope]] 속성에서 객체를 복사하여 스코프 체인이 구축됩니다. 그 후, 또 다른 활성 객체 (그림에서 0으로 인덱싱)가 생성되어 실행 환경의 스코프 체인의 전면으로 밀립니다.
일반적으로 함수가 실행되면 로컬 활성 객체가 파괴되고 전역 범위 만 메모리에 저장됩니다. 그러나 폐쇄 상황은 다릅니다.
폐쇄 범위 체인을 살펴 보겠습니다.
함수 sayname (name) {return function () {return name;}} var say = sayname ( 'jozo');이 클로저 인스턴스는 이전 예보다 익명 기능에 대한 하나의 범위가 하나 더 있습니다.
익명 함수가 SayName () 함수에서 반환 된 후 스코프 체인은 활성 객체 및 SayName () 함수를 포함하는 전역 변수 객체로 초기화됩니다. 이러한 방식으로 익명 함수는 SayName ()에 정의 된 모든 변수 및 매개 변수에 액세스 할 수 있습니다. 더 중요한 것은, SayName () 함수를 실행 한 후에 익명 함수의 스코프 체인이 여전히 활성 객체를 지칭하기 때문에 활성 객체는 파괴되지 않을 것입니다. 다시 말해서, SayName () 함수의 실행 후에 실행 환경의 스코프 체인이 파괴되지만 익명 함수가 파괴 될 것이라는 사실을 알면서 활성 객체가 메모리에 남아있을 것입니다. 이것은 또한 나중에 논의 될 메모리 누출 문제이기도합니다.
나는 스코프 체인 문제에 대해 그렇게 많이 쓰지 않으며, 책에 물건을 쓰는 것도 매우 피곤합니다. o (□) o
3. 폐쇄의 예
예 1 : 축적의 구현
// 메소드 1var a = 0; var add = function () {a ++; console.log (a)} add (); add (); // 메소드 2 : Closure var var add = () // undefinedAdd (); add ();이에 비해 방법 2는 더 우아하고 글로벌 변수를 줄이고 변수를 민영화합니다.
예 2 : 각 Li에 클릭 이벤트를 추가하십시오
var oli = document.getElementsByTagName ( 'li'); var i; for (i = 0; i <5; i ++) {oli [i] .onclick = function () {alert (i);}} console.log (i); // 5 // 익명 함수 실행 (function () {alert (i); // 5} ());위는 전형적인 예입니다. 우리는 모두 실행 결과가 5 개의 팝업이라는 것을 알고 있으며, 폐쇄 가이 문제를 해결하는 데 사용될 수 있다는 것을 알고 있지만, 처음에는 5 개의 팝업이 왜, 왜 폐쇄 가이 문제를 해결할 수 있는지 이해할 수 없습니다. 나중에, 나는 그것을 분류하고 분명히했다.
에이. 먼저 폐쇄가 사용되기 전에 상황을 분석하겠습니다. for 루프에서 익명 기능을 각 Li 클릭 이벤트에 바인딩하고 변수 I의 값은 익명 기능에서 반환합니다. 루프가 끝나면 변수 i의 값이 5가됩니다. 현재, 우리는 각 Li를 클릭합니다. 즉, 해당 익명 함수를 실행합니다 (위의 코드 참조). 이것은 I 이미 5 인 변수입니다. 각 클릭은 5가 나타납니다. 여기에 반환 된 각 익명 함수는 동일한 변수 i를 나타 내기 때문에 루프가 실행될 때 i의 전류 i 값을 저장하기 위해 새로운 변수를 만들면 익명 함수 가이 변수를 적용하고 마지막 으로이 익명 함수를 반환하여 목적을 달성 할 수 있습니다. 이것은 클로저를 사용하여 달성됩니다!
비. 클로저를 사용할 때 상황을 분석하겠습니다.
var oli = document.getElementsByTagName ( 'li'); var i; for (i = 0; i <5; i ++) {oli [i] .onclick = (function (num) {var a = num; // 문제를 설명합니다. // 5FER 루프가 실행되면 클릭 이벤트에 바인딩 된 익명 함수가 I에 전달되고 즉시 실행되어 내부 익명 기능을 반환합니다. 매개 변수는 값으로 전달되므로 공식 매개 변수 NUM은 i의 현재 값을 저장 한 다음 로컬 변수 a에 값을 할당합니다. 그런 다음 내부 익명 함수는 a의 참조를 유지합니다. 즉, 현재 값은 i의 현재 값을 유지합니다. 따라서 루프가 실행되면 각 Li를 클릭하면 반환 된 익명 함수가 저장된 값의 값을 나타냅니다.
4. 폐쇄의 적용
폐쇄의 목적을 살펴 보겠습니다. 사실, 클로저를 사용함으로써 많은 일을 할 수 있습니다. 예를 들어, 객체 지향 코드 스타일을 시뮬레이션합니다. 더 우아하고 간결하게 코드를 표현하십시오. 일부 측면에서 코드 실행 효율성을 향상시킵니다.
1. 익명 자체 이행 기능
실제 상황에서, 우리는 종종 일부 기능을 한 번만 실행해야하며 UI 초기화와 같이 내부 변수를 유지할 필요가 없으므로 클로저를 사용할 수 있습니다.
// 모든 li fonts를 빨간색으로 변경합니다 (var els = document.getElementsByTagName ( 'li'); for (var i = 0, lng = els.length; i <lng; i ++) {els [i] .style.color = 'red';}}) ();익명 기능을 만들고 즉시 실행합니다. 외부는 내부의 변수를 참조 할 수 없으므로 ELS, I 및 LNG와 같은 로컬 변수는 실행 직후에 릴리스되어 메모리를 저장합니다!
핵심은이 메커니즘이 글로벌 객체를 오염시키지 않는다는 것입니다.
2. 캡슐화/모듈 식 코드 구현
var person = function () {// 변수의 범위는 함수 내부에 있으며 var name = "default"는 외부에 액세스 할 수 없습니다. return {getName : function () {return name; }, setName : function (newName) {name = newName; }}} (); console.log (person.name); // 직접 액세스, 결과는 정의되지 않은 console.log (person.getName ()); // default person.setName ( "jozo"); console.log (person.getName ()); // jozo3. 객체 지향을 구현하십시오
이런 식으로, 다른 물체 (클래스의 인스턴스)에는 독립적 인 구성원과 상태가 있으며 서로를 방해하지 않습니다. JavaScript의 클래스와 같은 메커니즘은 없지만 클로저를 사용하여 이러한 메커니즘을 시뮬레이션 할 수 있습니다. 위의 예에 대해 이야기 해 봅시다.
함수 person () {var name = "default"; return {getName : function () {return name; }, setName : function (newName) {name = newName; }}}; var person1 = person (); print (person1.getName ()); john.setName ( "person1"); print (person1.getName ()); // person1 var person2 = person (); print (person2.getName ()); jack.setname ( "Eson2"); print (erson2.getName ()); // person2사람의 두 가지 사례 person1과 person2는 서로를 방해하지 않습니다! 이 두 인스턴스는 이름 멤버에 독립적으로 액세스하기 때문입니다.
5. 메모리 누출 및 솔루션
쓰레기 재활용 메커니즘
메모리 관리에 대해 말하면, JS의 쓰레기 수집 메커니즘과 자연스럽게 분리 할 수 없습니다. 쓰레기 수집을 실현하기위한 두 가지 전략이 있습니다 : 마크 클리어런스 및 참조 계산 ;
마크 제거 : 쓰레기 수집기가 실행되면 메모리에 저장된 모든 변수를 표시합니다. 그런 다음 환경에서 변수 태그와 환경의 변수에 의해 참조 된 변수 태그를 제거합니다. 그 후 변수가 다시 표시되면 변수를 삭제할 준비가되었음을 의미합니다. 2008 년까지, 즉 Firefox, Opera, Chrome 및 Safari의 JavaScript가 모두이 방법을 사용했습니다.
참조 수 : 각 값이 참조되는 횟수를 추적합니다. 변수가 선언되고 기준 유형의 값이 변수에 할당되면,이 값이 참조되는 횟수는 1입니다.이 값이 다른 변수에 할당되면, 참조 값이 1만큼 증가한 횟수.
이 방법의 큰 문제는 원형 기준입니다. 즉, 대상 A는 B에 대한 포인터를 포함하고 A에 대한 참조도 포함되어 있습니다. 여기에는 많은 양의 메모리가 재활용 될 수 있습니다 (메모리 누출). 참조는 0이 될 수 없기 때문에 초기 IE 버전 (IE4-IE6)이 채택 된 수집 메커니즘을 채택했습니다. 폐쇄가 메모리 누출을 일으킨 이유 중 하나는이 알고리즘의 결함이었습니다.
우리는 IE의 일부 객체가 기본 JavaScript 객체가 아니라는 것을 알고 있습니다. 예를 들어, BOM 및 DOM의 객체는 COM 객체의 형태로 구현되며 COM 객체의 쓰레기 수집 메커니즘은 참조 계수를 채택합니다. 따라서 IE의 JavaScript 엔진은 태그 청산 전략을 채택하지만 COM 객체에 액세스하는 것은 여전히 참조 계수를 기반으로하므로 COM 객체가 IE에 설계되는 한 원형 참조 문제가 발생합니다!
밤나무를 가져 가십시오 :
window.onload = function () {var el = document.getElementById ( "id"); el.onclick = function () {alert (el.id);}}이 코드가 메모리 누출을 유발하는 이유는 무엇입니까?
el.onclick = function () {alert (el.id);};이 코드를 실행할 때 익명 함수 객체는 EL의 OnClick 속성에 할당됩니다. 그런 다음 익명 함수는 내부의 EL 객체를 말하며 원형 참조가 있으므로 재활용 할 수 없습니다.
해결책:
window.onload = function () {var el = document.getElementById ( "id"); var id = el.id; // 회의를 회의 el.onClick = function () {alert (id); } el = null; // 클로저에서 참조하는 외부 함수에서 활성 객체를 지우십시오}위의 것은 JS 클로저 범위 체인 쓰레기 수집 메모리 누출에 대한 관련 지식의 요약입니다. 나는 그것이 모두에게 도움이되기를 바랍니다!