JavaScript에서 클로저를 실제로 사용하는 것은 무엇입니까?


279

노력 자바 스크립트 폐쇄 주위에 내 머리를 정리하고 내 어려운합니다.

내부 함수를 반환하면 직계 부모에 정의 된 모든 변수에 액세스 할 수 있습니다.

이것이 어디에서 유용할까요? 어쩌면 나는 아직 내 머리를 가지고 있지 않았을 것입니다. 온라인에서 본 대부분의 예제는 실제 코드를 제공하지 않고 모호한 예제를 제공합니다.

누군가 나에게 클로저의 실제 사용을 보여줄 수 있습니까?

예를 들어 이것입니까?

var warnUser = function (msg) {
    var calledCount = 0;
    return function() {
       calledCount++;
       alert(msg + '\nYou have been warned ' + calledCount + ' times.');
    };
};

var warnForTamper = warnUser('You can not tamper with our HTML.');
warnForTamper();
warnForTamper();

17
당신의 가장 어려운 시도에 +1 :-) 클로저는 시작하기가 정말 어려워 보일 수 있습니다. 당신이 그들을 끊으면 즉시 훨씬 더 나은 코더가 될 것입니다.
Andy E

7
방금 JavaScript를 사용하여 helfpul을 찾을 수 있는 블로그 게시물을 작성했습니다 .
Skilldrick

@ 스킬 드릭. 링크가 죽었습니다 ... 또한이 실용적인 예가 매우 유용하다는 것을 알았습니다. youtube.com/watch?v=w1s9PgtEoJs .
Abhi

답변:


240

클로저를 사용하여 다음과 같은 작업을 수행했습니다.

a = (function () {
    var privatefunction = function () {
        alert('hello');
    }

    return {
        publicfunction : function () {
            privatefunction();
        }
    }
})();

보시다시피 a이제 클로저 내부에만 존재하는를 호출 하는 메소드 publicfunction( a.publicfunction())가 있는 객체 privatefunction가 있습니다. 당신은 할 수 없습니다 호출 privatefunction(예 : 직접 a.privatefunction()단지) publicfunction().

최소한의 예이지만 용도가 보입니까? 우리는 이것을 공개 / 비공개 방법을 시행하기 위해 사용했습니다.


26
아, 이것이 클로저라면, 나는 그것을 알지 못하고 클로저를 사용했습니다! 나는 종종 그런 식으로 다른 함수를 넣은 다음 예제와 같이 객체 리터럴을 반환하여 필요한 것을 공개합니다.
alex

1
예, 보시다시피, 반환하는 객체는 변수와 함수를 참조하기 때문에 함수의 컨텍스트를 유지합니다. 그래서 당신은 그것들을 사용하고 있었고, 당신은 그것을 몰랐습니다.
Francisco Soto

9
기술적으로 브라우저에서 Javascript로 만드는 모든 기능은 윈도우 객체가 바인딩되어 있기 때문에 닫힙니다.
Adam Gent

9
나는 이것이 오래된 질문이라는 것을 알고 있지만, 나에게 이것은 여전히 ​​적절한 대답을 제공하지 못한다. 왜 함수를 직접 호출하지 않습니까? 왜 개인 기능이 필요합니까?
qodeninja

5
예제에는 함수 만 있지만 외부에서 액세스 할 수 없는 변수도있을 수 있습니다 . say : var obj = (함수 () {var value = 0; return {get : function () {return value;}, set : function (val) {value = val;}}}) (); obj.set (20); obj.get (); => 20 등
Francisco Soto

211

사용자 가 웹 페이지 에서 버튼클릭 한 횟수계산 한다고 가정합니다 .
이를 위해 onclick버튼의 이벤트에서 변수의 개수를 업데이트 하는 함수를 트리거 합니다.

<button onclick="updateClickCount()">click me</button>  

이제 다음과 같은 많은 접근 방식이있을 수 있습니다.

1) 전역 변수카운터 를 늘리는 함수를 사용할 수 있습니다 .

var counter = 0;

function updateClickCount() {
    ++counter;
    // do something with counter
}

그러나 함정은 페이지의 모든 스크립트updateClickCount() 가을 호출하지 않고 카운터를 변경할 수 있다는 것 입니다.


2) 이제 함수 내부에서 변수를 선언하려고 생각할 수 있습니다.

function updateClickCount() {
    var counter = 0;
    ++counter;
    // do something with counter
}

그러나 이봐! updateClickCount()기능이 호출 될 때마다 카운터는 다시 1로 설정됩니다.


3) 중첩 함수 에 대한 생각 ?

중첩 함수는 "위"범위에 액세스 할 수 있습니다.
이 예에서 내부 함수 updateClickCount()는 상위 함수의 카운터 변수에 액세스 할 수 있습니다.countWrapper()

function countWrapper() {
    var counter = 0;
    function updateClickCount() {
    ++counter;
    // do something with counter
    }
    updateClickCount();    
    return counter; 
}

updateClickCount()외부 에서 함수에 도달 할 수 있고 counter = 0매번 한 번만 실행할 수있는 방법을 찾아야 한다면 카운터 딜레마를 해결할 수 있습니다 .


4) 구조 폐쇄! (자기 호출 기능) :

 var updateClickCount=(function(){
    var counter=0;

    return function(){
     ++counter;
     // do something with counter
    }
})();

자체 호출 기능은 한 번만 실행됩니다. counter를 0으로 설정하고 함수 표현식을 리턴합니다.

이런 식 updateClickCount으로 기능이됩니다. "멋진"부분은 부모 범위의 카운터에 액세스 할 수 있다는 것입니다.

이것을 JavaScript 클로저 라고합니다 . 함수가 " private "변수 를 가질 수 있습니다 .

counter익명 함수의 범위에 의해 보호되며, 단지 추가 기능을 사용하여 변경할 수 있습니다!

클로저에 대한보다 생생한 예 :

<script>
        var updateClickCount=(function(){
    	var counter=0;
    
    	return function(){
    	++counter;
    	 document.getElementById("spnCount").innerHTML=counter;
    	}
      })();
    </script>

    <html>
	 <button onclick="updateClickCount()">click me</button>
	  <div> you've clicked 
		<span id="spnCount"> 0 </span> times!
	 </div>
    </html>


참조 : https://www.w3schools.com/js/js_function_closures.asp


50
이것은 나에게 말을 만든 첫 번째 대답은 "아, 그건 내가 클로저를 사용하는 이유!"
악마의 옹호자

6
u my day :)
JerryGoyal

15
방금 폐쇄에 대한 w3schools 페이지를 읽은 다음 자세한 내용을 보려면 여기로 왔습니다. 이것은 w3schools 페이지와 동일합니다 : w3schools.com/js/js_function_closures.asp
tyelford

1
@JerryGoyal 두 개의 별도 버튼으로 작동하게 할 수 있습니까? 나는 2 가지 변수 (함수의 사본)에 의지하지 않고 어떻게 주요 이점 / 편의점을 제거하는 것처럼 보이는지 알 수 없습니다.
Tyler Collier

2
좋은 대답입니다. 참고 폐쇄하지 않는하지만 필요 자동 호출 기능으로, 그러나 수 있습니다 합니다. 클로저는 경우 이다 (즉, 즉시 함수 후 ()를 추가로 불림)자가 호출은,이 방법은 리턴 값을 즉시 계산보다는되는 함수 반환되는 반환 값이 계산되고 나중에 함수가 호출되면. 클로저는 실제로 다른 함수 내의 함수일 수 있으며, 주요 특징은 변수 및 메서드를 포함하여 부모 함수의 범위에 액세스 할 수 있다는 것입니다.
Chris Halcrow

69

당신이주는 예는 훌륭한 것입니다. 클로저는 우려를 매우 명확하게 분리 할 수있는 추상화 메커니즘입니다. 예는 의미론 (오류보고 API)에서 계측 (계산 호출)을 분리하는 경우입니다. 다른 용도는 다음과 같습니다.

  1. 매개 변수화 된 동작을 알고리즘에 전달 (클래식 고차 프로그래밍) :

    function proximity_sort(arr, midpoint) {
        arr.sort(function(a, b) { a -= midpoint; b -= midpoint; return a*a - b*b; });
    }
  2. 객체 지향 프로그래밍 시뮬레이션 :

    function counter() {
        var a = 0;
        return {
            inc: function() { ++a; },
            dec: function() { --a; },
            get: function() { return a; },
            reset: function() { a = 0; }
        }
    }
  3. jQuery의 이벤트 처리 및 AJAX API와 같은 이국적인 흐름 제어를 구현합니다.


3
( int?) 마지막으로, JavaScript는 오리 형식 언어였습니다. 아마도 당신은 자바를 생각하고 있었습니까?
Hello71

1
@ Hello71 : 나는 JavaScript를 생각하고 있었지만 오래된 습관은 열심히 죽습니다. 잘 잡았습니다.
Marcelo Cantos

2
@MarceloCantos 카운터 구현에서 쉼표를 잊어 버린 것처럼 보입니다. 게시물을 수정하여 수정했습니다. 괜찮습니다 :)
Natan Streppel

2
@Streppel : 잘 잡아! 내 코드를 개선하게되어 기쁩니다. :-)
Marcelo Cantos

# 1을 이해하려고 시도합니다. 근접성 정렬을 어떻게 호출 하시겠습니까?
Dave2081

26

나는이 질문에 대답하는 데 늦었지만 2018 년에도 여전히 답을 찾는 사람에게 도움이 될 수 있습니다.

자바 스크립트 클로저를 사용 하여 애플리케이션에서 스로틀디 바운스 기능 을 구현할 수 있습니다 .

조절 :

제한은 시간이 지남에 따라 함수를 호출 할 수있는 최대 횟수로 제한합니다. "이 기능은 100 밀리 초마다 최대 한 번 실행"과 같이

코드 :

const throttle = (func, limit) => {
  let isThrottling
  return function() {
    const args = arguments
    const context = this
    if (!isThrottling) {
      func.apply(context, args)
      isThrottling = true
      setTimeout(() => isThrottling = false, limit)
    }
  }
}

수신 거부 :

Debouncing은 함수가 호출되지 않고 일정 시간이 지날 때까지 함수에 제한을 두지 않습니다. "이 함수를 호출하지 않고 100 밀리 초가 지난 경우에만 실행"과 같이

암호:

const debounce = (func, delay) => {
  let debouncing
  return function() {
    const context = this
    const args = arguments
    clearTimeout(debouncing)
    debouncing = setTimeout(() => func.apply(context, args), delay)
  }
}

보시다시피 모든 웹 앱은 부드러운 UI 경험 기능을 제공해야하는 두 가지 아름다운 기능을 구현하는 데 도움이되었습니다.

누군가에게 도움이되기를 바랍니다.


18

예, 이것이 유용한 폐쇄의 좋은 예입니다. warnUser에 대한 호출 calledCount은 해당 범위에 변수를 작성하고 변수에 저장된 익명 함수를 리턴 warnForTamper합니다. calledCount 변수를 사용하는 클로저가 여전히 있기 때문에 함수가 종료 될 때 삭제되지 않으므로,에 대한 각 호출 warnForTamper()은 범위가 지정된 변수를 증가시키고 값을 경고합니다.

StackOverflow에서 가장 일반적인 문제는 누군가가 각 루프에서 증가하는 변수의 사용을 "지연"하려는 경우이지만 변수의 범위가 지정되므로 변수에 대한 각 참조는 루프가 끝난 후 발생합니다. 변수의 종료 상태 :

for (var i = 0; i < someVar.length; i++)
    window.setTimeout(function () { 
        alert("Value of i was "+i+" when this timer was set" )
    }, 10000);

이로 인해 모든 경고 i는 루프가 종료 될 때 증가한 값 과 동일한 값을 표시합니다 . 해결책은 변수에 대한 별도의 범위 인 새 클로저를 만드는 것입니다. 이는 즉시 실행되는 익명 함수를 사용하여 수행 할 수 있습니다.이 함수는 변수를 수신하고 상태를 인수로 저장합니다.

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 

흥미로운 -1, 이것이 "자바 스크립트에서 클로저를위한 실제적인 사용"이 아닌 것 같아요?
Andy E

1
나는 그것을 읽었을 때 약간의 사용을 발견하여 downvote 전에 +1을 수여했다.
alex

1
@ alex : 감사합니다. 공감대를 보았습니다. 나는 여기 SO에서 익명의 downvotes에 익숙해졌습니다. 내가 부정확하거나 잘못된 것을 말했는지 알고 싶기 때문에 나에게 성가신 일이며, 그들은 당신이 자신의 대답에 대한 더 나은 가시성을 원하는 다른 응답자에 의해 당신이 다운 투표했다고 생각하는 경향이 있습니다. 다행히도 나는 복수형이 아니다 ;-)
Andy E

1
나는 이것이 JavaScript가 깨진 블록 범위에 대한 해결 방법이라고 생각합니다. var j = i를 추가 할 수 있어야합니다. 첫 번째 setTimeout 전에 경고를 사용하여 해당 j를 사용하십시오. 다른 해결 방법은 'with'를 다음과 같이 사용하는 것입니다. for (var i = 0; i <someVar.length; i ++) {with ({i : i}) {window.setTimeout (function () {alert ( "Value of i)는 "+ I +"이 타이머}) "로 설정했을 때, 100이었다;}}
davidbuttar

1
@AndyE Funny이 옳지 않을 수도 있습니다. 나는 종종 사람들이이 페이지의 많은 답변과 같이 폐쇄를 설명하기 위해 자체 호출 기능을 사용한다는 것을 알았습니다. 그러나 setTimeout의 콜백 함수도 클로저입니다. 콜백에서 다른 로컬 변수에 액세스 할 수 있으므로 "실제 사용"으로 간주 될 수 있습니다. 클로저에 대해 배울 때 이것이 유용하다는 것을 깨달았습니다. 클로저는 아케이드 JavaScript 패턴뿐만 아니라 모든 곳에 있습니다.
antoine

14

특히 JavaScript (또는 모든 ECMAScript) 언어에서 클로저는 인터페이스를 표시하면서 기능 구현을 숨기는 데 유용합니다.

예를 들어, 날짜 클래스 유틸리티 메소드를 작성 중이고 사용자가 색인으로 요일 이름을 검색 할 수 있지만 사용자가 후드 아래에서 사용하는 이름 배열을 수정하지 못하게한다고 가정하십시오.

var dateUtil = {
  weekdayShort: (function() {
    var days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
    return function(x) {
      if ((x != parseInt(x)) || (x < 1) || (x > 7)) {
        throw new Error("invalid weekday number");
      }
      return days[x - 1];
    };
  }())
};

점을 유의 days배열은 단순히의 속성으로 저장 될 수있는 dateUtil객체하지만 그것은 스크립트의 사용자가 볼 것 그들이 원한다면 그들은 심지어 심지어 소스 코드를 필요로하지 않고, 그것을 변경할 수 있습니다. 그러나 날짜 조회 기능을 반환하는 익명 함수로 묶여 있기 때문에 조회 기능을 통해서만 액세스 할 수 있으므로 이제 변조 방지됩니다.


2
바보처럼 들릴지 모르지만 JavaScript 파일 자체를 열고 구현을 볼 수 없었습니까?
itsmichaelwang

1
@ Zapurdead : 예, 물론 구현을 수는 있지만 소스 코드를 직접 수정하지 않고는 실수로 또는 의도적으로 구현을 변경할 수 없습니다 . Java의 보호 멤버와 비교할 수 있다고 가정합니다.
maerics

6

모질라 개발자 네트워크실용 폐쇄 섹션이 있습니다 .


이것을 통해, 나는 전체를 제거하는 것처럼 return function ()...코드가 여전히 잘 작동 하는 것처럼 그것이 어떻게 "실용적"인지 알지 못한다 . 폐쇄는 nessecary가 아니다
가발

@James_Parsons 그런 다음 예제에서했던 것처럼 이벤트 핸들러에 할당 할 수 없습니다.
alex

5

클로저에 대한 또 다른 일반적인 용도 this는 메소드에서 특정 객체 에 바인딩 하여 다른 곳 (예 : 이벤트 핸들러)으로 호출 할 수 있도록하는 것입니다.

function bind(obj, method) {
    if (typeof method == 'string') {
        method = obj[method];
    }
    return function () {
        method.apply(obj, arguments);
    }
}
...
document.body.addEventListener('mousemove', bind(watcher, 'follow'), true);

mousemove 이벤트가 발생할 때마다 watcher.follow(evt)호출됩니다.

클로저는 고차 함수의 필수 부분으로, 유사하지 않은 부분을 매개 변수화하여 여러 유사 함수를 단일 고차 함수로 다시 쓰는 매우 일반적인 패턴을 허용합니다. 추상적 인 예로서,

foo_a = function (...) {A a B}
foo_b = function (...) {A b B}
foo_c = function (...) {A c B}

된다

fooer = function (x) {
    return function (...) {A x B}
}

여기서 A와 B는 구문 단위가 아니라 소스 코드 문자열 (문자열 리터럴이 아님)입니다.

구체적인 예는 " 함수로 자바 스크립트 간소화 "를 참조하십시오 .


5

여기에 여러 번 말하고 싶은 인사가 있습니다. 클로저를 만들면 해당 함수를 호출하여 인사말을 녹음 할 수 있습니다. 클로저를 만들지 않으면 매번 내 이름을 전달해야합니다.

폐쇄없이 ( https://jsfiddle.net/lukeschlangen/pw61qrow/3/ ) :

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";
  console.log(message);
}

greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Billy", "Bob");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");
greeting("Luke", "Schlangen");

폐쇄 ( https://jsfiddle.net/lukeschlangen/Lb5cfve9/3/ ) :

function greeting(firstName, lastName) {
  var message = "Hello " + firstName + " " + lastName + "!";

  return function() {
    console.log(message);
  }
}

var greetingBilly = greeting("Billy", "Bob");
var greetingLuke = greeting("Luke", "Schlangen");

greetingBilly();
greetingBilly();
greetingBilly();
greetingLuke();
greetingLuke();
greetingLuke();

1
확실하지는 않지만 클로저가 없으면 u로 호출 할 수 있습니다. var grretBilly = greeting ( "Billy", "Bob"); grretBilly ()를 호출하십시오. 그것은 여전히 ​​똑같 을까요 ?? 비록 폐쇄를 만들거나 다른 문제는 아니지만 매번 이름을 전달하는 것은 여기서 문제가되지 않습니다.
user2906608

4

객체 지향적 의미에서 클래스를 인스턴스화한다는 개념에 익숙하다면 (즉, 해당 클래스의 객체를 만드는 것) 클로저를 이해하는 데 가깝습니다.

이것을 두 가지 Person 객체로 인스턴스화 할 때 클래스 멤버 변수 "Name"이 인스턴스간에 공유되지 않는다는 것을 알고 있습니다. 각 객체에는 자체 '복사본'이 있습니다. 마찬가지로 클로저를 만들 때 자유 변수 (위 예제에서 'Count')는 함수의 '인스턴스'에 바인딩됩니다.

warnUser 함수가 반환하는 모든 함수 / 클로저 ( 고차 함수 임)는 클로저가 '초기화'를 동일한 초기 값 (0)으로 바인딩하지만 클로저를 만들 때 종종 개념적 도약이 약간 방해한다고 생각합니다. 클래스의 생성자에 다른 값을 전달하는 것과 같이 다른 이니셜 라이저를 고차 함수에 전달하는 것이 더 유용합니다.

따라서 'calledCount'가 특정 값에 도달하면 사용자 세션을 종료한다고 가정합니다. 요청이 로컬 네트워크에서 들어오는 지 또는 나쁜 인터넷에서 온 것인지에 따라 다른 값을 원할 수 있습니다 (예, 고안된 예입니다). 이를 달성하기 위해, 호출 된 count의 다른 초기 값을 warnUser에 전달할 수 있습니다 (예 : -3 또는 0?).

문헌의 문제점 중 일부는이를 기술하는 데 사용 된 명명법 ( "어휘 범위", "자유 변수")입니다. 당신을 속이게하지 마라. 폐쇄는 생각보다 단순하다. ... prima facie ;-)


3

여기에 전자 상거래 사이트 나 다른 많은 사이트에서도 사용할 수있는 폐쇄 개념의 간단한 예가 있습니다. 예제와 함께 jsfiddle 링크를 추가하고 있습니다. 여기에는 3 개의 작은 제품 목록과 1 개의 카트 카운터가 있습니다.

JS 피들

//Counter clouser implemented function;
var CartCouter = function(){
	var counter = 0;
  function changeCounter(val){
  	counter += val
  }
  return {
  	increment: function(){
    	changeCounter(1);
    },
    decrement: function(){
    changeCounter(-1);
    },
    value: function(){
    return counter;
    }
  }
}

var cartCount = CartCouter();
function updateCart(){
	document.getElementById('cartcount').innerHTML = cartCount.value();
  }

var productlist = document.getElementsByClassName('item');
for(var i = 0; i< productlist.length; i++){
	productlist[i].addEventListener('click',function(){
  	if(this.className.indexOf('selected')<0){
    		this.className += " selected";
        cartCount.increment();
        updateCart();
    } else{
    	this.className = this.className.replace("selected", "");
      cartCount.decrement();
      updateCart();
    }
  })
}
.productslist{
  padding:10px;
}
ul li{
  display: inline-block;
  padding: 5px;
  border: 1px solid #ddd;
  text-align: center;
  width: 25%;
  cursor: pointer;
}
.selected{
  background-color: #7CFEF0;
  color: #333;
}
.cartdiv{
  position: relative;
  float:right;
  padding: 5px;
  box-sizing: border-box;
  border: 1px solid #f1f1f1;
}
<div>
<h3>
Practical Use of JavaScript Closure consept/private variable.
</h3>
<div class="cartdiv">
    <span id="cartcount">0</span>
</div>
<div class="productslist">
    <ul >
    <li class="item">Product 1</li>
     <li class="item">Product 2</li>
     <li class="item">Product 3</li>
    </ul>

</div>
</div>


2

폐쇄 사용 :

클로저는 JavaScript의 가장 강력한 기능 중 하나입니다. JavaScript는 함수의 중첩을 허용하고 외부 함수 내부에 정의 된 모든 변수 및 함수 (및 외부 함수가 액세스 할 수있는 다른 모든 변수 및 함수)에 대한 내부 액세스 권한을 내부 함수에 부여합니다. 그러나 외부 함수는 내부 함수 내에 정의 된 변수 및 함수에 액세스 할 수 없습니다. 이것은 내부 함수 변수에 대한 일종의 보안을 제공합니다. 또한 내부 함수가 외부 함수의 범위에 액세스 할 수 있으므로 내부 함수가 외부 함수의 수명을 초과하여 생존 할 수 있으면 외부 함수에 정의 된 변수 및 함수가 외부 함수 자체보다 오래 작동합니다.

예 :

<script>
var createPet = function(name) {
  var sex;

  return {
    setName: function(newName) {
      name = newName;
    },

    getName: function() {
      return name;
    },

    getSex: function() {
      return sex;
    },

    setSex: function(newSex) {
      if(typeof newSex == "string" && (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
        sex = newSex;
      }
    }
  }
}

var pet = createPet("Vivie");
console.log(pet.getName());                  // Vivie

console.log(pet.setName("Oliver"));   
console.log(pet.setSex("male"));
console.log(pet.getSex());                   // male
console.log(pet.getName());                  // Oliver
</script>

위의 코드에서 외부 함수의 이름 변수는 내부 함수에 액세스 할 수 있으며 내부 함수를 제외하고 내부 변수에 액세스하는 다른 방법은 없습니다. 내부 함수의 내부 변수는 내부 함수의 안전한 저장소 역할을합니다. 내부 기능이 작동 할 "영구적이지만 안전하지만"데이터를 보유합니다. 함수는 변수에 할당되거나 이름이 없어도됩니다. 자세한 내용은 여기 를 읽으 십시오


2

나는 Mozilla의 함수 팩토리 예제를 좋아한다 .

function makeAdder(x) {

    return function(y) {
        return x + y;
    };
}

var addFive = makeAdder(5);

console.assert(addFive(2) === 7); 
console.assert(addFive(-5) === 0);

11
이것이 제 의견으로는 사람들이 클로저 또는 자신의 장점을 이해하는 데 도움이되지 않는 유형의 예입니다. 예제를 제외하고 숫자를 추가하는 함수를 반환하기 위해 클로저를 몇 번이나 작성한 적이 있습니까?
하마

2

JavaScript 모듈 패턴은 클로저를 사용합니다. 멋진 패턴을 사용하면 "public"및 "private"변수를 비슷하게 가질 수 있습니다.

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();


1

이 스레드는 클로저 작동 방식을 더 잘 이해하는 데 크게 도움이되었습니다. 나는 내 자신의 실험을 해본 결과 다른 사람들이 클로저를 실제적인 방법으로 사용하는 방법과 클로즈를 다른 수준에서 사용하여 정적 및 전역 변수를 덮어 쓰거나 혼동 할 위험이없는 전역 변수 이 작업은 각 개별 버튼에 대한 로컬 수준과 전역 수준에서 버튼 클릭을 추적하여 모든 버튼 클릭을 계산하여 단일 수치에 기여합니다. 참고 나는 이것을하기 위해 전역 변수를 사용하지 않았다.

전문가들이여, 여기서 나쁜 관행을 저질렀다면 알려주십시오! 나는 아직도이 물건을 스스로 배우고 있습니다.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Closures on button presses</title>
<script type="text/javascript">

window.addEventListener("load" , function () {
    /*
    grab the function from the first closure,
    and assign to a temporary variable 
    this will set the totalButtonCount variable
    that is used to count the total of all button clicks

    */
    var buttonHandler = buttonsCount(); 

    /*
    using the result from the first closure (a function is returned) 
    assign and run the sub closure that carries the 
    individual variable for button count and assign to the click handlers 
    */
    document.getElementById("button1").addEventListener("click" , buttonHandler() );
    document.getElementById("button2").addEventListener("click" , buttonHandler() );
    document.getElementById("button3").addEventListener("click" , buttonHandler() );

    // Now that buttonHandler has served its purpose it can be deleted if needs be
    buttonHandler = null;
});



function buttonsCount() {
    /* 
        First closure level 
        - totalButtonCount acts as a sort of global counter to count any button presses
    */
    var totalButtonCount = 0;

    return  function () {
        //second closure level
        var myButtonCount = 0;

        return function (event) {
            //actual function that is called on the button click
            event.preventDefault();
            /*  
               increment the button counts.
               myButtonCount only exists in the scope that is 
               applied to each event handler, therefore acts 
               to count each button individually whereas because 
               of the first closure totalButtonCount exists at 
               the scope just outside, so maintains a sort 
               of static or global variable state 
            */

            totalButtonCount++;
            myButtonCount++;

            /* 
                do something with the values ... fairly pointless 
                but it shows that each button contributes to both 
                it's own variable and the outer variable in the 
                first closure 
            */
            console.log("Total button clicks: "+totalButtonCount);
            console.log("This button count: "+myButtonCount);
        }
    }
}

</script>
</head>

<body>
    <a href="#" id="button1">Button 1</a>
    <a href="#" id="button2">Button 2</a>
    <a href="#" id="button3">Button 3</a>
</body>
</html>

0

참조 : 클로저의 실제 사용법

실제로 클로저는 우아한 계산을 생성하여 다양한 계산, 지연된 호출, 콜백, 캡슐화 된 범위 생성 등을 사용자 지정할 수 있습니다.

정렬 조건 함수를 인수로 허용하는 배열의 정렬 방법의 예는 다음과 같습니다.

[1, 2, 3].sort(function (a, b) {
    ... // sort conditions
});

기능 인수의 조건에 따라 새 배열을 맵핑하는 배열의 맵 메소드로 기능 맵핑 :

[1, 2, 3].map(function (element) {
   return element * 2;
}); // [2, 4, 6]

종종 검색에 대한 거의 무제한 조건을 정의하는 함수 인수를 사용하여 검색 기능을 구현하는 것이 편리합니다.

 someCollection.find(function (element) {
        return element.someProperty == 'searchCondition';
    });

또한 함수를 요소의 배열에 적용하는 forEach 메소드와 같이 기능을 적용하는 것에 주목할 수 있습니다.

[1, 2, 3].forEach(function (element) {
    if (element % 2 != 0) {
        alert(element);
    }
}); // 1, 3

함수는 인수 (적용된 인수 목록 및 호출 된 위치 지정된 인수)에 적용됩니다.

(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);

지연된 통화 :

var a = 10;
    setTimeout(function () {
      alert(a); // 10, after one second
    }, 1000);

콜백 함수 :

var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // callback, which will be called deferral ,
  // when data will be ready;
  // variable "x" here is available,
  // regardless that context in which,
  // it was created already finished
  alert(x); // 10
};

보조 객체를 숨길 목적으로 캡슐화 된 범위 만들기 :

var foo = {};
(function (object) {
  var x = 10;
  object.getX = function _getX() {
    return x;
  };
})(foo);
alert(foo.getX());// get closured "x" – 10

0

프론트 엔드 JavaScript로 작성하는 대부분의 코드는 이벤트 기반입니다. 동작을 정의한 다음 사용자가 트리거 한 이벤트 (예 : 클릭 또는 키 누르기)에 연결합니다. 우리의 코드는 일반적으로 콜백으로 첨부됩니다 : 이벤트에 대한 응답으로 실행되는 단일 함수. size12, size14 및 size16은 이제 본문 텍스트의 크기를 각각 12, 14 및 16 픽셀로 조정하는 함수입니다. 우리는 다음과 같이 버튼 (이 경우 링크)에 연결할 수 있습니다.

function makeSizer(size) {
    return function() {
    document.body.style.fontSize = size + 'px';
    };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

깡깡이


이 코드는 질문에 대답 할 수 있지만 문제를 해결하는 방법 및 / 또는 이유에 대한 추가 컨텍스트를 제공하면 답변의 장기적인 가치가 향상됩니다.
Donald Duck

1
이 예제는 표준 기능을 통해 폐쇄하지 않고 구현할 수 있습니다.
Zach Smith

0

클로저는 유용한 방법입니다. 주문형으로 증가 된 순서 :

    var foobar = function(i){var count = count || i; return function(){return ++count;}}

    baz = foobar(1);
    console.log("first call: " + baz()); //2
    console.log("second call: " + baz()); //3

차이점은 다음과 같이 요약됩니다.

익명 함수 정의 된 함수

메서드로 사용할 수 없습니다 개체의 메서드로 사용할 수 있습니다

정의 된 범위 내에서만 존재합니다. 정의 된 범위 내에서 존재합니다.

정의 된 범위 내에서만 호출 가능 코드의 어느 시점에서나 호출 가능

새 값을 다시 할당하거나 삭제할 수 있습니다. 삭제하거나 변경할 수 없습니다

참고 문헌


0

주어진 샘플에서 동봉 된 변수 'counter'의 값이 보호되며 주어진 함수 (증가, 감소)를 사용해서 만 변경할 수 있습니다. 폐쇄되어 있기 때문에

var MyCounter= function (){
    var counter=0;
    return {
    	increment:function () {return counter += 1;},
        decrement:function () {return counter -= 1;},
        get:function () {return counter;}
    };
};

var x = MyCounter();
//or
var y = MyCounter();

alert(x.get());//0
alert(x.increment());//1
alert(x.increment());//2

alert(y.increment());//1
alert(x.get());// x is still 2


0

JavaScript에서 클로저에 대한 실제 사용 설명 ---

다른 함수 안에 함수를 만들면 클로저가 만들어집니다. 클로저는 외부 기능의 데이터를 읽고 조작 할 수 있기 때문에 강력합니다. 함수가 호출 될 때마다 해당 호출에 대한 새 범위가 작성됩니다. 함수 내부에 선언 된 로컬 변수는 해당 범위에 속하며 해당 함수에서만 액세스 할 수 있습니다. 함수가 실행을 마치면 일반적으로 범위가 삭제됩니다.

이러한 기능의 간단한 예는 다음과 같습니다.

function buildName(name) { 
    const greeting = "Hello, " + name; 
    return greeting;
}

위 예제에서 buildName () 함수는 로컬 변수 인사말을 선언하고 반환합니다. 모든 함수 호출은 새로운 지역 변수로 새로운 범위를 만듭니다. 함수가 실행 된 후에는 해당 범위를 다시 참조 할 방법이 없으므로 가비지 수집됩니다.

그러나 우리가 그 범위와 연결될 때 어떻습니까?

다음 함수를 보자 :

function buildName(name) { 
    const greeting = "Hello, " + name + " Welcome "; 
    const sayName = function() {
        console.log(greeting); 
    };
    return sayName; 
}

const sayMyName = buildName("Mandeep");
sayMyName();  // Hello, Mandeep Welcome

이 예제의 sayName () 함수는 클로저입니다. sayName () 함수에는 자체 로컬 범위 (변수 환영)가 있으며 외부 (클로 싱) 함수 범위에도 액세스 할 수 있습니다. 이 경우 buildName ()에서 변수 인사말입니다.

buildName이 실행 된 후에는이 경우 범위가 삭제되지 않습니다. sayMyName () 함수는 여전히 액세스 할 수 있으므로 가비지 수집되지 않습니다. 그러나 클로저를 제외하고 외부 범위에서 데이터에 액세스하는 다른 방법은 없습니다. 클로저는 글로벌 컨텍스트와 외부 범위 사이의 게이트웨이 역할을합니다.


0

나는 clousures를 배우려고 노력하고 있으며 내가 만든 예가 실용적인 사용 사례라고 생각합니다. 스 니펫을 실행하고 콘솔에서 결과를 볼 수 있습니다. 별도의 데이터를 가진 두 명의 별도 사용자가 있습니다. 그들 각각은 실제 상태를보고 업데이트 할 수 있습니다.

function createUserWarningData(user) {
  const data = {
    name: user,
    numberOfWarnings: 0,
  };

  function addWarning() {
    data.numberOfWarnings = data.numberOfWarnings + 1;
  }

  function getUserData() {
    console.log(data);
    return data;
  }

  return {
    getUserData: getUserData,
    addWarning: addWarning,
  };
}

const user1 = createUserWarningData("Thomas");
const user2 = createUserWarningData("Alex");

//USER 1
user1.getUserData(); // returning data user object
user1.addWarning(); // add one warning to specific user
user1.getUserData(); // returning data user object

//USER2
user2.getUserData(); // returning data user object
user2.addWarning(); // add one warning to specific user
user2.addWarning(); // add one warning to specific user
user2.getUserData(); // returning data user object

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.