클로저 문제 극복
JavaScript를 처음 배울 때 클로저를 학습하는 데 우회를 많이 했습니다. . 이번에는 기초 지식을 정리하기 위해 다시 돌아왔습니다. 클로저를 명확하게 설명하는 것도 매우 큰 도전입니다.
마감은 얼마나 중요합니까? 프론트엔드를 처음 접하시는 분들이라면 실제 개발에서 클로저가 얼마나 널리 사용되는지 직관적으로 말씀드릴 수는 없지만 프론트엔드 인터뷰에서는 클로저에 대해 물어봐야 한다는 점은 말씀드릴 수 있습니다. 면접관은 면접관의 기본 수준을 결정하기 위해 클로저에 대한 이해를 활용하는 경우가 많습니다. 보수적으로 추정하면 프론트엔드 면접관 10명 중 최소 5명은 클로저로 사망할 것입니다.
그런데 폐쇄가 왜 그렇게 중요한데도 여전히 많은 사람들이 이를 이해하지 못하고 있나요? 모두가 배우려고 하지 않기 때문일까요? 실제로는 그렇지 않은데 검색을 통해 찾아낸 클로저를 설명하는 중국 기사의 대부분은 클로저에 대해 명확하게 설명하지 않았습니다. 피상적이거나 이해할 수 없거나 말도 안되는 일입니다. 저를 포함해 폐쇄에 대한 요약을 쓴 적이 있는데, 돌이켜보면 차마 그 내용을 볼 수가 없었습니다.
따라서 이 글의 목적은 독자들이 클로저를 절반만 이해하는 것이 아니라 읽고 나서 완전히 이해할 수 있도록 명확하고 명확하게 클로저를 설명하는 것입니다.
1. 스코프와 스코프 체인
스코프 체인을 자세히 설명하기 전에, 여러분이 다음과 같은 JavaScript의 중요한 개념을 대략적으로 이해했다고 가정합니다. 이러한 개념은 매우 도움이 될 것입니다.
아직 이해하지 못하셨다면 이 시리즈의 처음 세 기사를 읽어보세요. 이 기사 끝에 목차 링크가 있습니다. 클로저를 설명하기 위해 모든 분들을 위한 기초 지식을 준비했습니다. 하하, 정말 대단한 쇼네요.
범위
-
JavaScript에서는 범위를 규칙 집합으로 정의할 수 있으며, 이는 엔진이 다음을 기반으로 변수를 조회하는 방법을 관리하는 데 사용됩니다. 현재 범위 및 중첩된 하위 범위의 식별자 이름입니다.
JavaScript만 있습니다. 전역 범위 및 함수 범위 (일반적인 개발에서는 eval이 거의 사용되지 않으므로 여기서는 논의하지 않습니다).
-
범위와 실행 컨텍스트는 완전히 다른 개념입니다. 많은 사람들이 혼동하고 있다는 것을 알고 있지만, 주의 깊게 구별해야 합니다.
JavaScript 코드의 전체 실행 과정은 코드 컴파일 단계와 코드 실행 단계의 두 단계로 나누어집니다. 컴파일 단계는 코드를 실행 가능한 코드로 변환하는 컴파일러에 의해 완료됩니다. 이 단계에서 범위 규칙이 결정됩니다. 실행 단계는 엔진에 의해 완료됩니다. 이 단계에서 실행 컨텍스트가 생성됩니다.
절차
스코프체인
검토하자 이전 글에서 분석한 실행 컨텍스트의 라이프사이클을 살펴보면 아래와 같습니다.
실행 컨텍스트 수명 주기
스코프 체인은 실행 컨텍스트 생성 단계에서 생성되는 것을 확인했습니다. 이것은 이상하다. 위에서는 컴파일 단계에서 범위가 규칙을 결정한다고 말했는데, 실행 단계에서 범위 체인이 결정되는 이유는 무엇입니까?
이런 질문을 하게 된 이유는 모두가 스코프와 스코프체인에 대해 오해를 하고 있기 때문입니다. 위에서 말했듯이 범위는 규칙 집합입니다. 그렇다면 범위 체인이란 무엇입니까? 이는 이 규칙 세트의 구체적인 구현입니다. 이것이 범위와 범위 체인의 관계이므로 모두가 이해해야 한다고 생각합니다.
우리는 함수가 호출되고 활성화되면 해당 실행 컨텍스트가 생성되기 시작한다는 것을 알고 있습니다. 실행 컨텍스트 생성 프로세스 중에 변수 개체, 범위 체인 및 this 값이 각각 결정됩니다. . 이전 글에서는 변수 객체에 대해 자세히 설명했고, 여기서는 스코프 체인에 대해 자세히 설명하겠습니다.
스코프 체인은 현재 환경과 상위 환경의 일련의 변수 객체로 구성되어 있으며, 접근 권한을 충족하는 변수와 함수에 대해 현재 실행 환경의 질서 있는 접근을 보장합니다.
스코프 체인의 이해를 돕기 위해 먼저 예제와 해당 다이어그램을 통해 설명하겠습니다.
var a = 20; function test() { var b = a + 10; function innerTest() { var c = 10; return b + c; } return innerTest(); } test();
위의 예에서는 전역의 실행 컨텍스트, 함수 테스트, 함수 innerTest가 차례로 생성됩니다. 변수 개체를 각각 VO(global), VO(test), VO(innerTest)로 설정했습니다. innerTest의 스코프 체인에는 이 세 가지 변수 객체가 동시에 포함되어 있으므로 innerTest의 실행 컨텍스트는 다음과 같이 표현할 수 있습니다.
innerTestEC = { VO: {...}, // 变量对象 scopeChain: [VO(innerTest), VO(test), VO(global)], // 作用域链 this: {} }
예, 읽으셨습니다. 배열을 직접 사용하여 범위 체인을 나타낼 수 있습니다. 배열의 첫 번째 항목인scopeChain[0]은 범위 체인 배열의 마지막 항목은 범위 체인의 끝이며 모든 끝은 전역 변수 개체입니다.
많은 사람들이 현재 범위와 상위 범위가 포괄적인 관계에 있다고 오해할 수 있지만 그렇지 않습니다. 앞부분에서 시작해서 끝부분으로 끝나는 일방통행이라는 표현이 더 적절한 설명인 것 같아요. 그림과 같습니다.
스코프 체인 다이어그램
실행 컨텍스트가 실행 단계 개체에 들어갈 때 변수 개체가 활성화되므로 이는 이전 글에서도 언급하였으므로 그림에서는 이를 표현하기 위해 AO를 사용하였습니다. 활성 객체
예, 범위 체인은 일련의 변수 객체로 구성됩니다. 이 단방향 채널 식별자에서 변수 객체를 쿼리할 수 있습니다. , 상위 범위의 변수에 액세스할 수 있도록 합니다.
2. 클로저
JavaScript를 사용해 본 경험이 조금 있지만 클로저의 개념을 실제로 이해한 적이 없는 사람들에게 클로저를 이해하는 것은 어떤 의미에서는 클로저를 깨는 것으로 볼 수 있습니다. 병목현상은 당신의 능력을 크게 향상시킬 수 있습니다.
클로저는 스코프 체인과 밀접한 관련이 있습니다.
클로저는 함수 실행 중에 확인됩니다.
먼저 클로저의 정의를 간단하게 버리세요: 클로저는 함수가 자신이 속한 범위(전역 범위 제외)를 기억하고 액세스할 수 있을 때 생성됩니다. 함수가 현재 범위 밖에서 실행됩니다.
간단히 말하면 함수 A가 함수 B 안에 정의되어 있고, 함수 A가 실행될 때 함수 B 내의 변수 객체에 접근한다면 B는 클로저 Bag입니다.
기본 고급(1)에서는 JavaScript의 가비지 수집 메커니즘을 정리했습니다. JavaScript에는 자동 가비지 수집 메커니즘이 있습니다. 가비지 수집 메커니즘과 관련하여 중요한 동작이 있습니다. 즉, 값이 메모리에서 참조를 잃으면 가비지 수집 메커니즘이 특수 알고리즘에 따라 해당 값을 찾습니다. . 그리고 재활용하여 메모리를 확보하세요.
그리고 우리는 함수의 실행 컨텍스트가 실행된 후 수명 주기가 끝나고 함수의 실행 컨텍스트가 참조를 잃게 된다는 것을 알고 있습니다. 그것이 차지하는 메모리 공간은 곧 가비지 수집기에 의해 해제됩니다. 그러나 클로저가 있으면 이 프로세스가 방해됩니다.
먼저 간단한 예를 들어보겠습니다.
var fn = null; function foo() { var a = 2; function innnerFoo() { console.log(a); } fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn } function bar() { fn(); // 此处的保留的innerFoo的引用 } foo(); bar(); // 2
위의 예에서 foo()
가 실행된 후 상식적으로 실행 환경 수명 주기가 종료되고 점유된 메모리는 가비지 수집기에 의해 해제됩니다. 그러나 fn = innerFoo
을 통해 innerFoo 함수에 대한 참조가 유지되고 전역 변수 fn에 복사됩니다. 이 동작으로 인해 foo의 변수 개체가 유지됩니다. 따라서 함수 bar 내에서 fn 함수가 실행되면 보유된 변수 개체에 계속 액세스할 수 있습니다. 따라서 이 순간에도 변수 a의 값에 액세스할 수 있습니다.
이러한 방식으로 foo를 클로저라고 부를 수 있습니다.
다음 그림은 클로저 foo의 스코프 체인을 보여줍니다.
클로저 foo의 스코프 체인, 사진의 제목이 잘못되었으니 무시하세요
에서 찾을 수 있습니다 chrOme 브라우저의 개발자 도구에서 이 코드가 실행될 때 생성된 함수 호출 스택 및 범위 체인 생성을 봅니다. 아래 그림과 같습니다.
Chrome에서 클로저를 관찰하는 방법과 클로저의 더 많은 예에 대한 자세한 내용은 기본 시리즈(6)를 읽어보세요.
그림에서 볼 수 있듯이 크롬 브라우저는 우리가 일반적으로 생각하는 innerFoo가 아닌 클로저를 foo로 생각하고 있습니다
在上面的图中,红色箭头所指的正是闭包。其中Call Stack为当前的函数调用栈,Scope为当前正在被执行的函数的作用域链,Local为当前的局部变量。
所以,通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量。比如在上面的例子中,我们在函数bar的执行环境中访问到了函数foo的a变量。个人认为,从应用层面,这是闭包最重要的特性。利用这个特性,我们可以实现很多有意思的东西。
不过读者老爷们需要注意的是,虽然例子中的闭包被保存在了全局变量中,但是闭包的作用域链并不会发生任何改变。在闭包中,能访问到的变量,仍然是作用域链上能够查询到的变量。
对上面的例子稍作修改,如果我们在函数bar中声明一个变量c,并在闭包fn中试图访问该变量,运行结果会抛出错误。
var fn = null; function foo() { var a = 2; function innnerFoo() { console.log(c); // 在这里,试图访问函数bar中的c变量,会抛出错误 console.log(a); } fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn } function bar() { var c = 100; fn(); // 此处的保留的innerFoo的引用 } foo(); bar();
关于这一点,很多同学把函数调用栈与作用域链没有分清楚,所以有的大神看了我关于介绍执行上下文的文章时就义正言辞的说我的例子有问题,而这些评论有很大的误导作用,为了帮助大家自己拥有能够辨别的能力,所以我写了基础(六),教大家如何在chrome中观察闭包,作用域链,this等。当然我也不敢100%保证我文中的例子就一定正确,所以教大家如何去辨认我认为才是最重要的。
闭包的应用场景
接下来,我们来总结下,闭包的常用场景。
我们知道setTimeout的第一个参数是一个函数,第二个参数则是延迟的时间。在下面例子中,
function fn() { console.log('this is test.') } var timer = setTimeout(fn, 1000); console.log(timer);
执行上面的代码,变量timer的值,会立即输出出来,表示setTimeout这个函数本身已经执行完毕了。但是一秒钟之后,fn才会被执行。这是为什么?
按道理来说,既然fn被作为参数传入了setTimeout中,那么fn将会被保存在setTimeout变量对象中,setTimeout执行完毕之后,它的变量对象也就不存在了。可是事实上并不是这样。至少在这一秒钟的事件里,它仍然是存在的。这正是因为闭包。
很显然,这是在函数的内部实现中,setTimeout通过特殊的方式,保留了fn的引用,让setTimeout的变量对象,并没有在其执行完毕后被垃圾收集器回收。因此setTimeout执行结束后一秒,我们任然能够执行fn函数。
柯里化
在函数式编程中,利用闭包能够实现很多炫酷的功能,柯里化算是其中一种。关于柯里化,我会在以后详解函数式编程的时候仔细总结。
模块
在我看来,模块是闭包最强大的一个应用场景。如果你是初学者,对于模块的了解可以暂时不用放在心上,因为理解模块需要更多的基础知识。但是如果你已经有了很多JavaScript的使用经验,在彻底了解了闭包之后,不妨借助本文介绍的作用域链与闭包的思路,重新理一理关于模块的知识。这对于我们理解各种各样的设计模式具有莫大的帮助。
(function () { var a = 10; var b = 20; function add(num1, num2) { var num1 = !!num1 ? num1 : a; var num2 = !!num2 ? num2 : b; return num1 + num2; } window.add = add; })(); add(10, 20);
在上面的例子中,我使用函数自执行的方式,创建了一个模块。add是模块对外暴露的一个公共方法。而变量a,b被作为私有变量。在面向对象的开发中,我们常常需要考虑是将变量作为私有变量,还是放在构造函数中的this中,因此理解闭包,以及原型链是一个非常重要的事情。模块十分重要,因此我会在以后的文章专门介绍,这里就暂时不多说啦。
此图中可以观看到当代码执行到add方法时的调用栈与作用域链,此刻的闭包为外层的自执行函数
为了验证自己有没有搞懂作用域链与闭包,这里留下一个经典的思考题,常常也会在面试中被问到。
利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5
for (var i=1; i<p>点此查看关于此题的详细解读</p><p>关于作用域链的与闭包我就总结完了,虽然我自认为我是说得非常清晰了,但是我知道理解闭包并不是一件简单的事情,所以如果你有什么问题,可以在评论中问我。你也可以带着从别的地方没有看懂的例子在评论中留言。大家一起学习进步。</p>
위 내용은 프런트엔드 고급(4): 범위 체인 및 종결에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

보다 효율적인 코드를 작성하고 성능 병목 현상 및 최적화 전략을 이해하는 데 도움이되기 때문에 JavaScript 엔진이 내부적으로 작동하는 방식을 이해하는 것은 개발자에게 중요합니다. 1) 엔진의 워크 플로에는 구문 분석, 컴파일 및 실행; 2) 실행 프로세스 중에 엔진은 인라인 캐시 및 숨겨진 클래스와 같은 동적 최적화를 수행합니다. 3) 모범 사례에는 글로벌 변수를 피하고 루프 최적화, Const 및 Lets 사용 및 과도한 폐쇄 사용을 피하는 것이 포함됩니다.

Python은 부드러운 학습 곡선과 간결한 구문으로 초보자에게 더 적합합니다. JavaScript는 가파른 학습 곡선과 유연한 구문으로 프론트 엔드 개발에 적합합니다. 1. Python Syntax는 직관적이며 데이터 과학 및 백엔드 개발에 적합합니다. 2. JavaScript는 유연하며 프론트 엔드 및 서버 측 프로그래밍에서 널리 사용됩니다.

Python과 JavaScript는 커뮤니티, 라이브러리 및 리소스 측면에서 고유 한 장점과 단점이 있습니다. 1) Python 커뮤니티는 친절하고 초보자에게 적합하지만 프론트 엔드 개발 리소스는 JavaScript만큼 풍부하지 않습니다. 2) Python은 데이터 과학 및 기계 학습 라이브러리에서 강력하며 JavaScript는 프론트 엔드 개발 라이브러리 및 프레임 워크에서 더 좋습니다. 3) 둘 다 풍부한 학습 리소스를 가지고 있지만 Python은 공식 문서로 시작하는 데 적합하지만 JavaScript는 MDNWebDocs에서 더 좋습니다. 선택은 프로젝트 요구와 개인적인 이익을 기반으로해야합니다.

C/C에서 JavaScript로 전환하려면 동적 타이핑, 쓰레기 수집 및 비동기 프로그래밍으로 적응해야합니다. 1) C/C는 수동 메모리 관리가 필요한 정적으로 입력 한 언어이며 JavaScript는 동적으로 입력하고 쓰레기 수집이 자동으로 처리됩니다. 2) C/C를 기계 코드로 컴파일 해야하는 반면 JavaScript는 해석 된 언어입니다. 3) JavaScript는 폐쇄, 프로토 타입 체인 및 약속과 같은 개념을 소개하여 유연성과 비동기 프로그래밍 기능을 향상시킵니다.

각각의 엔진의 구현 원리 및 최적화 전략이 다르기 때문에 JavaScript 엔진은 JavaScript 코드를 구문 분석하고 실행할 때 다른 영향을 미칩니다. 1. 어휘 분석 : 소스 코드를 어휘 단위로 변환합니다. 2. 문법 분석 : 추상 구문 트리를 생성합니다. 3. 최적화 및 컴파일 : JIT 컴파일러를 통해 기계 코드를 생성합니다. 4. 실행 : 기계 코드를 실행하십시오. V8 엔진은 즉각적인 컴파일 및 숨겨진 클래스를 통해 최적화하여 Spidermonkey는 유형 추론 시스템을 사용하여 동일한 코드에서 성능이 다른 성능을 제공합니다.

실제 세계에서 JavaScript의 응용 프로그램에는 서버 측 프로그래밍, 모바일 애플리케이션 개발 및 사물 인터넷 제어가 포함됩니다. 1. 서버 측 프로그래밍은 Node.js를 통해 실현되며 동시 요청 처리에 적합합니다. 2. 모바일 애플리케이션 개발은 재교육을 통해 수행되며 크로스 플랫폼 배포를 지원합니다. 3. Johnny-Five 라이브러리를 통한 IoT 장치 제어에 사용되며 하드웨어 상호 작용에 적합합니다.

일상적인 기술 도구를 사용하여 기능적 다중 테넌트 SaaS 응용 프로그램 (Edtech 앱)을 구축했으며 동일한 작업을 수행 할 수 있습니다. 먼저, 다중 테넌트 SaaS 응용 프로그램은 무엇입니까? 멀티 테넌트 SAAS 응용 프로그램은 노래에서 여러 고객에게 서비스를 제공 할 수 있습니다.

이 기사에서는 Contrim에 의해 확보 된 백엔드와의 프론트 엔드 통합을 보여 주며 Next.js를 사용하여 기능적인 Edtech SaaS 응용 프로그램을 구축합니다. Frontend는 UI 가시성을 제어하기 위해 사용자 권한을 가져오고 API가 역할 기반을 준수하도록합니다.


핫 AI 도구

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

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

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

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

인기 기사

뜨거운 도구

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

안전한 시험 브라우저
안전한 시험 브라우저는 온라인 시험을 안전하게 치르기 위한 보안 브라우저 환경입니다. 이 소프트웨어는 모든 컴퓨터를 안전한 워크스테이션으로 바꿔줍니다. 이는 모든 유틸리티에 대한 액세스를 제어하고 학생들이 승인되지 않은 리소스를 사용하는 것을 방지합니다.

WebStorm Mac 버전
유용한 JavaScript 개발 도구

mPDF
mPDF는 UTF-8로 인코딩된 HTML에서 PDF 파일을 생성할 수 있는 PHP 라이브러리입니다. 원저자인 Ian Back은 자신의 웹 사이트에서 "즉시" PDF 파일을 출력하고 다양한 언어를 처리하기 위해 mPDF를 작성했습니다. HTML2FPDF와 같은 원본 스크립트보다 유니코드 글꼴을 사용할 때 속도가 느리고 더 큰 파일을 생성하지만 CSS 스타일 등을 지원하고 많은 개선 사항이 있습니다. RTL(아랍어, 히브리어), CJK(중국어, 일본어, 한국어)를 포함한 거의 모든 언어를 지원합니다. 중첩된 블록 수준 요소(예: P, DIV)를 지원합니다.
