function 자바스크립트 생활코딩 - JavaScript 클로저는 어떻게 작동합니까?





15 Answers

다른 함수 내에서 function 키워드를 볼 때마다 내부 함수는 외부 함수의 변수에 액세스 할 수 있습니다.

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

barfoo 대한 인수로 정의 된 x 액세스 할 수 있고 foo 에서 tmp 액세스 할 수 있기 때문에 항상 16을 기록합니다.

그것은 종결이다. 함수가 클로저라고하기 위해 반환 할 필요는 없습니다. 즉각적인 어휘 범위 외부의 변수에 액세스하기 만하면 클로저가 만들어집니다 .

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

심지어 위의 함수는 더 이상 직접 범위 안에 있지 않더라도 bar 는 여전히 xtmp 참조 할 수 있기 때문에 16을 기록합니다.

그러나 tmpbar 의 클로저 안쪽에 매달려 있기 때문에 증가하고 있습니다. bar 를 호출 할 때마다 증가 bar .

클로저의 가장 간단한 예는 다음과 같습니다.

var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

JavaScript 함수가 호출되면 새 실행 컨텍스트가 만들어집니다. 이 실행 컨텍스트는 함수 인수와 부모 개체와 함께 외부에서 선언 된 모든 변수를받습니다 (위의 예에서는 'a'와 'b'모두).

둘 이상의 closure 함수를 생성하는 것은 그것의리스트를 반환하거나 전역 변수로 설정함으로써 가능합니다. 이 모든 것들은 동일한 x 와 같은 tmp 를 참조 할 것이고, 그것들은 그들 자신의 복사본을 만들지 않는다.

여기서 x 는 숫자입니다. JavaScript의 다른 리터럴과 마찬가지로 foo 가 호출되면 숫자 x 가 인수 xfoo 복사 됩니다.

반면에 JavaScript는 객체를 처리 할 때 항상 참조를 사용합니다. 말하면, foo 를 객체와 함께 호출하면, 반환 된 클로저는 그 원래 객체를 참조 할 것입니다!

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

예상대로 bar(10) 각 호출은 x.memb 를 증가 x.memb . 예상 할 수없는 것은 x 는 단순히 age 변수와 동일한 객체를 참조한다는 것입니다. bar 몇 번 전화하면 age.memb 가 2가됩니다! 이 참조는 HTML 객체를 사용한 메모리 누수의 기초입니다.

스위프트 php 의미

JavaScript closure를 구성하는 개념 (예 : 함수, 변수 등)에 대한 지식이있는 사람에게 어떻게 설명하겠습니까?하지만 클로저 자체는 이해하지 못합니까?

위키 백과에서 주어진 Scheme 예제를 보았지만 유감스럽게도 도움이되지 않았습니다.




질문을 진지하게 받아들이면 전형적으로 6 살짜리 아기가인지 할 수있는 것을 발견해야합니다. 자바 스크립트에 관심이있는 사람은 그렇게 일반적이지 않습니다.

유년기 발달에 : 5 7 년 그것은 밝힌다 :

귀하의 자녀는 2 단계 지시를 따를 수 있습니다. 예를 들어, 자녀에게 "부엌에 가서 쓰레기 봉투를 가져 가라."라고 말하면 그 방향을 기억할 수 있습니다.

이 예제를 사용하여 다음과 같이 클로저를 설명 할 수 있습니다.

부엌은 trashBags 라고하는 지역 변수가있는 클로저입니다. 부엌에는 getTrashBag 라는 함수가있어 하나의 쓰레기 봉투를 가져 와서 반환합니다.

우리는 이것을 다음과 같이 JavaScript로 코딩 할 수 있습니다 :

function makeKitchen() {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

console.log(kitchen.getTrashBag()); // returns trash bag C
console.log(kitchen.getTrashBag()); // returns trash bag B
console.log(kitchen.getTrashBag()); // returns trash bag A

왜 클로저가 흥미로운지를 설명하는 추가 포인트 :

  • makeKitchen() 이 호출 될 때마다 새 클로저가 자체 별도의 trashBags 와 함께 만들어 trashBags .
  • trashBags 변수는 각 주방 내부에 국한되어 있으며 외부에서 액세스 할 수 없지만 getTrashBag 속성의 내부 함수에는 액세스 할 수 있습니다.
  • 모든 함수 호출은 클로저를 생성하지만 클로저 내부에 액세스 할 수있는 내부 함수가 클로저 외부에서 호출 될 수있는 경우가 아니라면 클로저를 둘 필요가 없습니다. getTrashBag 함수를 사용하여 객체를 반환하면 여기에서 수행합니다.



클로저는 모든 사람들이 어쨌든 직관적으로 작업 할 것으로 예상되는 일부 동작 작업을 작성하는 데 사용되기 때문에 설명하기 어렵습니다. 나는 그들을 설명 할 수있는 가장 좋은 방법을 찾는다. (그리고 그들이하는 일을 배웠던 방식) 그들없이 상황을 상상하는 것이다.

    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

자바 스크립트 클로저를 알지 못하면 어떻게 될까요? 마지막 줄의 메서드를 메서드 본문 (기본적으로 함수 호출이하는 기능)으로 바꾸면됩니다.

console.log(x + 3);

자, x 의 정의는 어디에 있습니까? 현재 범위에서 정의하지 않았습니다. 유일한 해결책은 plus5 가 범위 (또는 부모의 범위)를 전달하도록하는 것입니다. 이렇게하면 x 는 잘 정의되어 있고 값 5에 바인딩됩니다.




OK, 6 살짜리 팬이 닫힙니다. 가장 단순한 폐쇄 사례를 듣고 싶습니까?

다음 상황을 상상해보십시오 : 운전자가 차에 앉아 있습니다. 그 차는 비행기 안에 있어요. 비행기가 공항에 있습니다. 운전자가 자신의 차 바깥에있는 물건에 접근 할 수는 있지만 비행기 내부에는 비행기가 공항을 떠나더라도 폐쇄가 가능합니다. 그게 전부 야.27 세가되면 더 자세한 설명 이나 아래의 예를보십시오.

여기 내 비행기 이야기를 코드로 변환 할 수있는 방법이 있습니다.

var plane = function(defaultAirport) {

  var lastAirportLeft = defaultAirport;

  var car = {
    driver: {
      startAccessPlaneInfo: function() {
        setInterval(function() {
          console.log("Last airport was " + lastAirportLeft);
        }, 2000);
      }
    }
  };
  car.driver.startAccessPlaneInfo();

  return {
    leaveTheAirport: function(airPortName) {
      lastAirportLeft = airPortName;
    }
  }
}("Boryspil International Airport");

plane.leaveTheAirport("John F. Kennedy");




나는 폐쇄를 설명하면서 잠시 블로그 포스트를 썼다. 여기에 클로저에 대해 내가 당신이 원하는지 에 관해 말한 내용이 있습니다.

클로저는 함수에 영구적 인 전용 변수 , 즉 하나의 함수만이 알고있는 변수가 있고, 실행 된 이전 시간의 정보를 추적 할 수있는 방법입니다.

이러한 의미에서 함수는 private 속성을 가진 객체처럼 약간 동작합니다.

전체 게시물 :

그래서이 폐쇄 장치는 무엇입니까?




6 살짜리 아이에게 어떻게 설명 할 수 있을까요?

어른들이 집을 소유 할 수있는 방법을 알고 집에 전화합니까? 엄마가 아이를 가질 때, 아이는 정말로 아무것도 소유하지 않습니다. 그렇죠? 그러나 부모는 집을 소유하고 있으므로 다른 사람이 자녀에게 "집이 어디 있습니까?"라고 묻는다면 "그 집!"이라고 대답하고 부모의 집을 가리킬 수 있습니다. "폐쇄 (Closure)"는 자녀가 항상 집을 소유하고있는 부모 인 경우에도 항상 (해외에 있더라도) 집에 있다고 말할 수있는 능력입니다.




나는 좋은 / 나쁜 비교에 의해 잘 배우는 경향이 있습니다. 나는 작동 코드가 뒤따를 때 누군가가 만날 가능성이있는 작동 코드를보고 싶어한다. 나는 jsFiddle 을 비교해 보았고 비교를 해보고 가장 간단한 설명과의 차이점을 생각해 냈습니다.

클로저 완료 :

console.log('CLOSURES DONE RIGHT');

var arr = [];

function createClosure(n) {
    return function () {
        return 'n = ' + n;
    }
}

for (var index = 0; index < 10; index++) {
    arr[index] = createClosure(index);
}

for (var index in arr) {
    console.log(arr[index]());
}
  • 위의 코드 createClosure(n)에서 루프의 모든 반복에서 호출됩니다. 새로운 함수 범위에서 생성 된 새로운 변수이고 외부 범위에 바인딩 된 것과 같은 변수가 아니라는 n점을 강조하기 위해 변수의 이름을 지정 했습니다 .index

  • 이렇게하면 새 범위가 만들어 n지고 해당 범위에 바인딩됩니다. 즉, 각 반복마다 하나씩 10 개의 분리 된 범위가 있음을 의미합니다.

  • createClosure(n) 해당 범위 내의 n을 반환하는 함수를 반환합니다.

  • 각 스코프 내 에서 호출 된 n값은 무엇이든간에 바인딩 createClosure(n)되므로 반환 된 중첩 된 함수는 항상 호출 n되었을 때 의 값을 반환합니다 createClosure(n).

클로저가 잘못됨 :

console.log('CLOSURES DONE WRONG');

function createClosureArray() {
    var badArr = [];

    for (var index = 0; index < 10; index++) {
        badArr[index] = function () {
            return 'n = ' + index;
        };
    }
    return badArr;
}

var badArr = createClosureArray();

for (var index in badArr) {
    console.log(badArr[index]());
}
  • 위의 코드에서 루프는 createClosureArray()함수 내에서 이동 되었고 함수는 이제 완성 된 배열을 반환합니다.이 배열은 언뜻보기에 더 직관적으로 보입니다.

  • 명확하지 않은 것은 이후 createClosureArray()루프가 반복 될 때마다 하나의 범위가 만들어 지기 때문에이 함수에 대해 만들어진 이후 에만 호출 된다는 것입니다.

  • 이 함수 내에서 명명 index된 변수 가 정의됩니다. 루프가 돌아가서 배열에 기능을 추가하고 추가합니다 index. 그는 주 index내에서 정의되고 createClosureArray오직 한 번만 호출됩니다 기능.

  • createClosureArray()함수 내에 하나의 범위 만 있기 때문에 해당 범위 내의 index값에만 바인딩됩니다. 즉, 루프가 값을 변경 할 때마다 index해당 범위 내 에서 루프 를 참조하는 모든 항목에 대해 루프가 변경 됩니다.

  • 배열에 추가 된 모든 함수 index는 첫 번째 예제와 같이 10 개의 서로 다른 범위의 10 가지 변수 대신 정의 된 상위 범위 의 SAME 변수를 반환합니다 . 최종 결과는 모든 10 개의 함수가 동일한 범위에서 동일한 변수를 반환한다는 것입니다.

  • 루프가 끝나고 index수정이 끝나면 최종 값은 10 이었으므로 배열에 추가 된 모든 함수 index는 이제 단일 변수 의 값을 반환합니다.이 값 은 이제 10으로 설정됩니다.

결과

클로즈드 원 클릭
n = 0
n = 1
n = 2
n = 3
n = 4
n = 5
n = 6
n = 7
n = 8
n = 9

폐쇄가 잘못되었습니다.
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10
n = 10




대화식 JavaScript 자습서를 사용하여 클로저 작동 방식을 설명했습니다. 폐쇄 란 무엇입니까?

여기에 예제 중 하나가 있습니다.

var create = function (x) {
    var f = function () {
        return x; // We can refer to x here!
    };
    return f;
};
// 'create' takes one argument, creates a function

var g = create(42);
// g is a function that takes no arguments now

var y = g();
// y is 42 here



나는 대답이 왜 그렇게 복잡한지 이해하지 못한다.

다음은 클로저입니다.

var a = 42;

function b() { return a; }

예. 당신은 아마 하루에 여러 번 사용합니다.


클로저가 특정 문제를 해결하기위한 복잡한 디자인 해킹이라고 생각할 이유는 없습니다. 아니요, 클로저는 함수가 선언 된 (실행되지 않음) 관점에서 상위 범위 에서 오는 변수를 사용하는 것 입니다.

이제 무엇을 할 수 있습니다 당신이 더 아름다운 될 수 있습니다 할 다른 답변을 볼 수 있습니다.




클로저는 내부 함수가 외부 함수의 변수에 액세스 할 수있는 곳입니다. 아마도 클로저를 위해 얻을 수있는 가장 단순한 한 줄짜리 설명 일 것입니다.




잠을 자고 있는데 댄을 초대합니다. Dan에게 하나의 XBox 컨트롤러를 가져 오라고 말합니다.

댄은 폴을 초대합니다. Dan은 Paul에게 하나의 컨트롤러를 가져 오라고합니다. 얼마나 많은 컨트롤러가 파티에 가져 왔습니까?

function sleepOver(howManyControllersToBring) {

    var numberOfDansControllers = howManyControllersToBring;

    return function danInvitedPaul(numberOfPaulsControllers) {
        var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
        return totalControllers;
    }
}

var howManyControllersToBring = 1;

var inviteDan = sleepOver(howManyControllersToBring);

// The only reason Paul was invited is because Dan was invited. 
// So we set Paul's invitation = Dan's invitation.

var danInvitedPaul = inviteDan(howManyControllersToBring);

alert("There were " + danInvitedPaul + " controllers brought to the party.");



Closures 작성자는 Closures 에 대해 설명하고 클로저가 필요한 이유를 설명하고 클로저를 이해하는 데 필요한 LexicalEnvironment를 설명합니다.
요약은 다음과 같습니다.

변수에 액세스했지만 로컬 변수가 아닌 경우에는 어떻게해야합니까? 여기처럼 :

이 경우 인터프리터는 외부 LexicalEnvironment객체 에서 변수를 찾습니다 .

프로세스는 두 단계로 구성됩니다.

  1. 첫째, 함수 f가 생성되면 빈 공간에 생성되지 않습니다. 현재 LexicalEnvironment 객체가 있습니다. 위의 경우 창 (a는 함수 생성시 정의되지 않음)입니다.

함수가 생성되면 현재 [LexicalEnvironment]를 참조하는 숨겨진 속성 [[Scope]]을 얻습니다.

변수를 읽었지만 어디에서도 찾을 수없는 경우 오류가 생성됩니다.

중첩 된 함수

함수는 스코프 체인이라고도 부를 수있는 LexicalEnvironments의 체인을 형성하여 다른 함수의 내부에 중첩 될 수 있습니다.

따라서 함수 g는 g, a 및 f에 액세스 할 수 있습니다.

마감

중첩 된 함수는 외부 함수가 완료된 후에도 계속 살 수 있습니다.

LexicalEnvironments 마크 업 :

우리가 볼 때, this.say사용자 개체의 속성입니다, 그래서 사용자가 완료된 후 거주하고 있습니다.

그리고 기억하면, 언제 this.say만들어 this.say.[[Scope]]지나요? (모든 함수와 마찬가지로) 현재 LexicalEnvironment에 대한 내부 참조 를 얻습니다. 따라서 현재 사용자 실행의 LexicalEnvironment는 메모리에 남아 있습니다. 사용자의 모든 변수도 속성이므로 일반적으로 정크가 아닌 신중하게 보관됩니다.

전체적인 요점은 내부 함수가 미래에 외부 변수에 액세스하려고한다면 그렇게 할 수 있다는 것입니다.

요약:

  1. 내부 함수는 외부 LexicalEnvironment에 대한 참조를 유지합니다.
  2. 내부 함수는 외부 함수가 완료 되더라도 언제든지 변수를 액세스 할 수 있습니다.
  3. 브라우저는 LexicalEnvironment와 모든 속성 (변수)을 참조하는 내부 함수가있을 때까지 메모리에 LexicalEnvironment 및 모든 속성 (변수)을 유지합니다.

이를 클로저 (closure)라고합니다.




좋아, 6 살짜리 아이와 이야기하면서, 나는 아마도 다음과 같은 협회를 사용할 것입니다.

상상해보십시오 - 당신은 집 전체에서 형제와 자매와 놀고 있습니다. 그리고 당신은 장난감으로 주위를 돌아 다니며 형제의 방으로 가져 왔습니다. 잠시 후 동생이 학교에서 돌아와서 자기 방으로 갔다가 안에 잠겨 있었으므로 지금은 더 이상 직접적인 방법으로 장난감에 접근 할 수 없었습니다. 하지만 문을 두드리고 형제에게 그 장난감을 물어볼 수 있습니다. 이를 장난감의 폐쇄 라고합니다 . 당신의 형제가 당신을 위해 그것을 만들었고, 그는 이제 바깥 범위에 있습니다.

문이 초안에 잠겨서 아무도 내부 기능을 수행하지 못하는 상황 (일반 기능 실행)과 비교하여 일부 지역 화재가 발생하여 (가비지 컬렉터 : D) 방을 태우고 새로운 방이 만들어져 이제 퇴원 할 수 있습니다 거기에 또 하나의 장난감 (새 함수 인스턴스)이 있지만 첫 번째 실 인스턴스에 남겨진 동일한 장난감을 결코 얻지 못합니다.

상급 자녀의 경우 다음과 같은 것을 넣을 것입니다. 그것은 완벽하지는 않지만 그것이 무엇인지 느껴지게합니다 :

function playingInBrothersRoom (withToys) {
  // We closure toys which we played in the brother's room. When he come back and lock the door
  // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
  var closureToys = withToys || [],
      returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.

  var brotherGivesToyBack = function (toy) {
    // New request. There is not yet closureToys on brother's hand yet. Give him a time.
    returnToy = null;
    if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.

      for ( countIt = closureToys.length; countIt; countIt--) {
        if (closureToys[countIt - 1] == toy) {
          returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
          break;
        }
      }
      returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
    }
    else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
      returnToy = 'Behold! ' + closureToys.join(', ') + '.';
      closureToys = [];
    }
    else {
      returnToy = 'Hey, lil shrimp, I gave you everything!';
    }
    console.log(returnToy);
  }
  return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
    askBrotherForClosuredToy = playingInBrothersRoom(toys);

// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined

// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there

보시다시피, 방에 남아있는 장난감은 방이 잠겨 있어도 형제를 통해 접근 할 수 있습니다. 여기 에 놀 수 있는 jsbin 이 있습니다.




JavaScript의 함수는 (C 언어에서와 같이) 일련의 명령어에 대한 참조 일뿐만 아니라 사용되는 모든 비 지역 변수 (캡처 된 변수)에 대한 참조로 구성된 숨겨진 데이터 구조도 포함합니다. 이러한 두 조각 기능을 클로저 (closure)라고합니다. JavaScript의 모든 함수는 클로저로 간주 될 수 있습니다.

클로저는 상태가있는 함수입니다. "this"는 함수에 대한 상태도 제공한다는 점에서 "this"와 다소 비슷하지만 "this"는 별도의 객체입니다 ( "this"는 단지 멋진 매개 변수 일 뿐이며이를 영구적으로 바인딩하는 유일한 방법입니다. 함수는 클로저를 만드는 것입니다.) "this"와 함수는 항상 별도로 존재하지만, 함수는 그 closure와 분리 될 수 없으며 언어는 캡처 된 변수에 액세스 할 수있는 방법을 제공하지 않습니다.

어휘 적으로 중첩 된 함수가 참조하는 이러한 외부 변수는 모두 어휘 적으로 묶는 함수의 체인에있는 로컬 변수이므로 (전역 변수는 루트 함수의 로컬 변수라고 가정 할 수 있음) 함수의 모든 단일 실행은 그것의 지역 변수를 가지면, 중첩 된 함수는 새로운 클로저를 생성합니다 (콜백으로 등록하는 등의 기능을 수행 할 때마다 실행을 나타내는 참조 된 비 로컬 변수 세트가 있음) 문맥).

또한 JavaScript의 로컬 변수는 스택 프레임이 아니라 힙에 만들어지고 아무도 참조하지 않는 경우에만 삭제된다는 점을 알아야합니다. 함수가 반환되면 로컬 변수에 대한 참조가 감소하지만 현재 실행 중에 클로저의 일부가되었고 어휘 적으로 중첩 된 함수에 의해 참조되는 경우에도 여전히 null이 될 수 있습니다 (이 경우에는 이러한 중첩 된 함수는 반환되거나 다른 외부 코드로 전송됩니다.

예 :

function foo (initValue) {
   //This variable is not destroyed when the foo function exits.
   //It is 'captured' by the two nested functions returned below.
   var value = initValue;

   //Note that the two returned functions are created right now.
   //If the foo function is called again, it will return
   //new functions referencing a different 'value' variable.
   return {
       getValue: function () { return value; },
       setValue: function (newValue) { value = newValue; }
   }
}

function bar () {
    //foo sets its local variable 'value' to 5 and returns an object with
    //two functions still referencing that local variable
    var obj = foo(5);

    //Extracting functions just to show that no 'this' is involved here
    var getValue = obj.getValue;
    var setValue = obj.setValue;

    alert(getValue()); //Displays 5
    setValue(10);
    alert(getValue()); //Displays 10

    //At this point getValue and setValue functions are destroyed
    //(in reality they are destroyed at the next iteration of the garbage collector).
    //The local variable 'value' in the foo is no longer referenced by
    //anything and is destroyed too.
}

bar();



나는 그것들을 모질라 클로저 페이지로 향하게 할 것이다 . 그것은 제가 발견 한 클로저 기본과 실제 사용법에 대한 가장 간결하고 간단한 설명 입니다. JavaScript를 배우는 모든 이에 게 권장됩니다.

그리고 예, 저는 6 살짜리에게 추천합니다. 6 살짜리가 클로저에 대해 배우면 , 기사에 제공된 간결하고 간단한 설명 을 이해할 준비가 된 것 입니다.






Related