2011-09-09 2 views
0

내가 폐쇄와 자바 스크립트에 대한 책을 읽은되었고, 나는이 시도까지 내가, 그것을 가지고 생각 :이 코드를 실행 한 다음 클로저는 정확히 무엇입니까?

var Object5 = function (param) { 
    var x = 0; 

    var squareBrace = function() { 
     return '[' + param + x + ']'; 
    }; 

    this.toString = function() { 
     x = x + 1; 
     return squareBrace(); 
    }; 
}; 

:

var counter = new Object5("Counter: "); 
print("Has x:" + ('x' in counter)); 
print("Has f:" + ('f' in counter)); 
print("Can access x:" + (!!counter.x)); 
print("Can Invoke f:" + (!!counter.f)); 
print(counter.toString()); 
print(counter.toString()); 
print(counter.toString()); 
print(counter.toString()); 
print(counter.toString()); 
print(counter.toString()); 

그리고 그것은 내가 가진 무엇을 :

Has x:false 
Has f:false 
Can access x:false 
Can Invoke f:false 
[Counter: 1] 
[Counter: 2] 
[Counter: 3] 
[Counter: 4] 
[Counter: 5] 
[Counter: 6] 

나는 'X'와 'F'가 '정의되지 않은'것 때문에 나는 '형식 오류'를 얻을 것이라고 생각하지만 나는 작업을 얻었다. 클로저는 이러한 동작을 가능하게하기위한 것이라고 생각했습니다. 'x'와 'y'는 '비공개'이며 폐쇄가 없으면 해당 멤버는 잊어 버릴 수 있습니다.

명백하게 나는 그것을 완전히 잘못 잡았다, 또는, 내가 여기에서 중요한 무엇인가 놓치고있다.

누군가가 내게 어떤 결론을 내릴 수 있었습니까? 그 이유는 무엇입니까?

감사합니다.

답변

3

클로저는 범위 지정 기술입니다. 한 범위에서 정의 된 매개 변수를 다른 범위로 가져 오는 방법입니다. 당신이

을 수행 할 때
var x = 1; 

var f = function(){ 
    console.log(x); // you've just 'closed in' the variable x. 
}; 

f(); 

x는 함수에 선언하지 않더라도 기능에서 사용할 수 있습니다.

구체적인 경우 2 개의 변수가 폐쇄됩니다 (paramx). 이들은 정의한 함수의 범위에 바인딩됩니다. toString을 실행하면 x에 클로즈됩니다. toStringsquareBrace을 실행하면이 메서드는 xparam을 모두 사용합니다. 그래서 변수 x을 두 개 닫습니다. 각 메소드 당 하나씩 (객체 자체의 범위에도 해당)

+0

그래서 'x'를 통해 두 개의 폐쇄가 있지만 두 가지 방법은 변수를 공유하는 권리?과도한 폐쇄가 성능에 좋지 않다는 것을 읽었습니다. 폐쇄가 없으면 똑같이 할 수있는 방법이 있습니까? – vtortola

6

클로저 주제를 다루기 위해 자바 스크립트에서 변수 범위가 작동하는 방법을 검토해야합니다. 의 기본적인 기능과 관련된 문제를보고 시작하자 :

이 기능은 다음 선언하고 변수 counter에 값 0을 할당하고,
function uniqueInteger(){  
    var counter = 0; 
    return ++counter; 
} 

console.log(uniqueInteger()); // returns '1' 
console.log(counter); // undefined 

가 증가하는 값을 반환합니다. 보시다시피 함수가 실행되는 동안 변수 카운터는 함수 내에서 액세스 할 수 있습니다. 그러나 일단 함수가 반환되면 변수는 더 이상 정의되지 않습니다. 이는 uniqueInteger() 함수의 범위의 일부이기 때문입니다. counter 변수가 개인이라고 말할 수 있습니다. 함수 uniqueInteger() 만 액세스 할 수 있습니다.

하지만 변수의 값을 기억하여 다음에이 함수가 호출 될 때 counter이 0이 아니라 1에서 시작하기를 바랍니다. 한 가지 방법은 변수 counter을 함수 외부에 선언하는 것이지만 더 이상 개인용이 아니며 다른 함수가 카운터 변수를 변경할 수 있습니다.

이 문제를 해결하려면 함수의 작동 방식을 살펴 봐야합니다. 함수가 호출되면 (위에서 보았 듯이) 함수의 변수는 반환 된 후에 사라집니다.그러나 함수의 범위 내에서, 중첩 된 기능은 여전히 ​​부모 함수의 "개인"변수에 액세스 할 수 있습니다 :

function uniqueInteger(){  
    var counter = 0; 

    // an example of nested function accessing the counter variable: 
    return (function() { return ++counter })(); 
} 

console.log(uniqueInteger()); // returns '1' 
console.log(counter); // undefined 

* 괄호로 호출의 수는() 정의 함수의 수와 일치합니다 . 중첩 함수 자체 호출되고 외부 기능 따라서 동일한 기능과 같이 쓸 수있다 라인 console.log(uniqueInteger()); 호출된다

function uniqueInteger(){  
    var counter = 0; 

    // an example of nested function accessing the counter variable: 
    return function() { return ++counter }; 
} 

console.log(uniqueInteger()()); // returns '1' 
console.log(counter); // undefined 

우리가 볼 수있는 바와 같이, 중첩 된 기능은 여전히 ​​변수 카운터와 액세스 갖는다 uniqueInteger() 함수는 여전히 "1"을 반환합니다. uniqueInteger()이 반환 된 후에 카운터 변수가 계속 사라지더라도 (나중에이 문제를 해결할 것입니다.) 중첩 된 함수가 counter에 액세스한다는 사실은이 변수에 일을 수행 한 다음 결과를 반환 할 수있는 기능을 제공합니다. uniqueInteger()이 호출 될 때마다 동일한 "범위 체인"이 존재하게됩니다. 매우 단순하게 말하면 이것을 어휘 범위이라고합니다.

이제 함수가 반환 된 후 변수 카운터가 사라지는 문제에 대해 살펴 보겠습니다. 여기서 일어나는 일은 가비지 콜렉션이라는 자바 스크립트 기능의 결과입니다. 함수와 변수가 더 이상 사용되지 않으면 "버려집니다". 그러나 함수의 반환 값에 대한 참조가있는 경우 (예를 들어 해당 함수가 외부 변수에 할당 된 경우), 우리가 말한 "범위 체인"에 따라 함수 내부의 변수와 함께 "기억"됩니다 위 :

function uniqueInteger(){  
    var counter = 0; 
    return function() { return ++counter }; 
} 

var uniqueInt = uniqueInteger(); 

console.log(uniqueInt()); // returns '1' 
console.log(uniqueInt()); // returns '2' 

console.log(counter) // still "undefined" 

그래서 어떻게 되었습니까? 중첩 된 함수의 반환 값을 외부 변수에 "저장"했기 때문에 변수 counter은 가비지 수집을하지 않았고 다음에 uniqueInt()이 호출되었을 때 counter는 여전히 1과 같습니다. 또한 상태가 저장되었다고 말할 수 있습니다 . 우리는 우리의 목표를 달성했습니다 : 우리는 변수를 변수로 정의하지 않고 호출간에 변수를 기억하고 비공개로 유지하는 함수를 만들었습니다.

우리는 위의 함수 정의의 표현으로이 기능을 다시 작성할 수 있습니다 :

var uniqueInteger = (function(){  
    var counter = 0; 
    return function() { return ++counter }; 
})(); 

console.log(uniqueInteger()); // returns '1' 
console.log(uniqueInteger()); // returns '2' 

console.log(counter) // still "undefined" 

참고 두 가지 기능, 따라서이 해당 호출이 여전히 있다는 것을. 이번에는 함수 자체가 아니라 반환 값을 변수 uniqueInteger에 저장하기 위해 외부 함수가 자체 호출됩니다. 그렇지 않으면 변수 uniqueInteger의 반환 값은 function() { return ++counter; }이됩니다. 위에서 설명한 기술은 본질적으로 클로저가 무엇입니까? 함수 내부에서 함수 (* 또는 객체)를 사용하여 호출간에 상태를 저장하면서 내부 값을 비공개로 유지하는 내부 값을 조작합니다.

var uniqueInteger = (function(){  
    var counter = 0; 
    return { 
      value: function() { return counter }, 
      count: function() { return ++counter }, 
      reset: function() { counter = 0; return counter;} 
    }; 

})(); 

console.log(uniqueInteger.value()); // 0 
uniqueInteger.count(); 
console.log(uniqueInteger.value()); // 1 
uniqueInteger.reset() 
console.log(uniqueInteger.value()); // again 0 

이 패턴은 당신이 자신의 개인 변수에 운영 자체에 포함 된 함수 객체를 가질 수 있습니다 :

또한 외부 함수의 값을 조작 기능을 가진 객체를 반환 할 수 있습니다 *

이름 충돌이나 외부로부터의 악의적 인 조작의 위험을 피하십시오.

나는 폐쇄를 너무 힘들어했지만 문학을 계속 읽으면 결국 결과가 나옵니다. 그냥 코드 주위에 놀고 계속 :

관련 문제