발행일

[ZBF] 자바스크립트 클로저 TAIL ASK

[ZBF] 자바스크립트 클로저 TAIL ASK

이 글은 네이버 블로그에 2024년 5월 22일에 올렸던 것을 그대로 옮겨온 것입니다.

실행 컨텍스트 (Execution Context)

  • 전역 컨텍스트 (Global Context)
  • 함수 컨텍스트 (Function Context)
  • 블록 컨텍스트 (Block Context)

자바스크립트에서 실행 컨텍스트는 코드가 실행되는 환경을 의미하며, 각각의 실행 컨텍스트는 특정한 상태와 관련된 정보를 포함합니다. 실행 컨텍스트에는 전역 컨텍스트, 함수 컨텍스트, 블록 컨텍스트가 있습니다.

실행 컨텍스트 (Execution Context)

실행 컨텍스트는 자바스크립트 코드가 실행되는 동안 유지되는 환경으로, 변수, 함수 선언, this 키워드, 스코프 체인 등을 포함합니다. 실행 컨텍스트는 코드가 실행될 때마다 생성되고, 다음의 세 가지 주요 타입으로 분류됩니다.

  1. 전역 컨텍스트 (Global Context)
  • 정의: 자바스크립트 코드가 처음 실행될 때 생성되는 컨텍스트입니다. 전역 컨텍스트는 프로그램이 종료될 때까지 유지되며, 최상위 스코프를 형성합니다.

  • 전역 객체 (window 또는 global 객체)

  • 전역 변수와 함수 선언

  • this 키워드: 전역 컨텍스트에서는 this가 전역 객체를 참조합니다.

  1. 함수 컨텍스트 (Function Context)
  • 정의: 함수가 호출될 때마다 생성되는 컨텍스트입니다. 각 함수 호출마다 새로운 함수 컨텍스트가 생성되며, 함수 실행이 완료되면 해당 컨텍스트는 사라집니다.

  • 지역 변수와 매개변수

  • 함수 내에서 선언된 다른 함수

  • this 키워드: 호출 방식에 따라 다릅니다.

  • arguments 객체: 함수 호출 시 전달된 인수들을 포함하는 유사 배열 객체입니다.

  1. 블록 컨텍스트 (Block Context)
  • 정의: ES6에서 도입된 컨텍스트로, 블록({}) 내에서 생성되는 컨텍스트입니다. let, const로 선언된 변수는 블록 스코프를 가지며, 블록이 끝나면 컨텍스트도 사라집니다.

  • 블록 내에서 선언된 변수 (let, const)

  • 블록 내에서 선언된 함수 (ES6 이후 블록 범위 함수 선언 가능)

  1. 선언에 의해 생성된 모든 식별자를 스코프를 구분하여 등록 하고 상태 변화를 지속적으로 관리 할 수 있어야 한다.

  2. 스코프는 중첩 관계에 의해 스코프 체인을 형성해야한다. 즉 , 스코프 체인을 통해 상위 스코프로 이동하여 식별자를 검색 할 수 있어야 한다.

  3. 현재 실행 중인 코드의 실행 순서를 변경할 수 있어야 하며 다시 되돌아 갈 수 있어야 한다.

실행 컨텍스트는 소스 코드를 실행하는데 필요한 환경을 제공하고 코드의 실행 결과를 실제로 관리하는 영역

실행 컨텍스트는 식별자를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 메커니즘, 모든 코드는 실행 되고 관리 된다.

렉시컬 환경은

식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료 구조로 실행 컨텍스를 구성하는 컴포넌트다.


클로저 (Closure)

  • 렉시컬 환경 (Lexical Environment)
  • 데이터 은닉 (Data Hiding)
  • 캡슐화 (Encapsulation)
  • 상태 유지 (State Preservation)
  • 자유 변수 (Free Variable)
  • 비공개 변수 (Private Variable)
  • 고차 함수 (Higher-order Function)
  • 커링 함수 (Curry Function)
항목설명
클로저클로저는 함수와 그 함수가 선언된 어휘적 환경의 조합으로, 함수가 자신이 생성될 때의 환경을 기억하는 특성을 가집니다. 이는 자바스크립트에서 매우 강력한 기능으로, 내부 함수가 외부 함수의 스코프에 있는 변수에 접근할 수 있게 해줍니다.
장점설명
데이터 은닉과 캡슐화클로저를 통해 함수 외부에서 접근할 수 없는 변수를 만들어 데이터를 보호할 수 있습니다.
지속성 있는 상태외부 함수가 종료된 후에도 내부 함수가 외부 함수의 변수를 참조할 수 있어 상태를 유지할 수 있습니다.
고차 함수 사용콜백과 같은 고차 함수에서 클로저를 사용하여 환경 설정이나 상태 유지를 효과적으로 할 수 있습니다.
캡슐화데이터와 그 데이터를 조작하는 함수를 하나의 단위로 묶어, 데이터를 보호하고 오직 정의된 함수를 통해서만 접근할 수 있게 합니다.
상태 유지함수가 생성될 때의 환경을 기억하여 해당 환경의 변수들의 상태를 유지하며, 외부 영향으로부터 독립적입니다.
단점설명
메모리 사용 증가클로저가 외부 변수를 참조하고 있으면 그 변수는 가비지 컬렉션의 대상이 되지 않아 메모리 사용이 증가할 수 있습니다. 불필요하게 메모리를 많이 사용하면 페이지의 성능이 저하될 수 있습니다.
복잡성 증가클로저를 과도하게 사용하면 코드의 복잡성이 증가할 수 있습니다. 특히 많은 수의 중첩 함수가 있을 경우, 코드를 이해하고 유지보수하는 것이 어려워질 수 있습니다.
디버깅 어려움클로저의 환경은 디버깅할 때 직관적으로 이해하기 어려울 수 있으며, 일부 개발 도구에서 클로저의 내부 상태를 추적하기 어려울 수 있습니다.
메모리 누수외부 함수가 종료한 후에도 클로저가 참조하고 있는 변수가 메모리에 남아 있을 수 있어, 메모리 누수가 발생할 수 있습니다.

클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합

자바스크립트 엔진은 함수를 어디서 호출 했는지가 아니라 함수를 어디에 정의 했는지에 따라 상위 스코프를 결정 한다. 이를 렉시컬 스코프라 한다.

렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장할 참조 값, 즉 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의 된 환경에 의해 결정된다. 이것이 바로 렉시컬 스코프이다.

함수 객체의 내부 슬롯에 저장된 현재 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 바로 상위 스코프다. 또한 자신이 호출 되었을 때 생성될 함수 렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장할 참조 값. 함수 객체는 내부 슬롯에 저장한 렉시컬 환경의 참조, 즉 상위 스코프를 자신이 존재하는 한 기억한다.

외부 함수보다 중첩 함수가 더 오래 유지되는 경우, 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저라 부른다.

실행 컨텍스트는 스택에서 제거 되지만 렉시컬 환경까지 소멸하는 것은 아니다.

클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고, 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것이 일반적이다.

클로저는 상태를 안전하게 변경하고 유지하기 위해 사용

상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용 하기 위해 사용

함수를 호출해 함수를 반환 할 때 반환된 함수는 자신의 독립된 렉시컬 환경을 갖는다.

클래스는 생성자 함수와는 다르다.

자바스크립트에서 클래스와 생성자 함수는 객체를 생성하고 구조화하는 방법을 제공합니다. ES6(ECMAScript 2015)에서 도입된 클래스는 생성자 함수와 비슷하게 동작하지만, 몇 가지 중요한 차이점이 있습니다. 여기서 두 가지 방법의 차이점을 설명해 드리겠습니다.

클래스 (Class)

  1. 문법적 설탕 (Syntactic Sugar):

클래스는 사실 생성자 함수와 프로토타입 기반 상속을 쉽게 사용할 수 있도록 만든 문법적 설탕입니다. 즉, 클래스는 생성자 함수와 동일한 기능을 하지만, 더 직관적이고 읽기 쉬운 구문을 제공합니다.

  1. Class 선언 및 정의:

클래스를 선언하는 구문은 class 키워드를 사용합니다. 클래스 내부에는 생성자 메서드(constructor)와 여러 메서드를 정의할 수 있습니다.

  1. 엄격 모드 (Strict Mode):

클래스 내부의 코드는 자동으로 엄격 모드(strict mode)로 실행됩니다. 이는 코드의 오류를 더 잘 잡아내고, 잠재적인 문제를 방지하는 데 도움을 줍니다.

  1. 호이스팅:

클래스 선언은 호이스팅되지 않습니다. 클래스는 정의된 이후에만 사용할 수 있습니다.

  1. 메서드 정의:

클래스 내에서 정의된 메서드는 열거할 수 없는(enumerable) 속성으로 정의됩니다. 생성자 함수에서는 명시적으로 프로토타입에 메서드를 추가해야 합니다.

생성자 함수 (Constructor Function)

  1. 기존 방식 (ES6 이전):

ES6 이전에는 객체를 생성하는 일반적인 방법은 생성자 함수를 사용하는 것이었습니다. 생성자 함수는 일반 함수처럼 정의되지만, new 키워드를 사용하여 호출할 때 새로운 객체를 생성합니다.

  1. 프로토타입 기반 상속:

생성자 함수는 프로토타입을 사용하여 메서드와 속성을 상속합니다. 이를 통해 모든 인스턴스가 공유할 수 있는 메서드를 정의할 수 있습니다.

  1. 엄격 모드:

생성자 함수는 엄격 모드에서 자동으로 실행되지 않습니다. 명시적으로 'use strict';를 선언해야 엄격 모드가 적용됩니다.

  1. 호이스팅:

생성자 함수 선언은 호이스팅됩니다. 즉, 함수 선언 전에 호출할 수 있습니다.

  1. 메서드 정의:

생성자 함수에서 메서드를 정의하려면 프로토타입을 사용해야 합니다. 생성자 함수 내부에서 메서드를 정의하면 모든 인스턴스가 해당 메서드를 가지게 되어 메모리 낭비가 발생할 수 있습니다.

정적 메서드와 프로토타입 메서드의 차이

자바스크립트에서 정적 메서드와 프로토타입 메서드는 객체 지향 프로그래밍에서 메서드를 정의하는 두 가지 방법입니다.

정적 메서드 (Static Method)

정적 메서드는 클래스 자체에 정의된 메서드로, 클래스의 인스턴스가 아닌 클래스 자체에서 호출됩니다. 정적 메서드는 클래스의 특정 인스턴스와 무관하게 동작하며, 일반적으로 클래스 레벨의 유틸리티 함수를 정의할 때 사용됩니다.

  • 특징:

  • 클래스 자체에서 호출되며, 클래스의 인스턴스에서는 호출할 수 없습니다.

  • 주로 클래스 레벨의 동작을 정의할 때 사용됩니다.

프로토타입 메서드 (Prototype Method)

프로토타입 메서드는 클래스의 인스턴스에 의해 호출되는 메서드로, 모든 인스턴스가 공유하는 메서드입니다. 이는 객체 지향 프로그래밍에서 인스턴스 레벨의 동작을 정의하는 데 사용됩니다.

  • 특징:

  • 인스턴스에서 호출되며, 클래스의 모든 인스턴스가 공유합니다.

  • 주로 인스턴스 레벨의 동작을 정의할 때 사용됩니다.

주요 차이점

  1. 호출 방식:
  • 정적 메서드는 클래스 이름을 통해 직접 호출합니다.

  • 프로토타입 메서드는 클래스의 인스턴스를 통해 호출합니다.

  1. 속성 위치:
  • 정적 메서드는 클래스 자체에 정의됩니다.

  • 프로토타입 메서드는 클래스의 프로토타입 객체에 정의됩니다.

  1. 사용 목적:
  • 정적 메서드는 클래스 레벨에서 공통적으로 사용되는 유틸리티 함수나 로직을 정의할 때 유용합니다.

  • 프로토타입 메서드는 인스턴스 레벨에서 개별 객체가 수행해야 하는 동작을 정의할 때 사용됩니다.


호이스팅 (Hoisting)

  • 임시적 사각지대 (Temporal Dead Zone, TDZ)
  • 변수 선언 (Variable Declaration)
  • 함수 선언 (Function Declaration)
  • 변수 초기화 (Variable Initialization)

호이스팅(Hoisting)은 자바스크립트의 동작 방식 중 하나로, 변수와 함수 선언이 해당 스코프의 최상단으로 끌어올려지는 것처럼 동작하는 것을 의미합니다. 이는 변수와 함수를 선언하기 전에 참조할 수 있게 해줍니다. 그러나 변수의 할당은 호이스팅되지 않으며, 초기화는 원래 코드 위치에서 발생합니다.

변수 호이스팅과 함수 호이스팅의 차이점:

- 변수 호이스팅: 선언은 호이스팅되지만, 초기화는 호이스팅되지 않습니다.

- 함수 호이스팅: 선언과 초기화가 모두 호이스팅됩니다.

변수 선언 (Variable Declaration)

변수 선언은 변수를 정의하는 과정입니다. 자바스크립트에서는 var, let, const 키워드를 사용하여 변수를 선언합니다.

  • var 선언:

  • 함수 스코프를 가집니다.

  • 선언과 동시에 undefined로 초기화됩니다.

  • 호이스팅되어 스코프의 최상단으로 끌어올려진 것처럼 동작합니다.

  • let 선언:

  • 블록 스코프를 가집니다.

  • 선언 후 초기화 전까지 TDZ(임시적 사각지대)에 놓입니다.

  • 호이스팅되지만 초기화는 선언된 위치에서 이루어집니다.

  • const 선언:

  • 블록 스코프를 가집니다.

  • 선언 후 초기화 전까지 TDZ에 놓입니다.

  • 호이스팅되지만 초기화는 선언된 위치에서 이루어집니다.

  • 선언 시 반드시 초기화가 필요하며, 이후 재할당이 불가능합니다.

함수 선언 (Function Declaration)

함수 선언은 함수의 이름과 본문을 정의하는 과정입니다. 함수 선언은 호이스팅되어 해당 스코프의 최상단에서 정의된 것처럼 동작합니다. 함수 선언 방식은 다음과 같습니다:

- 호이스팅:

함수 선언은 스코프의 최상단으로 호이스팅됩니다. 따라서 함수 선언 이전에 호출해도 문제가 없습니다.

변수 초기화 (Variable Initialization)

변수 초기화는 변수에 값을 할당하는 과정입니다. 초기화는 선언과 동시에 이루어질 수도 있고, 나중에 별도로 이루어질 수도 있습니다.

  • var 초기화:

  • 선언과 동시에 undefined로 초기화됩니다. 이후 값을 할당할 수 있습니다.

  • let 초기화:

  • 선언 후 초기화 전까지 TDZ에 놓입니다. 초기화는 선언된 위치에서 이루어집니다.

  • const 초기화:

  • 선언과 동시에 초기화가 이루어져야 합니다. 초기화 후에는 재할당이 불가능합니다.

변수 (Variable)

  • var
  • let
  • const

const로 선언된 객체의 속성을 변경할 수 있는 이유를 설명해 주세요.

const 키워드는 변수를 상수로 선언할 때 사용되며, 선언된 변수의 값을 재할당할 수 없도록 합니다. 그러나, const로 선언된 변수가 객체를 참조하는 경우, 객체의 속성을 변경할 수 있습니다. 이는 const가 변수 자체의 참조를 고정하지만, 참조된 객체의 내용까지 고정하지 않기 때문입니다.

  • 참조의 고정:

const로 선언된 변수는 선언된 이후에 새로운 참조 값으로 재할당할 수 없습니다. 즉, 변수에 저장된 참조 주소는 변경되지 않습니다.

  • 객체의 속성 변경 가능:

객체는 참조 타입 데이터입니다. const로 선언된 변수에 객체가 할당된 경우, 해당 변수는 객체의 메모리 주소를 참조합니다. 객체의 속성은 그 메모리 주소 내에서 관리되므로, 객체의 속성 값은 변경될 수 있습니다. 이는 객체의 참조 주소는 동일하게 유지되지만, 참조된 객체의 내용은 변경될 수 있음을 의미합니다.

letconst의 임시적 사각지대(TDZ)란 무엇인가요?

임시적 사각지대(Temporal Dead Zone, TDZ)는 letconst로 선언된 변수가 호이스팅되지만, 초기화되기 전까지는 접근할 수 없는 상태를 말합니다. TDZ는 코드에서 변수가 선언되기 전까지 해당 변수에 접근하는 것을 방지하여 잠재적인 오류를 줄이는 역할을 합니다.

  • 호이스팅:

letconst 변수 선언은 호이스팅되지만, 선언된 위치에서 초기화됩니다. 이로 인해 코드 상에서 변수를 선언하기 전에 접근하려 하면 ReferenceError가 발생합니다.

  • 임시적 사각지대의 시작과 끝:

TDZ는 스코프의 시작(블록의 시작 또는 함수의 시작)부터 변수 선언의 실제 위치까지입니다. 변수가 선언되기 전까지는 TDZ에 있으며, 이 기간 동안 해당 변수에 접근하려 하면 오류가 발생합니다.

var

var 키워드는 ES6 이전에 변수를 선언하는 유일한 방법이었습니다.

  • 스코프 (Scope):

  • 함수 스코프를 가집니다. 즉, 함수 내에서 선언된 var 변수는 함수 전체에서 유효합니다.

  • 함수 외부에서 선언된 var 변수는 전역 스코프를 가집니다.

  • 호이스팅 (Hoisting):

  • 변수 선언이 호이스팅됩니다. 즉, 변수 선언이 해당 스코프의 최상단으로 끌어올려지는 것처럼 동작합니다.

  • 변수 선언 전에 접근하면 undefined가 반환됩니다.

  • 재할당:

  • 가능합니다.

let

let 키워드는 ES6에서 도입되었으며, 블록 스코프를 가집니다.

  • 스코프 (Scope):

  • 블록 스코프를 가집니다. 즉, 블록(\{\}) 내에서 선언된 let 변수는 그 블록 내에서만 유효합니다.

-호이스팅 (Hoisting):

  • 변수 선언이 호이스팅되지만, 초기화가 선언된 위치에서 이루어집니다. 선언 전에 접근하려 하면 ReferenceError가 발생합니다. 이 상태를 임시적 사각지대(TDZ, Temporal Dead Zone)라고 합니다.

  • 재할당:

  • 가능합니다.

const

const 키워드는 ES6에서 도입되었으며, 상수를 선언할 때 사용됩니다.

  • 스코프 (Scope):

  • 블록 스코프를 가집니다. let과 동일합니다.

  • 호이스팅 (Hoisting):

  • 변수 선언이 호이스팅되지만, 초기화가 선언된 위치에서 이루어집니다. 선언 전에 접근하려 하면 ReferenceError가 발생합니다. 이 상태를 임시적 사각지대(TDZ)라고 합니다.

  • 재할당:

  • 불가능합니다. 한 번 값을 할당하면 변경할 수 없습니다. 하지만, 객체나 배열의 경우 내부 속성은 변경할 수 있습니다.

특징varletconst
스코프함수 스코프블록 스코프블록 스코프
호이스팅선언과 초기화가 호이스팅됨선언은 호이스팅되나, 초기화는 선언 위치에서 이루어짐 (TDZ)선언은 호이스팅되나, 초기화는 선언 위치에서 이루어짐 (TDZ)
재할당가능가능불가능
사용 시기재할당이 필요하고, 함수 내 전역적으로 사용될 변수재할당이 필요하고, 블록 내에서만 유효할 변수재할당이 필요 없고, 블록 내에서만 유효할 변수