목차
1. 폐쇄란 무엇인가요?
2. 闭包中的作用域链
4. 闭包的运用
5. 内存泄露及解决方案
6. 总结闭包的优缺点
웹 프론트엔드 JS 튜토리얼 js의 클로저에 대한 자세한 소개

js의 클로저에 대한 자세한 소개

Jun 29, 2017 am 11:35 AM
범위 메모리 재활용

1. 폐쇄란 무엇인가요?

클로저에 대한 몇 가지 정의를 살펴보겠습니다.

  1. 클로저는 다른 함수의 범위에 있는 변수에 액세스할 수 있는 권한이 있는 함수를 나타냅니다. - "JS 고급 프로그래밍 제3판" p178

  2. Function Objects 스코프 체인을 통해 연관될 수 있으며, 함수 본문 내부의 변수를 함수 스코프에 저장할 수 있는 기능을 '클로저'라고 합니다. -- "JS 최종 가이드" p183

  3. 내부 함수는 자신이 정의된 외부 함수의 매개변수와 변수에 액세스할 수 있습니다(인수 제외). ). -- "JS Language Essence" p36thisarguments)。 --《JS语言精粹》 p36

来个定义总结

  1. 可以访问外部函数作用域中变量的函数

  2. 被内部函数访问的外部函数的变量可以保存在外部函数作用域内而不被回收---这是核心,后面我们遇到闭包都要想到,我们要重点关注被闭包引用的这个变量。

来创建个简单的闭包

var sayName = function(){var name = 'jozo';return function(){
        alert(name);
    }
};var say = sayName(); 
say();
로그인 후 복사

来解读后面两个语句:

  • var say = sayName() :返回了一个匿名的内部函数保存在变量say中,并且引用了外部函数的变量name,由于垃圾回收机制,sayName函数执行完毕后,变量name并没有被销毁。

  • say() :执行返回的内部函数,依然能访问变量name,输出 'jozo' .

2. 闭包中的作用域链

理解作用域链对理解闭包也很有帮助。

变量在作用域中的查找方式应该都很熟悉了,其实这就是顺着作用域链往上查找的。

当函数被调用时:

  1. 先创建一个执行环境(execution context),及相应的作用域链;

  2. 将arguments和其他命名参数的值添加到函数的活动对象(activation object)

作用域链:当前函数的活动对象优先级最高,外部函数的活动对象次之,外部函数的外部函数的活动对象依次递减,直至作用域链的末端--全局作用域。优先级就是变量查找的先后顺序;

先来看个普通的作用域链:

function sayName(name){return name;
}var say = sayName('jozo');
로그인 후 복사

这段代码包含两个作用域:a.全局作用域;b.

정의를 요약해보자

함수는 외부 함수 범위의 변수에 접근할 수 있다

는 다음과 같이 접근된다. 내부 함수 외부 함수의 변수는 재활용되지 않고 외부 함수의 범위에 저장될 수 있습니다. 이것이 핵심입니다. 나중에 클로저가 참조하는 변수에 집중해야 합니다. 폐쇄.

간단한 클로저 생성

function sayName(name){return function(){return name;
    }
}var say = sayName('jozo');
로그인 후 복사

다음 두 문 해석:

    var say = sayName() </ code>: say 변수에 저장된 익명의 내부 함수를 반환하며, 외부 함수의 변수 이름을 참조합니다. 가비지 수집 메커니즘으로 인해 sayName 함수가 실행된 후에 변수 이름이 삭제되지 않습니다. </p><p></p><p><code>say(): 반환된 내부 함수를 실행하면 여전히 변수 이름에 액세스하고 'jozo'를 출력할 수 있습니다.

2. 범위 체인을 이해하는 것은 클로저를 이해하는 데에도 도움이 됩니다.

스코프에서 변수를 검색하는 방식에 익숙해야 합니다. 실제로 이는 스코프 체인을 검색하는 것입니다.

함수 호출 시:

먼저 실행 컨텍스트와 해당 범위 체인을 만듭니다. 🎜🎜🎜🎜인수 값과 기타 명명된 매개 변수를 함수의 활성 객체에 추가합니다(활성화 객체). )🎜🎜🎜스코프 체인: 현재 함수의 활성 개체가 가장 높은 우선순위를 갖고, 그 다음이 외부 함수의 활성 개체이며, 외부 함수의 활성 개체는 다음이 될 때까지 순차적으로 감소합니다. 범위 체인의 끝-- 전역 범위. 우선순위는 변수가 검색되는 순서입니다. 🎜🎜🎜먼저 일반적인 범위 체인을 살펴보겠습니다. 🎜🎜
// 方式1var a = 0;var add = function(){
    a++;
    console.log(a)
}add();add();//方式2 :闭包var add = (function(){
    var  a = 0;
    return function(){
        a++;
        console.log(a);
    }
})();
console.log(a); //undefinedadd();add();

相比之下方式2更加优雅,也减少全局变量,将变量私有化
로그인 후 복사
🎜이 코드에는 두 가지 범위가 포함되어 있습니다: a.전역 범위; /code>sayName 함수의 범위, 즉 변수 개체가 2개만 있습니다. 해당 실행 환경이 실행되면 변수 개체가 활성 개체가 되어 실행 환경 범위 체인의 프런트 엔드로 푸시됩니다. 즉, 우선순위가 가장 높은 것입니다. 그림을 보면서 이야기해볼까요? 🎜🎜🎜🎜🎜🎜🎜이 그림은 JS 고급 프로그래밍 책에도 나오는데, 제가 다시 그렸습니다. 🎜🎜sayName() 함수를 생성할 때 변수 객체를 미리 포함하는 스코프 체인이 생성되는데, 이는 그림의 인덱스 1인 스코프 체인이며, 호출되면 내부 [[Scope]] 속성에 저장됩니다. sayName() 함수를 호출하면 실행 환경이 생성되고, 그 후 함수의 [[Scope]] 속성에 객체를 복사하여 활성 도메인 체인이 구성됩니다. )이 생성되어 실행 환경 범위 체인 앞으로 푸시됩니다. 🎜🎜일반적으로 함수 실행이 완료되면 로컬 활성 개체가 소멸되고 전역 범위만 메모리에 저장됩니다. 그러나 클로저의 상황은 다릅니다: 🎜🎜🎜 클로저의 범위 체인을 살펴보겠습니다: 🎜🎜
 var oli = document.getElementsByTagName(&#39;li&#39;); var i; for(i = 0;i < 5;i++){
     oli[i].onclick = function(){
         alert(i);
     }
 } console.log(i); // 5 //执行匿名函数
 (function(){
    alert(i);  //5
 }());
로그인 후 복사
🎜이 클로저 인스턴스에는 이전 예제보다 익명 함수 범위가 하나 더 있습니다: 🎜🎜🎜🎜 🎜🎜🎜After the 익명 함수는 sayName() 함수에서 반환되며 해당 범위 체인은 sayName() 함수를 포함하는 활성 개체 및 전역 변수 개체로 초기화됩니다. 이러한 방식으로 익명 함수는 sayName()에 정의된 모든 변수와 매개변수에 액세스할 수 있습니다. 더 중요한 것은 sayName() 함수가 실행된 후 이 활성 개체의 범위 체인으로 인해 해당 활성 개체가 삭제되지 않는다는 것입니다. 즉, sayName() 함수가 실행된 후 해당 실행 환경의 범위 체인은 삭제되지만 해당 활성 개체는 익명 함수가 삭제될 때까지 메모리에 남아 있습니다. 이는 나중에 다루게 될 메모리 누수 문제이기도 합니다. 🎜🎜스코프 체인 문제에 대해서는 별로 쓰지 않겠습니다. o(╯□╰)o🎜🎜3. 클로저의 예🎜🎜🎜예 1: 누적 구현🎜🎜
     var oli = document.getElementsByTagName(&#39;li&#39;);     var i;     for(i = 0;i < 5;i++){
         oli[i].onclick = (function(num){             var a = num; // 为了说明问题             return function(){
                 alert(a);
             }
         })(i)
     }     console.log(i); // 5
로그인 후 복사
로그인 후 복사
🎜🎜 예제 2 : 각 li🎜🎜
//将全部li字体变为红色
(function(){    var els = document.getElementsByTagName(&#39;li&#39;);for(var i = 0,lng = els.length;i < lng;i++){
        els[i].style.color = &#39;red&#39;;
    }    
})();
로그인 후 복사
로그인 후 복사
🎜에 클릭 이벤트를 추가합니다. 위의 내용은 실행 결과가 5라는 것을 모두 알고 있으며 클로저를 사용하여 이 문제를 해결할 수 있다는 것도 알고 있습니다. 왜 매번 나타나는지 이해할 수 없었습니다. 모두 5개인데 왜 이 문제를 해결할 수 있습니까? 약간의 검색 끝에 마침내 알아냈습니다. 🎜

a. 先来分析没用闭包前的情况:for循环中,我们给每个li点击事件绑定了一个匿名函数,匿名函数中返回了变量i的值,当循环结束后,变量i的值变为5,此时我们再去点击每个li,也就是执行相应的匿名函数(看上面的代码),这是变量i已经是5了,所以每个点击弹出5. 因为这里返回的每个匿名函数都是引用了同一个变量i,如果我们新建一个变量保存循环执行时当前的i的值,然后再让匿名函数应用这个变量,最后再返回这个匿名函数,这样就可以达到我们的目的了,这就是运用闭包来实现的!

b. 再来分析下运用闭包时的情况:

     var oli = document.getElementsByTagName(&#39;li&#39;);     var i;     for(i = 0;i < 5;i++){
         oli[i].onclick = (function(num){             var a = num; // 为了说明问题             return function(){
                 alert(a);
             }
         })(i)
     }     console.log(i); // 5
로그인 후 복사
로그인 후 복사

这里for循环执行时,给点击事件绑定的匿名函数传递i后立即执行返回一个内部的匿名函数,因为参数是按值传递的,所以此时形参num保存的就是当前i的值,然后赋值给局部变量 a,然后这个内部的匿名函数一直保存着a的引用,也就是一直保存着当前i的值。 所以循环执行完毕后点击每个li,返回的匿名函数执行弹出各自保存的 a 的引用的值。

4. 闭包的运用

我们来看看闭包的用途。事实上,通过使用闭包,我们可以做很多事情。比如模拟面向对象的代码风格;更优雅,更简洁的表达出代码;在某些方面提升代码的执行效率。

1. 匿名自执行函数

我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,比如UI的初始化,那么我们可以使用闭包:

//将全部li字体变为红色
(function(){    var els = document.getElementsByTagName(&#39;li&#39;);for(var i = 0,lng = els.length;i < lng;i++){
        els[i].style.color = &#39;red&#39;;
    }    
})();
로그인 후 복사
로그인 후 복사

我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,
因此els,i,lng这些局部变量在执行完后很快就会被释放,节省内存!
关键是这种机制不会污染全局对象。

2. 实现封装/模块化代码

var person= function(){    //变量作用域为函数内部,外部无法访问    var name = "default";       return {    
       getName : function(){    
           return name;    
       },    
       setName : function(newName){    
           name = newName;    
       }    
    }    
}();console.log(person.name);//直接访问,结果为undefined    
console.log(person.getName());  //default 
person.setName("jozo");    
console.log(person.getName());  //jozo
로그인 후 복사

3. 实现面向对象中的对象
这样不同的对象(类的实例)拥有独立的成员及状态,互不干涉。虽然JavaScript中没有类这样的机制,但是通过使用闭包,
我们可以模拟出这样的机制。还是以上边的例子来讲:

function 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("erson2");    
print(erson2.getName());  //person2
로그인 후 복사

Person的两个实例person1 和 person2 互不干扰!因为这两个实例对name这个成员的访问是独立的 。

5. 内存泄露及解决方案

垃圾回收机制

说到内存管理,自然离不开JS中的垃圾回收机制,有两种策略来实现垃圾回收:标记清除 和 引用计数;

标记清除:垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记,然后,它会去掉环境中的变量的标记和被环境中的变量引用的变量的标记,此后,如果变量再被标记则表示此变量准备被删除。 2008年为止,IE,Firefox,opera,chrome,Safari的javascript都用使用了该方式;

引用计数:跟踪记录每个值被引用的次数,当声明一个变量并将一个引用类型的值赋给该变量时,这个值的引用次数就是1,如果这个值再被赋值给另一个变量,则引用次数加1。相反,如果一个变量脱离了该值的引用,则该值引用次数减1,当次数为0时,就会等待垃圾收集器的回收。

这个方式存在一个比较大的问题就是循环引用,就是说A对象包含一个指向B的指针,对象B也包含一个指向A的引用。 这就可能造成大量内存得不到回收(内存泄露),因为它们的引用次数永远不可能是 0 。早期的IE版本里(ie4-ie6)采用是计数的垃圾回收机制,闭包导致内存泄露的一个原因就是这个算法的一个缺陷。

我们知道,IE中有一部分对象并不是原生额javascript对象,例如,BOM和DOM中的对象就是以COM对象的形式实现的,而COM对象的垃圾回收机制采用的就是引用计数。因此,虽然IE的javascript引擎采用的是标记清除策略,但是访问COM对象依然是基于引用计数的,因此只要在IE中设计COM对象就会存在循环引用的问题!

举个栗子:

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; // 将闭包引用的外部函数中活动对象清除
}
로그인 후 복사

6. 总结闭包的优缺点

优点:

  • 可以让一个变量常驻内存 (如果用的多了就成了缺点

  • 避免全局变量的污染

  • 私有化变量

缺点

  • 클로저는 자신을 포함하는 함수의 범위를 전달하므로 다른 함수보다 더 많은 메모리를 차지합니다

  • 메모리 누수를 유발합니다

위 내용은 js의 클로저에 대한 자세한 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 채팅 명령 및 사용 방법
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

대용량 메모리 최적화, 컴퓨터가 16g/32g 메모리 속도로 업그레이드했는데 변화가 없다면 어떻게 해야 하나요? 대용량 메모리 최적화, 컴퓨터가 16g/32g 메모리 속도로 업그레이드했는데 변화가 없다면 어떻게 해야 하나요? Jun 18, 2024 pm 06:51 PM

기계식 하드 드라이브나 SATA 솔리드 스테이트 드라이브의 경우 소프트웨어 실행 속도의 증가를 느낄 수 있지만 NVME 하드 드라이브라면 느끼지 못할 수도 있습니다. 1. 레지스트리를 데스크탑으로 가져와 새 텍스트 문서를 생성하고, 다음 내용을 복사하여 붙여넣은 후 1.reg로 저장한 후 마우스 오른쪽 버튼을 클릭하여 병합하고 컴퓨터를 다시 시작합니다. WindowsRegistryEditorVersion5.00[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SessionManager\MemoryManagement]"DisablePagingExecutive"=d

C 언어에서 typedef 구조체 사용법 C 언어에서 typedef 구조체 사용법 May 09, 2024 am 10:15 AM

typedef struct는 C 언어에서 구조 사용을 단순화하기 위해 구조 유형 별칭을 만드는 데 사용됩니다. 구조 별칭을 지정하여 기존 구조에 새 데이터 유형의 별칭을 지정합니다. 향상된 가독성, 코드 재사용 및 유형 확인 등의 이점이 있습니다. 참고: 별칭을 사용하기 전에 구조를 정의해야 합니다. 별칭은 프로그램에서 고유해야 하며 선언된 범위 내에서만 유효해야 합니다.

소식통에 따르면 삼성전자와 SK하이닉스는 2026년 이후 적층형 모바일 메모리를 상용화할 것으로 보인다. 소식통에 따르면 삼성전자와 SK하이닉스는 2026년 이후 적층형 모바일 메모리를 상용화할 것으로 보인다. Sep 03, 2024 pm 02:15 PM

3일 홈페이지 보도에 따르면 국내 언론 에트뉴스는 어제(현지시간) 삼성전자와 SK하이닉스의 'HBM형' 적층구조 모바일 메모리 제품이 2026년 이후 상용화될 것이라고 보도했다. 소식통에 따르면 두 한국 메모리 거대 기업은 적층형 모바일 메모리를 미래 수익의 중요한 원천으로 여기고 'HBM형 메모리'를 스마트폰, 태블릿, 노트북으로 확장해 엔드사이드 AI에 전력을 공급할 계획이라고 전했다. 이 사이트의 이전 보도에 따르면 삼성전자 제품은 LPWide I/O 메모리라고 하며 SK하이닉스는 이 기술을 VFO라고 부른다. 두 회사는 팬아웃 패키징과 수직 채널을 결합하는 것과 거의 동일한 기술 경로를 사용했습니다. 삼성전자 LPWide I/O 메모리의 비트폭은 512이다.

js 클로저의 장점과 단점 js 클로저의 장점과 단점 May 10, 2024 am 04:39 AM

JavaScript 클로저의 장점에는 변수 범위 유지, 모듈식 코드 활성화, 실행 지연 및 이벤트 처리가 포함됩니다. 단점에는 메모리 누수, 복잡성 증가, 성능 오버헤드 및 범위 체인 효과가 포함됩니다.

C++에서 include의 의미는 무엇입니까? C++에서 include의 의미는 무엇입니까? May 09, 2024 am 01:45 AM

C++의 #include 전처리기 지시문은 외부 소스 파일의 내용을 현재 소스 파일에 삽입하고 해당 내용을 현재 소스 파일의 해당 위치에 복사합니다. 표준 입출력 함수를 포함하기 위한 #include <iostream>과 같이 코드에 필요한 선언이 포함된 헤더 파일을 포함하는 데 주로 사용됩니다.

Kingbang은 CAMM2, LPCAM2 및 일반 모델 중에서 선택할 수 있는 새로운 DDR5 8600 메모리를 출시했습니다. Kingbang은 CAMM2, LPCAM2 및 일반 모델 중에서 선택할 수 있는 새로운 DDR5 8600 메모리를 출시했습니다. Jun 08, 2024 pm 01:35 PM

6월 7일 이 사이트의 소식에 따르면 GEIL은 2024년 타이페이 국제 컴퓨터 쇼에서 최신 DDR5 솔루션을 출시했으며 선택할 수 있는 SO-DIMM, CUDIMM, CSODIMM, CAMM2 및 LPCAM2 버전을 제공했습니다. ▲사진출처: Wccftech 사진에서 볼 수 있듯이 진방이 전시한 CAMM2/LPCAMM2 메모리는 매우 컴팩트한 디자인을 채택해 최대 128GB의 용량과 최대 8533MT/s의 속도를 제공할 수 있다. 보조 냉각 없이 9000MT/s까지 오버클럭된 AMDAM5 플랫폼에서 안정적입니다. 보고서에 따르면 Jinbang의 2024 Polaris RGBDDR5 시리즈 메모리는 최대 8400을 제공할 수 있습니다.

C++ 스마트 포인터: 수명 주기에 대한 포괄적인 분석 C++ 스마트 포인터: 수명 주기에 대한 포괄적인 분석 May 09, 2024 am 11:06 AM

C++ 스마트 포인터의 수명 주기: 생성: 스마트 포인터는 메모리가 할당될 때 생성됩니다. 소유권 이전: 이동 작업을 통해 소유권을 이전합니다. 해제: 스마트 포인터가 범위를 벗어나거나 명시적으로 해제되면 메모리가 해제됩니다. 객체 소멸: 가리키는 객체가 소멸되면 스마트 포인터는 유효하지 않은 포인터가 됩니다.

DDR5 MRDIMM 및 LPDDR6 CAMM 메모리 사양 출시 준비 완료, JEDEC에서 주요 기술 세부 정보 공개 DDR5 MRDIMM 및 LPDDR6 CAMM 메모리 사양 출시 준비 완료, JEDEC에서 주요 기술 세부 정보 공개 Jul 23, 2024 pm 02:25 PM

7월 23일 이 홈페이지의 소식에 따르면, 마이크로 전자공학 표준 제정 기관인 JEDEC 솔리드 스테이트 기술 협회(JEDEC Solid State Technology Association)는 현지 시간으로 22일 DDR5MRDIMM 및 LPDDR6CAMM 메모리 기술 사양이 곧 공식 출시될 것이라고 발표하고 이 두 가지의 주요 세부 사항을 소개했습니다. 추억. DDR5MRDIMM의 "MR"은 MultiplexedRank를 의미하며, 이는 메모리가 두 개 이상의 랭크를 지원하고 추가적인 물리적 연결 없이 단일 채널에서 여러 데이터 신호를 결합 및 전송할 수 있음을 의미합니다. 연결을 통해 대역폭을 효과적으로 늘릴 수 있습니다. JEDEC는 DDR5RDIMM 메모리의 현재 6.4Gbps와 비교하여 궁극적으로 대역폭을 12.8Gbps로 늘리는 것을 목표로 여러 세대의 DDR5MRDIMM 메모리를 계획했습니다.

See all articles