폐쇄의 기원에 대해 이야기합시다
함수 a () {var i = 0; 함수 b () {console.log (i);} return b;} var c = a (); c ();일반적으로 함수 내의 익명 함수가 자체 변수를 사용하고 익명 함수가 반환되면 위 코드와 같은 폐쇄가 생성됩니다.
이 시점에서, 전화의 끝이 파괴 되더라도, 나는 존재하고 사라지지 않을 것입니다. A가 정의되면 JS 통역사는 기능 A의 범위 체인을 정의 된 환경으로 설정합니다. A가 실행되면 A는 해당 실행 환경에 들어갑니다. 실행 환경이 생성 된 후에 만 스코프 스코프 속성이있는 다음 활성 객체를 작성한 다음 스코프 체인의 상단으로 설정합니다.
이제 A의 스코프 체인에는 A와 창의 활성 객체가 있습니다.
그런 다음 활성 객체에 인수 속성을 추가하십시오
이때, A의 반환 함수 B에 대한 참조는 c에 주어진다. B의 스코프 체인에는 A의 활성 객체 참조가 포함되므로 C는 A의 활성 객체에 액세스 할 수 있습니다. 현재, A는 돌아온 후 GC가되지 않습니다.
위의 것은 폐쇄에 대한 간단한 소개입니다. 너무 많이 말하면 돌아 다니기가 쉽습니다. 여기서 간단히 끝내고 실제 장면을 입력하여 설명해 봅시다.
실제 장면
동료의 의심
내 동료가 코드를 읽어달라고 요청했습니다.
var user = function (opts) {var scope = this; for (var k in opts) {scope [ 'get' + k] = function () {return opts [k];}; scope [ 'set' + k] = function (v) {return opts [k] = v;};}}; var u = 새 사용자 ({name : 'test : 11});이 코드는 매우 간단하고 들어오는 객체에 대한 get/set 메소드를 생성하고 싶지만 여기에서는 폐쇄 문제가 발생했습니다.
이 문제의 이유는 반품 값에 내부적으로 사용 된 k가 항상 "나이"이기 때문입니다. 이 k는 getxxx 함수에 의해 공유되는 활성 객체 때문이므로 여기에서 수정하는 것은 비교적 간단합니다.
var user = function (opts) {var scope = this; for (var k in opts) {(함수 (k) {scope [ 'get' + k] = function () {return opts [k];}; scope [ 'set' + k] = function (v) {return opts [k] = v;};}) (k);}}; 11});for 루프 내부에서 즉각적인 실행 함수를 만들고 k를 전달합니다.이 시점에서 getxxx 함수는 각 익명 함수의 "k"를 공유합니다.
고유 한 ID를 생성하십시오
고유 한 ID를 생성하는 것도 클로저를 사용하는 전형적인 방법입니다.
함수 getUuid () {var id = 0; return function () {return ++ id;}} var uuid = getUuid ();이 코드는 실제로 매우 의미가 있습니다. 우리는 브라우저에서 uuid ()를 지속적으로 실행하고 실제로 다른 값을 얻을 수 있지만 getuuid () () 만 사용하면 값은 매번 동일합니다.
이 문제의 이유는 GetUuid 실행 후 결과를 UUID에 할당하기 때문입니다. 현재 UUID는 익명 함수에 대한 참조를 저장하고 익명 함수는 getuuid의 활성 객체를 저장하므로 ID가 파괴되지 않았습니다.
직접 호출하면 활성 객체가 매번 재생되므로 ID를 저장할 수 없습니다.
흥미로운 코드
util.taryurl = function (url) {var iframe = document.createElement ( 'iframe'); iframe.height = 1; iframe.width = 1; iframe.frameborder = 0; iframe.style.position = '절대'; iframe.style.left = '-9999px'; iframe. '-9999px'; docum이 코드는 매우 흥미 롭습니다. 처음으로 호출하면 iframe 객체를 만들고 Iframe 객체는 두 번째로 존재합니다. 여기에서 코드를 단순화 할 것입니다.
var getUuid = function () {var i = 0; getUuid = function () {return i ++;}; return getUuid ();};이 조정 후에는 실제로 반환 기능이 없지만 여전히 폐쇄를 형성합니다.
이벤트 대표단 및 폐쇄
우리는 jQuery 's ON을 사용한다는 것을 알고 있지만 이벤트 대표단이 무엇인지 진정으로 이해하려면 여전히 많은 노력이 필요합니다. 여기에서 시도해 봅시다.
폐쇄는 이벤트 대표단의 구현의 초석입니다. 마지막으로, 오늘의 폐쇄 연구를 이벤트 대표단으로 끝내겠습니다.
다음 DOM 구조에 페이지를 추가하십시오
<입력 ID = "입력"값 = "입력"유형 = "버튼"/> <div id = "div"> i am div> <span id = "span"> 나는 span </span> <div id = "wrapper"> <입력 id = "내부"value = "i am inner"type = "butter"/> </div>입니다.
Zepto를 사용하는 경우 다음 방법을 사용하여 이벤트를 바인딩합니다.
$ .on ( 'Click', 'Selector', FN)
우리는 여기에 Zepto가 없습니다. 단순히 스스로 구현하십시오.
이벤트 대표단의 원칙
우선, 이벤트 대의원 구현의 초석은 이벤트 버블입니다. 페이지의 각 클릭은 결국 부모 요소로 거품이되므로 문서에서 모든 이벤트를 포착 할 수 있습니다.
이 문제를 알면 간단한 대리인 이벤트 바인딩 메소드를 스스로 구현할 수 있습니다.
함수 대의원 (선택기, 유형, fn) {document.addeventListener (type, fn, false);} delegate ( '#input', 'click', function () {console.log ( 'ttt');});이 코드는 가장 간단한 구현입니다. 우선, 우리는 페이지를 클릭 한 곳에 상관없이 클릭 이벤트를 실행합니다. 물론 이것은 분명히 우리가보고 싶은 상황이 아니므로 클릭 할 때마다 필요한 이벤트를 트리거하기 위해 처리를 수행합니다.
몇 가지 더 심각한 질문은 다음과 같습니다.
① 이벤트가 문서에 바운드되었으므로 지금 클릭하는 요소를 어떻게 알 수 있습니까?
e.Target을 기준으로 현재 클릭 요소를 얻을 수 있더라도 어떤 요소가 이벤트가 있는지 어떻게 알 수 있습니까?
procelector 셀렉터를 기반으로 이벤트를 실행 해야하는 현재 클릭의 요소를 결정할 수 있더라도 어떤 이벤트가 있는지 확인할 수있는 방법
위의 문제를 해결할 수 있다면 후속 프로세스는 비교적 간단합니다.
클릭 요소가 이벤트를 트리거하는지 확인하십시오
먼저, 클릭하면 e.target을 사용하여 현재 클릭 요소를 얻은 다음 선택기에 따라 부모 dom을 검색 할 수 있습니다. 발견되면 이벤트가 트리거되어야합니다.
트리거시기 만 결정할 수 있으므로 간단한 작업 후에는 FN 콜백 기능을 다시 작성해야합니다.
var arr = []; var slice = arr.slice; var extend = function (src, obj) {var o = {}; for (var k in src) {o [k] = src [k];} for (var k in obj) = function (e) {// selector var selectorel = docum 포함 된 경우, 이벤트는 트리거/*************************** 이것은 단순한 구현 일 뿐이며 실제 응용 프로그램 **************************/if (electorel.contains (el)) {var evt = extend (e, {currentTarget : selectorel}); [evt] .concat (slice.call (Arguments, 1)); Callback.apply (selectorel, evt); var s = '';} var s = '';}; document.addeventListener (유형, 핸들러, 거짓);}그래서 우리는 통화를 확장 할 수 있습니다.
Delegate ( '#input', 'click', function () {console.log ( 'input');}); delegate ( '#div', 'click', function () {console.log ( 'div');}); Delegate ( '#Wrapper', 'click', function () {console.log ( 'wrapper'); () {console.log ( 'span');}); delegate ( '##inner', 'click', function () {console.log ( '내부');});전체 프로그램을 간단히 분석하겠습니다
∎ 우리는 대의원에게 전화하여 신체에 이벤트를 추가합니다.
② 바인딩 할 때 콜백을 다시 작성했습니다.
click 클릭 할 때 (바인딩 이벤트가 실제로 클릭을 트리거하는 횟수), 현재 요소는 선택기에서 검색 한 요소에 포함되어 있는지 확인합니다. 포함하면 이벤트가 트리거됩니다.
∎ 여기에 등록 할 때마다 폐쇄가 형성되므로 들어오는 콜백이 유지되므로 호출 할 때마다 고유 한 콜백 기능을 찾을 수 있습니다 (여기에서 폐쇄를 이해하는 데 매우 도움이됩니다).
∎ 마지막으로, 이벤트 핸들의 현재 표적을 다시 작성하여 이벤트 대표단이 종료됩니다.
추신 : 이벤트 처리와 같은 구현에 문제가 있지만 데모로서는주의를 기울이지 않을 것입니다. 관심있는 친구들은 Zepto 구현을 스스로 확인할 수 있습니다.
이벤트 대표 문제
이벤트 대표단은 효율성을 향상시킬 수 있지만 한 가지 더 성가신 것은 거품을 막는 것이 쓸모가 없다는 것입니다.
위의 코드를 예로 들어보십시오. 내부 요소와 래퍼 요소가 있습니다. 그들은 서로의 관계를 감싸고 있습니다.
그러나 모든 이벤트가 문서에 바인딩되기 때문에 실행 순서는 이벤트 버블 내부 및 외부의 순서가 아닙니다. 여기서 실행 순서는 등록 주문에 의해 결정됩니다.
다음은 질문이 있습니다 : "거품을 멈추는 방법"
내부에서 실행을 완료했습니다
E.StopimmediatePropagation ()
목적을 달성 할 수 있지만 여전히 내부 요소를 이전에 등록해야합니다.
또한, 하나의 이벤트 만 그러한 중첩 요소에 구속되어 있으며, E.Target은 어떤 이벤트를 실행할 것인지 결정합니다. 자세한 내용은 직접 고려하십시오.
백본을 사용할 때 위의 문제는 실제로 발생할 수 있습니다.