> 웹 프론트엔드 > JS 튜토리얼 > 클로저 공개: JavaScript의 숨겨진 영역 탐색

클로저 공개: JavaScript의 숨겨진 영역 탐색

DDD
풀어 주다: 2024-12-12 12:22:24
원래의
285명이 탐색했습니다.

Closures Unveiled: Exploring the Hidden Realms of JavaScript

목차

  • 코딩 혼란에서 벗어나기
  • 폐쇄란 정확히 무엇인가요?
  • Breaking Down: 폐쇄 공개
  • 실용적인 마법: 클로저를 사용한 캐싱 여정
  • 일반적인 함정과 이를 피하는 방법
  • 여정은 계속됩니다

코딩 혼란에서 탈출하기 ?‍♂️

귀하의 코드가 제멋대로여서 지저분해지고 정리된 상태를 유지하기를 거부하는 것 같은 느낌을 받은 적이 있습니까? 걱정하지 마세요. 우리 모두 거기에 가본 적이 있습니다. 노련한 마법사에게도 JavaScript는 까다로울 수 있습니다. 하지만 상황을 통제할 수 있는 비밀 무기가 있다고 말하면 어떻게 될까요? 폐쇄를 입력하세요.

클로저를 함수가 나중에 필요할 수 있는 변수와 메모리를 저장하는 마법의 배낭이라고 생각하세요. 이러한 작은 프로그래밍 마법은 코드를 체계적으로 유지하고, 상태를 깔끔하게 관리하며, 역동적이고 유연한 패턴을 열어줍니다.

클로저를 마스터하면 코드에서 새로운 차원의 강력함과 우아함을 누릴 수 있습니다. 그러니 코딩 지팡이(또는 진한 커피 한잔)를 들고 함께 숨겨진 영역으로 모험을 떠나보세요. ?✨


클로저란 정확히 무엇입니까? ?

클로저는 단순히 원래 환경이 더 이상 존재하지 않은 후에도 원래 환경의 변수를 기억하는 함수입니다. JavaScript는 이러한 변수를 버리는 대신 필요할 때 호출할 수 있도록 숨겨둡니다.

const createCounter = () => {
    let count = 0; // Private variable in the closure's secret realm

    return () => {
        count++; // Whispers an increment to the hidden counter
        return count; // Reveal the mystical number
    };
}

// Summoning our magical counter
const counter = createCounter();

console.log(counter()); // Outputs: 1
console.log(counter()); // Outputs: 2
console.log(counter()); // Outputs: 3
console.log(counter.count);  // Outputs: undefined (`count` is hidden!) ?️‍♀️
로그인 후 복사
로그인 후 복사
로그인 후 복사

createCounter 실행이 완료되더라도 내부 함수는 count에 대한 액세스 권한을 유지합니다. 이 "메모리"는 클로저의 핵심입니다. 즉, 데이터를 안전하게 유지하고 강력하고 유연한 코드를 가능하게 합니다. ?✨


Breaking Down: 폐쇄 공개?

클로저가 마술처럼 느껴질 수도 있지만 이는 단순히 JavaScript가 범위메모리를 처리하는 방식의 결과일 뿐입니다. 모든 함수는 함수가 정의된 컨텍스트인 어휘적 환경에 대한 링크를 전달합니다.

? 어휘 환경은 해당 범위에서 액세스할 수 있는 항목을 정의하는 변수 바인딩의 구조화된 기록입니다. 주어진 블록이나 함수 내에 어떤 변수와 함수가 있는지 보여주는 지도와 같습니다.

클로저는 역동적인 스토리텔러인가요?

클로저는 하나의 값에만 고정되지 않습니다. 시간에 따른 변화를 추적합니다. 외부 범위의 변수가 업데이트되면 클로저에 새 값이 표시됩니다.

const createCounter = () => {
    let count = 0; // Private variable in the closure's secret realm

    return () => {
        count++; // Whispers an increment to the hidden counter
        return count; // Reveal the mystical number
    };
}

// Summoning our magical counter
const counter = createCounter();

console.log(counter()); // Outputs: 1
console.log(counter()); // Outputs: 2
console.log(counter()); // Outputs: 3
console.log(counter.count);  // Outputs: undefined (`count` is hidden!) ?️‍♀️
로그인 후 복사
로그인 후 복사
로그인 후 복사

클로저가 마법 같은 필수 요소인 이유는 무엇입니까? ?

클로저는 액세스가 제어된 개인 변수를 생성하여 캡슐화를 가능하게 하고, 전역에 의존하지 않고 여러 호출에서 상태를 관리하고, 팩토리, 콜백과 같은 동적 동작을 지원합니다. , 그리고 후크.

React와 같은 프레임워크는 이러한 기능을 활용하여 기능적 구성 요소를 상태 비저장 상태로 유지하면서 useState와 같은 후크로 상태를 관리합니다. 모두 클로저의 마법 덕분입니다.


실용적인 주문 제작: 클로저를 사용한 캐싱 여행 ?‍♂️

클로저는 상태를 저장할 수 있으므로 비용이 많이 드는 작업의 결과 캐싱과 같은 주문에 이상적입니다. 이를 단계별로 살펴보고 주문을 강화해 봅시다.

1단계: ?️ 메모리 키퍼 – 기본 캐싱

첫 번째 주문은 간단하면서도 강력합니다. 바로 기억을 지키는 사람입니다. 동일한 입력을 다시 요청하면 즉시 캐시된 결과를 반환합니다.

// A variable in the global magical realm
let multiplier = 2;

const createMultiplier = () => {
  // The inner function 'captures' the essence of the outer realm
  return (value: number): number => value * multiplier;
}; 


// Our magical transformation function
const double = createMultiplier();

console.log(double(5)); // Outputs: 10
multiplier = 3;
console.log(double(5)); // Outputs: 15 (The magic adapts!) ✨
로그인 후 복사
로그인 후 복사

2단계: ⏳ 사라지는 주문 – 캐시 만료

그러나 일부 주문은 너무 강력해서 영원히 지속되지 않습니다. 오래된 추억을 잊는 능력으로 캐시를 강화합시다. 값뿐만 아니라 그 마법의 수명도 저장하기 위해 CacheEntry를 생성하겠습니다.

(이전 아이디어를 어떻게 구축하고 있는지 확인하세요. 클로저를 사용하면 경로를 잃지 않고 복잡성을 쉽게 추가할 수 있습니다.)

function withCache(fn: (...args: any[]) => any) {
  const cache: Record<string, any> = {};

  return (...args: any[]) => {
    const key = JSON.stringify(args);

    // Have we encountered these arguments before?
    if (key in cache) return cache[key]; // Recall of past magic! ?

    // First encounter? Let's forge a new memory
    const result = fn(...args);
    cache[key] = result;

    return result;
  };
}

// Example usage
const expensiveCalculation = (x: number, y: number) => {
  console.log('Performing complex calculation');
  return x * y;
};

// Summoning our magical cached calculation
const cachedCalculation = withCache(expensiveCalculation);

console.log(cachedCalculation(4, 5)); // Calculates and stores the spell
console.log(cachedCalculation(4, 5)); // Uses cached spell instantly
로그인 후 복사
로그인 후 복사

3단계: ? 비동기 매직 – 약속 처리

때때로 주문에는 시간이 필요합니다. 예를 들어 멀리 있는 오라클(또는 API)이 응답할 때까지 기다리는 것과 같습니다. 우리의 주문도 그것을 처리할 수 있습니다. Promise를 기다리고, 해결된 값을 저장하고, 나중에 반환합니다. 반복적인 가져오기는 필요하지 않습니다.

type CacheEntry<T> = { value: T; expiry: number; };

function withCache<T extends (...args: any[]) => any>(
  fn: T,
  expirationMs: number = 5 * 60 * 1000, // Default 5 minutes
) {
  const cache = new Map<string, CacheEntry<ReturnType<T>>>();

  return (...args: Parameters<T>): ReturnType<T> => {
    const key = JSON.stringify(args);
    const now = Date.now(); // Current magical moment
    const cached = cache.get(key);

    // Is our magical memory still vibrant?
    if (cached && now < cached.expiry) return cached.value;

    // The memory has faded; it’s time to create new ones!
    const result = fn(...args);
    cache.set(key, { value: result, expiry: now + expirationMs });

    return result;
  };
}

// ...

const timeLimitedCalc = 
  withCache(expensiveCalculation, 3000); // 3-second cache

console.log(timeLimitedCalc(4, 5)); // Stores result with expiration
console.log(timeLimitedCalc(4, 5)); // Returns cached value before expiry

setTimeout(() => {
  console.log(timeLimitedCalc(4, 5)); // Recalculates after expiration
}, 3000);
로그인 후 복사
로그인 후 복사

여기에서 전체 주문을 살펴보세요.

마법사의 도전??‍♂️

우리의 캐싱 마법은 강력하지만 이는 시작에 불과합니다. 코드 레벨을 높일 수 있다고 생각하시나요? 오류 처리를 추가하거나, 마법 같은 메모리 정리를 구현하거나, 보다 정교한 캐싱 전략을 만드는 것을 고려해 보세요. 진정한 코딩 기술은 실험, 한계 확장, 가능성 재구상에 있습니다! ??


일반적인 함정과 이를 피하는 방법 ?️

폐쇄는 강력하지만 최고의 주문에도 위험이 따릅니다. 클로저를 자신있게 사용하는 데 도움이 되는 몇 가지 일반적인 함정과 솔루션을 알아보세요.

함정 #1: ?️ 비열한 루프 함정

코딩 인터뷰에서 자주 등장하는 고전적인 JavaScript 문제는 ​​루프, 특히 루프 변수 및 클로저를 처리하는 방법과 관련이 있습니다.

// ...

// The memory has faded; it’s time to create new ones!
const result = fn(...args);

if (result instanceof Promise) {
  return result.then((value) => {
    cache.set(key, { value, expiry: now + expirationMs });
    return value;
  });
}

// ...
로그인 후 복사
로그인 후 복사

위의 예에서는 var가 모든 클로저에 대해 단일 공유 변수를 생성하기 때문에 숫자 5를 5번 기록합니다.

해결책 1: 블록 범위 지정을 보장하려면 let을 사용하세요.
let 키워드는 각 반복마다 새로운 블록 범위 변수를 생성하므로 클로저가 올바른 값을 캡처합니다.

const createCounter = () => {
    let count = 0; // Private variable in the closure's secret realm

    return () => {
        count++; // Whispers an increment to the hidden counter
        return count; // Reveal the mystical number
    };
}

// Summoning our magical counter
const counter = createCounter();

console.log(counter()); // Outputs: 1
console.log(counter()); // Outputs: 2
console.log(counter()); // Outputs: 3
console.log(counter.count);  // Outputs: undefined (`count` is hidden!) ?️‍♀️
로그인 후 복사
로그인 후 복사
로그인 후 복사

해결책 2: IIFE(즉시 호출 함수 표현식)를 사용하세요.
IIFE는 각 반복마다 새로운 범위를 생성하여 루프 내에서 적절한 변수 처리를 보장합니다.

// A variable in the global magical realm
let multiplier = 2;

const createMultiplier = () => {
  // The inner function 'captures' the essence of the outer realm
  return (value: number): number => value * multiplier;
}; 


// Our magical transformation function
const double = createMultiplier();

console.log(double(5)); // Outputs: 10
multiplier = 3;
console.log(double(5)); // Outputs: 15 (The magic adapts!) ✨
로그인 후 복사
로그인 후 복사

보너스 팁: ? 기능적 트릭.
이 주문을 아는 마법사는 거의 없으며, 솔직히 말해서 코딩 인터뷰에서 이 주문이 언급되는 것을 거의 본 적이 없습니다. setTimeout이 추가 인수를 콜백에 직접 전달할 수 있다는 것을 알고 계셨습니까?

function withCache(fn: (...args: any[]) => any) {
  const cache: Record<string, any> = {};

  return (...args: any[]) => {
    const key = JSON.stringify(args);

    // Have we encountered these arguments before?
    if (key in cache) return cache[key]; // Recall of past magic! ?

    // First encounter? Let's forge a new memory
    const result = fn(...args);
    cache[key] = result;

    return result;
  };
}

// Example usage
const expensiveCalculation = (x: number, y: number) => {
  console.log('Performing complex calculation');
  return x * y;
};

// Summoning our magical cached calculation
const cachedCalculation = withCache(expensiveCalculation);

console.log(cachedCalculation(4, 5)); // Calculates and stores the spell
console.log(cachedCalculation(4, 5)); // Uses cached spell instantly
로그인 후 복사
로그인 후 복사

함정 #2: ? 메모리 누수 - 소리 없는 위협

클로저는 외부 범위에 대한 참조를 유지합니다. 이는 변수가 예상보다 오래 머무르고 메모리 누수로 이어질 수 있음을 의미합니다.

type CacheEntry<T> = { value: T; expiry: number; };

function withCache<T extends (...args: any[]) => any>(
  fn: T,
  expirationMs: number = 5 * 60 * 1000, // Default 5 minutes
) {
  const cache = new Map<string, CacheEntry<ReturnType<T>>>();

  return (...args: Parameters<T>): ReturnType<T> => {
    const key = JSON.stringify(args);
    const now = Date.now(); // Current magical moment
    const cached = cache.get(key);

    // Is our magical memory still vibrant?
    if (cached && now < cached.expiry) return cached.value;

    // The memory has faded; it’s time to create new ones!
    const result = fn(...args);
    cache.set(key, { value: result, expiry: now + expirationMs });

    return result;
  };
}

// ...

const timeLimitedCalc = 
  withCache(expensiveCalculation, 3000); // 3-second cache

console.log(timeLimitedCalc(4, 5)); // Stores result with expiration
console.log(timeLimitedCalc(4, 5)); // Returns cached value before expiry

setTimeout(() => {
  console.log(timeLimitedCalc(4, 5)); // Recalculates after expiration
}, 3000);
로그인 후 복사
로그인 후 복사

여기서 무슨 일이 일어나고 있나요? 클로저는 작은 부분만 필요하더라도 전체 데이터 변수를 유지하므로 상당한 리소스가 낭비될 수 있습니다.

해결책은 어떤 클로저가 캡처하는지 신중하게 관리하고 불필요한 참조를 명시적으로 해제하는 것입니다. 이렇게 하면 필요할 때만 대규모 데이터세트를 로드하고 정리 방법을 통해 사전에 해제할 수 있습니다.

// ...

// The memory has faded; it’s time to create new ones!
const result = fn(...args);

if (result instanceof Promise) {
  return result.then((value) => {
    cache.set(key, { value, expiry: now + expirationMs });
    return value;
  });
}

// ...
로그인 후 복사
로그인 후 복사

함정 #3: ?️ 돌연변이 대혼란

공유 상태가 변경되면 클로저로 인해 예기치 않은 동작이 발생할 수 있습니다. 단순한 참고사항처럼 보이는 내용이 의도하지 않은 부작용을 초래할 수 있습니다.

for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i); // Logs 5, five times ?
  }, i * 1000); 
}
로그인 후 복사

여기서 무슨 일이 일어나고 있나요? getUsers 메소드는 사용자 배열을 노출하여 캡슐화를 깨고 외부 수정으로 인한 의도하지 않은 부작용의 위험이 있습니다.

해결책은 내부 상태의 복사본을 반환하는 것입니다. 이는 외부 수정을 방지하고 데이터 무결성을 유지하며 클로저의 내부 논리를 보호합니다.

for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i); // Works as expected ?
  }, i * 1000);
}
로그인 후 복사

마감 관리의 황금률?

  1. 캡처에 대해 의도적이어야 합니다: 불필요한 종속성과 메모리 문제를 피하기 위해 어떤 클로저가 캡처하는지 이해하세요.
  2. 현명하게 변수 범위 지정: 공유 참조 버그를 방지하고 올바른 변수 캡처를 보장하려면 블록 범위 지정을 사용하세요.
  3. 불변성 수용: 불변 패턴을 선호하고 공유 상태를 변경하는 대신 복사본을 반환하여 부작용을 방지합니다.
  4. 정리 연습: 특히 크거나 민감한 데이터의 경우 메모리 누수를 방지하기 위해 불필요한 참조를 해제합니다.

이러한 기술을 익히면 클로저의 마법을 자신 있게 발휘하는 데 도움이 됩니다. 진정한 숙달은 회피가 아니라 이해에 있습니다. ✨


여행은 계속됩니다

클로저는 처음에는 복잡해 보일 수 있지만 더 우아하고 효율적인 코드를 작성할 수 있는 잠재력을 열어줍니다. 단순한 기능을 지속적이고 상태가 있는 엔터티로 전환함으로써 클로저는 시간과 공간에 걸쳐 비밀을 우아하게 공유할 수 있습니다. 이 강력한 기능은 JavaScript를 단순한 스크립트 언어에서 복잡한 문제를 해결하기 위한 강력하고 유연한 도구로 끌어올립니다.

당신의 여정은 여기서 끝나지 않습니다. 비동기 패턴, 함수형 프로그래밍, JavaScript 엔진의 내부 작동 방식에 대해 자세히 알아보세요. 각 단계에서 이 매혹적인 언어의 더 많은 레이어가 드러나 새로운 아이디어와 솔루션이 탄생합니다.

결국 진정한 숙달은 호기심과 탐구에서 비롯됩니다. 귀하의 코드가 우아하고 효율적이며 약간 마술적이기를 바랍니다. ?

위 내용은 클로저 공개: JavaScript의 숨겨진 영역 탐색의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿