찾다
웹 프론트엔드JS 튜토리얼고급 프런트엔드 기본 사항(4): 범위 체인 및 종결에 대한 자세한 설명

고급 프런트엔드 기본 사항(4): 범위 체인 및 종결에 대한 자세한 설명

Feb 16, 2017 pm 01:28 PM
스코프 체인프런트 엔드베이스고급의폐쇄

고급 프런트엔드 기본 사항(4): 범위 체인 및 종결에 대한 자세한 설명

JavaScript를 처음 배울 때 클로저 학습에서 많은 우회를 했습니다. 하지만 이번에는 다시 기본 지식을 정리하고 클로저에 대해 명확하게 설명하는 것 역시 매우 큰 과제입니다.

마감은 얼마나 중요합니까? 프론트엔드를 처음 접하는 경우 실제 개발에서 클로저가 얼마나 널리 사용되는지 직관적으로 말할 수는 없지만 프론트엔드 인터뷰에서는 클로저에 대해 물어봐야 한다는 점은 말씀드릴 수 있습니다. 면접관은 면접관의 기본 수준을 결정하기 위해 클로저에 대한 이해를 활용하는 경우가 많습니다. 보수적으로 추정하면 프론트엔드 면접관 10명 중 최소 5명은 클로저로 사망할 것입니다.

그런데 폐쇄가 왜 그렇게 중요한데도 여전히 많은 사람들이 이를 이해하지 못하고 있나요? 모두가 배우려고 하지 않기 때문일까요? 실제로는 그렇지 않습니다. 검색을 통해 찾은 클로저를 설명하는 대부분의 중국 기사에는 클로저에 대한 설명이 명확하지 않았습니다. 피상적이거나 이해할 수 없거나 말도 안되는 일입니다. 저를 포함해 폐쇄에 대한 요약을 쓴 적이 있는데, 돌이켜보면 차마 그 내용을 볼 수가 없었습니다.

따라서 이 글의 목적은 독자들이 클로저를 절반만 이해하는 것이 아니라 읽고 나면 클로저를 완전히 이해할 수 있도록 명확하고 분명하게 클로저를 설명하는 것입니다.


1. 스코프 및 스코프 체인

스코프 체인을 자세히 설명하기 전에, 여러분이 이미 다음과 같은 JavaScript의 중요한 개념에 대해 전반적으로 이해하고 있다고 가정합니다. 이러한 개념은 매우 도움이 될 것입니다.

1. 기본 데이터 유형 및 참조 데이터 유형

2. 메모리 공간

3. 실행 컨텍스트

5. 가변 객체와 활성 객체

아직 이해하지 못했다면 이 시리즈의 처음 세 기사를 읽어보세요. 이 기사 끝에 디렉터리 링크가 있습니다. 클로저를 설명하기 위해 모든 분들을 위한 기초 지식을 준비했습니다. 하하, 정말 대단한 쇼네요.

Scope1. JavaScript에서는 엔진을 관리하는 데 사용되는 규칙 집합으로 범위를 정의할 수 있습니다. 현재 범위와 중첩된 하위 범위의 식별자 이름을 기반으로 변수 조회.

여기서 식별자는 변수명이나 함수명을 가리킨다

2. 자바스크립트에는 전역 스코프와 함수 스코프만 있습니다(우리는 보통 eval을 개발하기 때문에 거의 사용되지 않으므로 여기서는 논의하지 않습니다.)

3. 범위와 실행 컨텍스트는 완전히 다른 개념입니다. 많은 사람들이 혼동하고 있다는 것을 알고 있지만, 주의 깊게 구별해야 합니다.

JavaScript 코드의 전체 실행 과정은 코드 컴파일 단계와 코드 실행 단계의 두 단계로 나누어집니다. 컴파일 단계는 코드를 실행 가능한 코드로 변환하는 컴파일러에 의해 완료됩니다. 이 단계에서 범위 규칙이 결정됩니다. 실행 단계는 엔진에 의해 완료됩니다. 이 단계에서 실행 컨텍스트가 생성됩니다.

고급 프런트엔드 기본 사항(4): 범위 체인 및 종결에 대한 자세한 설명

스코프 체인이전 기사에서 분석한 실행 컨텍스트의 수명 주기를 떠올려 보세요. 아래 그림과 같이.

고급 프런트엔드 기본 사항(4): 범위 체인 및 종결에 대한 자세한 설명실행 컨텍스트의 생성 단계에서 범위 체인이 생성되는 것을 확인했습니다. 이것은 이상하다. 위에서 범위 규칙은 컴파일 단계에서 결정된다고 했는데 왜 범위 체인은 실행 단계에서 결정되는 걸까요?

이런 질문을 드리는 이유는 모두가 스코프와 스코프체인에 대해 오해를 갖고 있기 때문입니다. 위에서 말했듯이 범위는 규칙 집합입니다. 그렇다면 범위 체인이란 무엇입니까? 이는 이 규칙 세트의 구체적인 구현입니다. 이것이 범위와 범위 체인의 관계이므로 모두가 이해해야 한다고 생각합니다.

우리는 함수가 호출되고 활성화되면 해당 실행 컨텍스트가 생성되기 시작한다는 것을 알고 있습니다. 실행 컨텍스트 생성 프로세스 중에 변수 개체, 범위 체인 및 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의 실행 컨텍스트는 다음과 같이 표현할 수 있습니다.

innerTestEC = {
    VO: {...},  // 变量对象
    scopeChain: [VO(innerTest), VO(test), VO(global)], // 作用域链
    this: {}
}

是的,你没有看错,我们可以直接用一个数组来表示作用域链,数组的第一项scopeChain[0]为作用域链的最前端,而数组的最后一项,为作用域链的最末端,所有的最末端都为全局变量对象。

很多人会误解为当前作用域与上层作用域为包含关系,但其实并不是。以最前端为起点,最末端为终点的单方向通道我认为是更加贴切的形容。如图。

고급 프런트엔드 기본 사항(4): 범위 체인 및 종결에 대한 자세한 설명

注意,因为变量对象在执行上下文进入执行阶段时,就变成了活动对象,这一点在上一篇文章中已经讲过,因此图中使用了AO来表示。Active Object

是的,作用域链是由一系列变量对象组成,我们可以在这个单向通道中,查询变量对象中的标识符,这样就可以访问到上一层作用域中的变量了。


二、闭包

对于那些有一点 JavaScript 使用经验但从未真正理解闭包概念的人来说,理解闭包可以看作是某种意义上的重生,突破闭包的瓶颈可以使你功力大增。

先直截了当的抛出闭包的定义:当一个函数可以记住并访问所在的作用域(全局作用域除外),并在定义该函数的作用域之外执行时,该函数就可以称之为一个闭包。

简单来说,假设函数A在函数B的内部进行定义了,并在函数B的作用域之外执行(不管是上层作用域,下层作用域,还有其他作用域),那么A就是一个闭包。记住这个定义,你在其他地方很难看到了。

在基础进阶(一)中,我总结了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的变量对象,也被保留了下来。于是,函数fn在函数bar内部执行时,依然可以访问这个被保留下来的变量对象。所以此刻仍然能够访问到变量a的值。

这样,我们就可以称fn为闭包。

下图展示了闭包fn的作用域链。

고급 프런트엔드 기본 사항(4): 범위 체인 및 종결에 대한 자세한 설명

所以,通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量。比如在上面的例子中,我们在函数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();

上面的例子,可以很直观的感受到闭包的存在,但是还有一种情况的闭包,则更加隐蔽难以感受。我们来看一个例子。

function test() {
    function bar (str) {
        console.log(str);
    }

    function foo (fn, string) {
        fn(string);
    }

    foo(bar, 'this is closure');
}


test();

这个例子中,函数bar在函数test的作用域中定义,然后被作为参数传入了函数foo中并在foo的作用域中被执行。根据定义,我们很容易知道函数bar就是一个闭包。因为其隐蔽性,很多人并没有意识到这就是一个闭包。这种情况,就是我们常常说的回调函数。在实际开发中,我们遇到的大多数回调函数都是闭包。

很多时候,回调函数都是匿名函数,但是要注意的是,在其他一些语言中,闭包与匿名函数是有区别的,但是JavaScript在实现匿名函数的时候允许形成闭包,当匿名函数作为参数传入函数中时,匿名函数的引用会保存在改函数变量对象的arguments对象中。因此在JavaScript中,我们可以不用那么严格的区别闭包与匿名函数。

闭包的应用场景

接下来,我们来总结下,闭包的常用场景。

延迟函数setTimeout

我们知道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被作为一个闭包,对外暴露了一个公共方法。而变量a,b被作为私有变量。在面向对象的开发中,我们常常需要考虑是将变量作为私有变量,还是放在构造函数中的this中,因此理解闭包,以及原型链是一个非常重要的事情。模块十分重要,因此我会在以后的文章专门介绍,这里就暂时不多说啦。

为了验证自己有没有搞懂作用域链与闭包,这里留下一个经典的思考题,常常也会在面试中被问到。

利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5

for (var i=1; i<=5; i++) { 
    setTimeout( function timer() {
        console.log(i);
    }, i*1000 );
}

关于作用域链的与闭包我就总结完了,虽然我自认为我是说得非常清晰了,但是我知道理解闭包并不是一件简单的事情,所以如果你有什么问题,可以在评论中问我,留言必回。你也可以带着从别的地方没有看懂的例子在评论中留言。大家一起学习进步。

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

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

브라우저 너머 : 실제 세계의 JavaScript브라우저 너머 : 실제 세계의 JavaScriptApr 12, 2025 am 12:06 AM

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

Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축Next.js (백엔드 통합)로 멀티 테넌트 SAAS 애플리케이션 구축Apr 11, 2025 am 08:23 AM

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

Next.js (Frontend Integration)를 사용하여 멀티 테넌트 SaaS 응용 프로그램을 구축하는 방법Next.js (Frontend Integration)를 사용하여 멀티 테넌트 SaaS 응용 프로그램을 구축하는 방법Apr 11, 2025 am 08:22 AM

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

JavaScript : 웹 언어의 다양성 탐색JavaScript : 웹 언어의 다양성 탐색Apr 11, 2025 am 12:01 AM

JavaScript는 현대 웹 개발의 핵심 언어이며 다양성과 유연성에 널리 사용됩니다. 1) 프론트 엔드 개발 : DOM 운영 및 최신 프레임 워크 (예 : React, Vue.js, Angular)를 통해 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축합니다. 2) 서버 측 개발 : Node.js는 비 차단 I/O 모델을 사용하여 높은 동시성 및 실시간 응용 프로그램을 처리합니다. 3) 모바일 및 데스크탑 애플리케이션 개발 : 크로스 플랫폼 개발은 개발 효율을 향상시키기 위해 반응 및 전자를 통해 실현됩니다.

JavaScript의 진화 : 현재 동향과 미래 전망JavaScript의 진화 : 현재 동향과 미래 전망Apr 10, 2025 am 09:33 AM

JavaScript의 최신 트렌드에는 Typescript의 Rise, 현대 프레임 워크 및 라이브러리의 인기 및 WebAssembly의 적용이 포함됩니다. 향후 전망은보다 강력한 유형 시스템, 서버 측 JavaScript 개발, 인공 지능 및 기계 학습의 확장, IoT 및 Edge 컴퓨팅의 잠재력을 포함합니다.

Demystifying JavaScript : 그것이하는 일과 중요한 이유Demystifying JavaScript : 그것이하는 일과 중요한 이유Apr 09, 2025 am 12:07 AM

JavaScript는 현대 웹 개발의 초석이며 주요 기능에는 이벤트 중심 프로그래밍, 동적 컨텐츠 생성 및 비동기 프로그래밍이 포함됩니다. 1) 이벤트 중심 프로그래밍을 사용하면 사용자 작업에 따라 웹 페이지가 동적으로 변경 될 수 있습니다. 2) 동적 컨텐츠 생성을 사용하면 조건에 따라 페이지 컨텐츠를 조정할 수 있습니다. 3) 비동기 프로그래밍은 사용자 인터페이스가 차단되지 않도록합니다. JavaScript는 웹 상호 작용, 단일 페이지 응용 프로그램 및 서버 측 개발에 널리 사용되며 사용자 경험 및 크로스 플랫폼 개발의 유연성을 크게 향상시킵니다.

Python 또는 JavaScript가 더 좋습니까?Python 또는 JavaScript가 더 좋습니까?Apr 06, 2025 am 12:14 AM

Python은 데이터 과학 및 기계 학습에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 간결한 구문 및 풍부한 라이브러리 생태계로 유명하며 데이터 분석 및 웹 개발에 적합합니다. 2. JavaScript는 프론트 엔드 개발의 핵심입니다. Node.js는 서버 측 프로그래밍을 지원하며 풀 스택 개발에 적합합니다.

See all articles

핫 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. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전By尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

WebStorm Mac 버전

WebStorm Mac 버전

유용한 JavaScript 개발 도구

맨티스BT

맨티스BT

Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

SublimeText3 Linux 새 버전

SublimeText3 Linux 새 버전

SublimeText3 Linux 최신 버전

메모장++7.3.1

메모장++7.3.1

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