>  기사  >  웹 프론트엔드  >  클로저에 대한 Node.js의 심층적인 이해(코드 첨부)

클로저에 대한 Node.js의 심층적인 이해(코드 첨부)

亚连
亚连원래의
2018-05-19 14:13:561103검색

이 글에서는 js의 클로저(closure)를 주로 소개하는데, 클로저는 js에서 상대적으로 이해하기 어려운 부분입니다.

클로저는 js에서 상대적으로 이해하기 어려운 점입니다. 특히 프로그래밍 기초가 없는 사람들에게는 더욱 그렇습니다.

사실 클로저에 관해 주의해야 할 사항이 몇 가지 있습니다. 모두 이해한다면 정복하는 것은 어렵지 않습니다. 클로저의 몇 가지 기본 원칙에 대해 이야기해 보겠습니다.

클로저의 개념

클로저는 생성된 함수에 함수와 범위 개체가 결합된 것입니다. (범위 객체는 아래에서 논의됩니다.)

더 간단히 말하면, "하나 이상의 함수가 함수에 중첩되어 있는 한 이를 클로저라고 부를 수 있습니다."

이와 유사합니다:

function A() {
 var i = 5;
 return function() {
  console.log('i = '+i);
 }
}

var a = A();
a(); // i = 5

클로저 원칙

1. 외부 함수의 로컬 변수가 클로저 함수에 의해 호출되면 외부 함수가 실행된 후 즉시 재활용되지 않습니다.

우리는 언어에 관계없이 운영 체제에는 초과 할당 공간을 재활용하여 메모리를 줄이는 가비지 수집 메커니즘이 있다는 것을 알고 있습니다. 함수 호출이 완료되면 함수 내부의 지역 변수가 재활용 메커니즘에 의해 재활용됩니다.

위의 예를 예로 들어보겠습니다. 외부 함수 A가 호출되면 A의 지역 변수 i는 운영 체제에 의해 재활용되며 존재하지 않습니다. 그러나 클로저를 사용하면 결과는 다음과 같습니다. 그거. 난 재활용 안 할 거야. 상상해 보세요. i가 재활용되면 반환된 함수가 정의되지 않은 것으로 인쇄되지 않을까요?

i왜 재활용되지 않나요?

JavaScript가 함수를 실행할 때 범위 개체를 생성하고 함수에 지역 변수를 저장하며(함수의 형식 매개변수도 지역 변수임) 함수에 전달된 변수와 함께 초기화됩니다.

A가 호출되면 범위 객체가 생성됩니다. Aa라고 부르면 이 Aa는 다음과 같아야 합니다. Aa { i: 5 }; A 함수가 함수를 반환한 후 A가 실행됩니다. Aa 객체는 재활용되어야 하지만 반환된 함수가 Aa의 i 특성을 사용하기 때문에 반환된 함수는 Aa에 대한 참조를 저장하므로 Aa는 재활용되지 않습니다.

그래서 범위 개체를 이해하면 클로저가 발생할 때 함수 호출이 완료될 때 함수의 로컬 변수가 즉시 재활용되지 않는 이유를 이해할 수 있습니다.

또 다른 예:

function A(age) {
 var name = 'wind';
 var sayHello = function() {
  console.log('hello, '+name+', you are '+age+' years old!');
 };
 return sayHello;
}
var wind = A(20);
wind(); // hello, wind, you are 20 years old!

스코프 객체 Ww가 무엇인지 알 수 있나요?

Ww{ age: 20; name: 'wind'; };

2. 외부 함수가 호출될 때마다 새로운 클로저가 생성되며 이전 클로저가 계속 존재하며 서로 영향을 미치지 않습니다.

3. 동일한 클로저는 마지막 상태를 유지하며, 다시 호출되면 마지막 상태를 기준으로 합니다.

외부 함수가 호출될 때마다 생성되는 범위 개체가 다릅니다. 위의 예에서 전달하는 매개 변수 age가 매번 다르기 때문에 생성되는 개체가 다릅니다.

외부 함수가 호출될 때마다 새로운 범위 개체가 생성됩니다.

function A() {
 var num = 42;
 return function() { console.log(num++); }
}
var a = A();
a(); // 42
a(); // 43

var b = A(); // 重新调用A(),形成新闭包
b(); // 42

이 코드를 사용하면 두 가지를 발견할 수 있습니다. 첫째, a()를 두 번 연속 호출하면 num이 원래 기준으로 증가합니다. 이는 동일한 클로저가 마지막 상태를 유지하며 다시 호출될 때 마지막 상태를 기반으로 한다는 것을 의미합니다. 2. b()의 결과는 42이며 이는 새로운 클로저이며 다른 클로저의 영향을 받지 않음을 나타냅니다.

비눗방울을 불듯이 이렇게 생각하면 됩니다. (외부 함수를 호출할 때마다) 새로운 비눗방울(마개)이 동시에 생성될 수 있습니다. 두 개의 비누 거품이 동시에 존재할 수 있습니다. 거품은 서로 영향을 미치지 않습니다.

4. 외부 함수에 존재하는 여러 함수 "살고 죽는다"

다음 세 가지 함수는 동시에 선언되며 범위 개체의 속성(지역 변수)에 액세스하고 작동할 수 있습니다.

var fun1, fun2, fun3;
function A() {
 var num = 42;
 fun1 = function() { console.log(num); }
 fun2 = function() { num++; }
 fun3 = function() { num--; } 
}

A();
fun1();  // 42
fun2(); 
fun2(); 
fun1();  // 44
fun3(); 
fun1();  //43

var old = fun1;

A(); 
fun1();  // 42
old();  // 43  上一个闭包的fun1()

함수는 여러 개의 반환 값을 가질 수 없기 때문에 전역 변수를 사용했습니다. A()를 두 번째 호출할 때 새로운 클로저가 생성되는 것을 다시 볼 수 있습니다.

클로저가 루프 변수를 만날 때

클로저에 대해 이야기할 때는 클로저가 루프 변수를 만날 때의 상황에 대해 이야기해야 합니다:

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    var item = &#39;item&#39; + i;
    result.push( function() {console.log(item + &#39; &#39; + arr[i])} );
  }
  return result;
}

var fnlist = buildArr([1,2,3]);
fnlist[0](); // item2 undefined
fnlist[1](); // item2 undefined
fnlist[2](); // item2 undefined

어떻게 이런 일이 일어날 수 있을까요? 일어나다? ? 우리가 구상하는 세 가지 출력은 item0 1, item1 2, item2 3이어야 합니다. 반환된 결과 배열에 정의되지 않은 item2가 세 개 저장되어 있는 이유는 무엇입니까?

原来当闭包遇到循环变量时都是循环结束之后统一保存变量值,拿我们上面的例子来说,i是循环变量,当循环全部结束的时候i正好是i++之后的3,而arr[3]是没有值的,所以为undefined,有人会疑惑:为什么item的值是item2,难道不应该是item3吗?注意,在最后一次循环的时候也就是i = 2的时候,item的值为item2,当i++,i = 3循环条件不满足循环结束,此时的item的值已经定下来了,所以此时的arr[i]为arr[3],而item为item2。这样能理解吗?如果我们将代码改成这样那就说得通了:

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) { 
    result.push( function() {console.log(&#39;item&#39; + i + &#39; &#39; + arr[i])} );
  }
  return result;
}

var fnlist = buildArr([1,2,3]);
fnlist[1](); // item3 undefined

那么问题来了,如何改正呢?且看代码:

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    result.push( (function(n) {
      return function() {
       var item = &#39;item&#39; + n;
       console.log(item + &#39; &#39; + arr[n]);
      }
    })(i));
  }
  return result;
}

var fnlist = buildArr([1,2,3]);
fnlist[0](); // item0 1
fnlist[1](); // item1 2
fnlist[2](); // item2 3

我们可以用一个自执行函数将i绑定,这样i的每一个状态都会被存储,答案就和我们预期的一样了。

所以以后在使用闭包的时候遇到循环变量我们要习惯性的想到用自执行函数来绑定它。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

js 闭包常见的两种情况的解析

浅谈js 闭包引起的内存泄露问题_javascript技巧

让你分分钟学会 JS 闭包

위 내용은 클로저에 대한 Node.js의 심층적인 이해(코드 첨부)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.