javascript 클로저(Closure)란?

Posted by negabaro kim on Thursday, February 14, 2019 Tags: js concept   7 minute read
  1. 클로저(Closure) 클로저는 JavaScript의 유효범위 체인을 이용하여 이미 생명 주기가 끝난 외부 함수의 변수를 참조하는 방법입니다. 외부 함수가 종료되더라도 내부함수가 실행되는 상태면 내부함수에서 참조하는 외부함수는 닫히지 못하고 내부함수에 의해서 닫히게 되어 클로저라 불리 웁니다. 따라서 클로저란 외부에서 내부 변수에 접근할 수 있도록 하는 함수입니다. http://www.nextree.co.kr/p7363/

    $ 스코프니 안에 있는걸 크로져

block じゃなくて

関数の中に関数があるの

const A = {
    b: function (){ return true;}
}

PPP


{

}

重要なのは、上記で説明したような関数の特性をしっかりと理解しておくことでしょう。そうすれば必然的にクロージャは扱えているはずだからです。

上記コードの 11 行目の変数「counter」には、createCounter により作成された関数が格納されますが、この関数の定義時のコンテキスト(createCounter 関数のローカルスコープ)と変数 counter のコンテキスト(グローバルスコープ)が異なるため、counter に格納される関数はクロージャになります。

클로저에 관한 오해1

JavaScript의 함수는 클로저다!

오해의 여지가 있는 정의다. MDN 자료를 보면 myFunc(함수명)이 클로저가 되었다는 문구가 있다. 그말은 즉 100% 함수=클로저 는 아니라는것

그래서 함수는 클로저가 된다라고 이해하는 이미지 잡기 쉬울것 같다.

클로저에 관한 오해2

少し余談ですが、たまに「クロージャは関数を返す関数」といった説明も見かけますが、それは誤りです。 上記で説明した通り、あくまでクロージャとは「関数とその関数の定義時のコンテキストのセット」にすぎず、「関数を返す」というのはクロージャを扱う過程の話だからです。

・・・といったことを書きつつも、個人的にはクロージャという言葉の定義の意味を知ること自体にはあまり意味がないと考えています。重要なのは、上記で説明したような関数の特性をしっかりと理解しておくことでしょう。そうすれば必然的にクロージャは扱えているはずだからです。

もしかしたらクロージャ、なんていう言葉は無かった方がみんな幸せだったかもしれませんね。

클로저는 왜사용하는가?

은닉화(프라이빗 변수같은 역할)할려고 함수를 만드는 함수를 만들기 위해

클로저는 왜알아야하는가?

좋은 코드를 짜기 위해 클로저 개념을 이용해 만들어진 코드를 이해하기 위해

※클로저를 몰라서 어떤 기능을 못만든다 그런건 없음.

클로저(Closure)란?

MDN에서는 클로저를 다음과 같이 정의하고 있다.

클로저는 독립적인 (자유) 변수를 가리키는 함수이다. 또는, 클로저 안에 정의된 함수는 만들어진 환경을 ‘기억한다’. https://hyunseob.github.io/2016/08/30/javascript-closure/

함수와 그함수가 정의되었을때의 컨텍스트를 셋트로한 특수한 오브젝트 JavaScript의 함수가 특정 조건에 의해 클로저가 됨. 클로저는 외부함수(포함하고 있는)의 변수에 접근할 수 있는 내부 함수를 일컬음.

※다 맞는 얘기라는데 다 다른 얘기같다..한마디로 정의하기가 참 어려움.

이 포스트에서는 JavaScript의 함수가 특정 조건에 의해 클로저가 됨.이라는 문장을 해석해보도록 하자.

일단은 함수가 클로저가 되므로 클로저를 알기 위해서는 함수를 잘알아야한다.

함수는 클로저가 된다라는 이 뜻을 이해하기 위해서는 먼저 함수의 3가지 특성에 대해 알아야 함.

  1. 함수는 정의했을때의 컨텍스트로 실행됨(※가장중요)
  2. 함수는 제1급 오브젝트이므로 정의시 컨텍스트와 다른 컨텍스트 상에서 실행이 가능
  3. 함수는 정의시 컨텍스트와 다른 컨텍스트로 넘겨질때 클로저로 변신함.

1. 함수는 정의했을때의 컨텍스트로 실행됨(※가장중요)

컨텍스트? 정의했을때?? 뭔소리지..하나하나 알아보자.

컨텍스트란?

=함수가 놓여진 환경 =함수가 참조가능한 외부 변수 =함수 밖의 로컬변수와 글로벌 변수

var scope = 'global';

function func1() {
    console.log(scope);
}

다음중 컨텍스트란? 답: var scope = 'global';

※느낌은 알겠는데 컨텍스트라는 말이 입에 붙지가 않네…

여기서 말하는 정의했을때란?

함수를 실행할때가 아닌 함수가 정의된 타이밍에의 컨텍스트를 말함

정의의 예)

var dahyun = 'dubu';
var funcA = function() {}

실행의 예)

funcA();
console.log(dahyun);

다시 1. 함수는 정의했을때의 컨텍스트로 실행됨

이제 용어정리는 대충됐고 이 말을 이해하기 위한 예제를 보자.

var scope = 'global';

function func1() {
    console.log(scope);
}

function func2() {
    var scope = 'local';

    func1();
}

func1(); //global
func2(); //global

func1은 외부변수(컨텍스트!?) scope의 값을 출력 정의시의 scope값은 global이므로 func1()를 실행하면 실행결과는 global이 된다.

다음으로 함수func1을 func2함수의 내부에서 실행하고있다. 이 타이밍에서 func1의 외부변수(컨텍스트!?) scope값을 local로 정의하고 있다. 그럼에도 불구하고 func2()의 결과는 global이다.

이것이 의미하는것은 함수는 어떤 타이밍과 장소에서 실행되더라도 정의시의 컨텍스트로 실행된다 라는것이다.

2. 함수는 제1급 오브젝트이므로 정의시 컨텍스트와 다른 컨텍스트 상에서 실행이 가능

제 1급 오브젝트란 통상적인 오브젝트와 같이 변수에 대입을 하고 다른 함수의 변수에 넘길수 있다는것을 의미함. 이것은 JavasScript의 함수는 정의된 컨텍스트와 다른 컨텍스트상에서 실행하는것이 가능하다는것을 의미 이하 예제를 보자

var funcA = function () {
        console.log('funcA Called!!');
    },

    funcB = function (fn) {
        fn();
    };

funcB(funcA); //funcA Called!!

위 예제에서 함수funcA는 글로벌 스코프상에 정의되어있지만, 실행시 스코프는 funcB의 로컬스코프 안이다.

여기까지 정리하면 함수는 어디로든지 넘겨줄 수 있으나 어디까지 정의할때의 컨텍스트로 실행된다라는것이다

그리고 함수는 클로저로서 독특한 액션을 일으키는 조건은 정의할때의 컨텍스트와 다른 컨텍스트상에서 실행되었을때 뿐이다.

3. 함수는 정의할때의 컨텍스트와 다른 컨텍스트상으로 넘겨졌을때 클로저가 됨.

var createCounter = function () {
        var cnt = 0;

        return function () {
            cnt += 1;
            console.log(cnt);
        };
    };


var counter = createCounter();
counter(); // 「1」
counter(); // 「2」
counter(); // 「3」
・
・
・

createCounter함수를 주목 이 함수는 내부에 새로운 함수를 정의해서 그함수를 리턴값으로 돌려주고 있음. JavaScript의 함수는 제1급 오브젝트이므로 함수의 리턴값으로 사용이 가능 ※이 예제에서는 무명함수를 정의해서 리턴해주고 있지만 통상적인 이름있는 함수여도 상관없음

그리고 리턴된 함수입장에서 정의했을때의 컨텍스트에는 createCounter함수의 로컬 변수(여기서는 cnt),와 글로벌 변수가 포함되어있음 counter();안에는 createCounter함수에 의해 생성된 함수가 들어가 있다.

이 함수를 실행하면 1,2,3…이라는 수치가 출력됨. 이 값은 createCounter함수내의 로컬변수 cnt가 increment된 결과

통상적으로 보면 var counter = createCounter(); 시점에서 함수의 실행은 완료했으므로 그 내부에 있는cnt는 파기될거라도 생각되지만 그 다음행 이후에서도 cnt은 값을 보존되어 있음.

이것은 var counter = createCounter(); 에서 변수counter에 대입된 함수가 실행했을때가 아닌 정의했을때의 컨텍스트로 실행되었기 때문

var counter = createCounter(); 의 변수counter에는 createCounter에의해 생성된 함수가 대입되어 있지만, 이 함수는 정의시의 컨텍스트(createCounter 함수의 로컬스코프)와 변수counter의 컨텍스트(GlobalScope)가 다르기때문에 counter에 대입된 함수는 클로저가 되는것

클로저는 함수와 그함수가 정의되었을때의 컨텍스트를 세트로한 특수한 오브젝트이므로 createCounter함수의 실행이 끝나도 내부함수의 cnt는 클로저내에 보관되어 있어 계속 참조할 수 있는것임

なぜクロージャを使うのか? クロージャを使うことは、あくまでもプログラミング上のテクニックのようなものです。ですので、JavaScript において、クロージャを使わなければ実現出来ない機能というのはあり得ません。 ではなぜクロージャは使われるのでしょうか?以下にクロージャが良く使われるケースを解説します。

グローバル変数を使わずに関数に「状態」を持たせる(疑似プライベート変数) 先ほどのカウンターの例で説明すると、インクリメントされる変数(ここでは cnt)をグローバル変数にしてしまえば同様の機能を実現できます。

クロージャの用途

클로저는 Node.js의 비동기, 논-블록킹 아키텍처의 핵심기능으로 활용되고 있습니다. 클로저는 jQuery에서도 빈번히 사용되며, 거의 모든 자바스크립트 코드에서 볼 수 있습니다. 一般的には、クロージャは次の様な目的で利用されることが多いと考えられます。

グローバル変数を使わずに関数に「状態」を持たせる(疑似プライベート変数) 関数を作る関数

(中略)

myFunc(← 関数名)がクロージャになったということです。クロージャは関数とその関数が作られた環境という 2 つのものが一体となった特殊なオブジェクトです。

関数はクロージャになり得る」という理解の方がイメージとしては正しいでしょう。

JavaScript の関数は、その関数が定義されたコンテキストとは異なるコンテキスト上にある変数に格納される時、その関数自身および関数の定義時のコンテキストが一体になった「クロージャ」という特殊なオブジェクトになる。

1.JavaScript の関数は定義時のコンテキストで実行される。

このことは、クロージャを理解するために最も重要な関数の特性です

コンテキスト

ここでいう「コンテキスト」とは、”その関数がおかれた環境”などと説明されたりもしますが、要は「その関数が参照可能な外部変数(=外側の関数のローカル変数とグローバル変数)」と考えて差し支えないでしょう。

そして、「定義時の」とありますが、これは関数の実行時ではなく、あくまでその関数が定義されたタイミングでのコンテキスト、ということです。このことをシンプルに表したものが以下のコードです。

장점

クロージャの一つの利点として、 変数をプライベートな変数として扱う事ができるようになります。

클로저 오해

たまに「クロージャは関数を返す関数」といった説明も見かけますが、それは誤りです

スコープ

まず JavaScript は関数ごとにスコープが作られます。 スコープとは変数を参照できる範囲のことです。 その範囲外では変数は参照できません。

クロージャの簡単な定義として 「自分を囲むスコープにある変数を参照できる関数」 클로저란? MDN에서는 클로저를 다음과 같이 정의하고 있다.

클로저는 독립적인 (자유) 변수를 가리키는 함수이다. 또는, 클로저 안에 정의된 함수는 만들어진 환경을 ‘기억한다’.

흔히 함수 내에서 함수를 정의하고 사용하면 클로저라고 한다. 하지만 대개는 정의한 함수를 리턴하고 사용은 바깥에서 하게된다. 말로 설명하면 설명하기가 복잡하니 우선 코드를 보자.

클로저(closure)는 내부함수가 외부함수의 맥락(context)에 접근할 수 있는 것을 가르킨다. 클로저는 자바스크립트를 이용한 고난이도의 테크닉을 구사하는데 필수적인 개념으로 활용된다

이런 정의만 보고 과연 사람들이 이해를 할 수 있는건지는 잘 모르겠지만, 클로저에 대한 이해가 거의 없는 분들은 아마도 이해하기 어렵울 것입니다. 그러면, 일단 코드를 보도록 하죠.

function outFunc(name) { var outVar = ‘my name is ‘ function innerFunc() { return outVar + name } return innerFunc }

var result = outFunc(‘bono’) console.log(‘result: ‘ + result())

// result: my name is bono 내부함수 innerFunc()에서 outFunc() 함수의 인자와 지역변수에 접근이 가능합니다. outFunc()의 return 값(var result 에 할당)은 innerFunc()라는 내부 함수입니다. outFunce() 함수가 실행되면, outFunc()의 스코프는 끝이 나기 때문에 outFunc() 인자인 name 과 지역변수인 outVar 는 메모리에서 정리되어야합니다. 하지만, 실제 console.log 에서 result 를 호출하면(내부 함수가 호출), 내부함수 innerFunc()가 선언될때 outFunc() 함수의 인자와 outVar() 지역변수를 innerFunc()의 클로저 객체로 남아 실제로 innerFunc()가 호출될 때 클로저 객체를 통해서 outFunc()의 인자와 변수에 접근이 가능한 것입니다. 이게 바로 클로저가 하는 일입니다.

클로저는 외부함수(포함하고 있는)의 변수에 접근할 수 있는 내부 함수를 일컫습니다. 클로저는 외부함수(포함하고 있는)의 변수에 접근할 수 있는 내부 함수를 일컫습니다. 스코프 체인(scope chain)으로 표현되기도 합니다.

익명 함수와 클로저는 자주 잘못 혼용 됩니다. 클로저는 다른 함수의 스코프에 있는 변수에 접근 가능한 함수입니다.

출처: https://blog.sonim1.com/142 [Kendrick’s Blog] 外部で定義した変数をクロージャの内部で変更することができるということ。

클로저는 단순히 함수 외부의 변수에 접근 가능한 내부함수가 아니라 함수가 선언되는 순간에 함수가 실행될때 실제 외부변수에 접근하기 위한 객체이다. 클로저도 남발하면 위험하다. 가비지컬렉션 대상이 되어야할 객체들이 메모리상에 남아 있게 되므로, 클로저를 남발하면 오버플로우가 발생할수도 있다. 이는 클로저에 대해 정확히 알아야 하는 이유이기도 하다.

클로저를 통한 은닉화

반복문 클로저

클로저의 성능

https://fullest-sway.me/blog/2017/11/13/js-closure/
http://analogic.jp/closure/
http://analogic.jp/closure/
https://qiita.com/takeharu/items/4975031faf6f7baf077a
https://hyunseob.github.io/2016/08/30/javascript-closure/
https://nesoy.github.io/articles/2017-05/Javascript-Closure
https://hyunseob.github.io/2016/08/30/javascript-closure/
http://chanlee.github.io/2013/12/10/understand-javascript-closure/
https://blueshw.github.io/2017/04/12/javascript-closure/
https://qiita.com/To_BB/items/bf4d6384f7dce47bb216
출처: https://blog.sonim1.com/142 [Kendrick's Blog]
https://moonscode.tistory.com/4?category=744351