>  기사  >  웹 프론트엔드  >  계속해서 javascript closure_javascript 기술을 배우세요.

계속해서 javascript closure_javascript 기술을 배우세요.

WBOY
WBOY원래의
2016-05-16 15:27:55936검색

1. 폐쇄란 무엇인가요?

공식적인 설명은 다음과 같습니다. 클로저는 많은 변수와 이러한 변수에 바인딩된 환경을 포함하는 표현식(일반적으로 함수)이므로 이러한 변수도 표현식의 일부입니다.
나는 그의 설명이 너무 학문적이기 때문에 이 문장을 직접적으로 이해할 수 있는 사람은 거의 없다고 생각합니다. 사실, 일반인의 관점에서 이 문장은 다음을 의미합니다. JavaScript의 모든 함수는 클로저입니다. 그러나 일반적으로 말하면, 중첩된 함수에 의해 생성된 클로저가 더 강력하며, 우리는 이를 대부분 "클로저"라고 부릅니다. 다음 코드를 보세요:

function a() { 
 var i = 0; 
 function b() { alert(++i); } 
 return b;
}
var c = a();
c();

이 코드에는 두 가지 특징이 있습니다.

1. 함수 b는 함수 a 안에 중첩되어 있습니다.

2. 함수 a는 함수 b를 반환합니다.

참조 관계는 그림과 같습니다.

이렇게 var c=a()를 실행한 후 변수 c는 실제로 함수 b를 가리킵니다. c()를 다시 실행하면 i 값을 표시하는 창이 팝업됩니다(처음에는 1입니다). 이 코드는 실제로 클로저를 생성합니다. 이유는 무엇입니까? 함수 a 외부의 변수 c는 함수 a 내의 함수 b를 참조하기 때문에 다음과 같습니다.

 함수 a의 내부 함수 b가 함수 a 외부의 변수에 의해 참조되면 클로저가 생성됩니다.

좀 더 철저하게 해보자. 소위 "클로저"는 생성자 본문에 있는 다른 함수를 대상 개체의 메서드 함수로 정의하고, 이 개체의 메서드 함수는 차례로 외부 함수 본문에 있는 임시 변수를 참조하는 것입니다. 이를 통해 대상 개체가 수명 동안 항상 메서드를 유지할 수 있는 한 원래 생성자 본문에서 사용된 임시 변수 값을 간접적으로 유지할 수 있습니다. 초기 생성자 호출이 종료되고 임시변수의 이름은 사라졌지만, 변수의 값은 항상 대상 객체의 메소드 내에서 참조할 수 있으며, 이 메소드를 통해서만 값에 접근할 수 있다. 동일한 생성자를 다시 호출하더라도 새 개체와 메서드만 생성되며 새 임시 변수는 마지막 호출과 독립적인 새 값에만 해당합니다.

2. 클로저의 기능은 무엇인가요?

간단히 말해, 클로저의 기능은 a가 실행되고 반환된 후 a의 내부 함수 b의 실행이 a에 의존해야 하기 때문에 클로저가 Javascript의 가비지 수집 메커니즘 GC가 a가 점유한 리소스를 회수하는 것을 방지한다는 것입니다. 의 변수 이는 클로저의 역할에 대한 매우 간단한 설명입니다. 전문적이거나 엄격하지는 않지만 일반적인 의미는 클로저를 이해하는 데에는 단계별 프로세스가 필요하다는 것입니다.

위의 예에서 클로저의 존재로 인해 a의 i는 함수 a가 반환된 후에 항상 존재하게 됩니다. 이런 식으로 c()가 실행될 때마다 i는 추가 후 경고되는 i 값이 됩니다. 1.

그러면 a가 b가 아닌 다른 것을 반환한다면 상황은 완전히 달라집니다. 왜냐하면 a가 실행된 후 b는 a의 외부 세계로 반환되지 않고 a에 의해서만 참조되기 때문입니다. 이때 a는 b에 의해서만 참조됩니다. 따라서 함수 a와 b는 서로를 참조하지만 방해받지 않습니다. 외부 세계에서 참조됨), 함수 a 및 b는 GC에 의해 재활용됩니다. (Javascript의 가비지 수집 메커니즘은 나중에 자세히 소개하겠습니다.)

3. 클로저 속 미시세계

클로저와 함수 a와 중첩 함수 b 사이의 관계를 더 깊이 이해하려면 함수 실행 컨텍스트(실행 컨텍스트), 활성 객체(호출 객체), 범위(범위) 등 몇 가지 다른 개념을 도입해야 합니다. ) ), 범위 체인. 이러한 개념을 설명하기 위해 정의부터 실행까지 함수 a의 프로세스를 예로 들어 보겠습니다.

  • 함수 a를 정의할 때 js 인터프리터는 함수 a의 범위 체인(scope chain)을 a를 정의할 때 a가 있는 "환경"으로 설정합니다. 전역 함수인 경우 범위 체인에는 창 개체만 있습니다.
  • 함수 a를 실행할 때 a는 해당 실행 컨텍스트로 들어갑니다.
  • 실행 환경을 생성하는 과정에서 먼저 범위인 a에 범위 속성이 추가되며, 의 값이 범위 체인입니다. 1단계에서. 즉, a.scope=a의 범위 체인입니다.
  • 그런 다음 실행 환경은 호출 개체를 생성합니다. 활성 객체도 속성을 가진 객체이지만 프로토타입이 없고 자바스크립트 코드를 통해 직접 접근할 수 없습니다. 활성 개체를 만든 후 범위 체인의 맨 위에 활성 개체를 추가합니다. 이때 a의 범위 체인에는 a의 활성 개체와 창 개체라는 두 개체가 포함됩니다.
  • 다음 단계는 함수 a를 호출할 때 전달된 매개변수를 보유하는 활성 객체에 인수 속성을 추가하는 것입니다.
  • 마지막으로 함수 a의 모든 형식 매개변수와 내부 함수 b에 대한 참조를 a의 활성 개체에 추가합니다. 이번 단계에서는 함수 b의 정의가 완료되므로 3단계와 마찬가지로 함수 b의 스코프 체인은 b가 정의된 환경, 즉 a의 스코프에 설정된다.

이제 전체 함수 a의 정의부터 실행까지의 단계가 완료됩니다. 이때 a는 함수 b의 참조를 c에 반환하고 함수 b의 범위 체인에는 함수 a의 활성 개체에 대한 참조가 포함되어 있습니다. 이는 b가 a에 정의된 모든 변수와 함수에 액세스할 수 있음을 의미합니다. 함수 b는 c에 의해 참조되고 함수 b는 함수 a에 의존하므로 함수 a는 반환 후 GC에 의해 재활용되지 않습니다.

b 함수가 실행되면 위의 단계와 동일합니다. 따라서 실행 중 b의 범위 체인에는 다음 그림과 같이 b의 활성 개체, a의 활성 개체 및 창 개체의 3개 개체가 포함됩니다.

如图所示,当在函数b中访问一个变量的时候,搜索顺序是:

  • 先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。
  • 如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。
  • 如果整个作用域链上都无法找到,则返回undefined。

小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:

function f(x) { 
 var g = function () { return x; }
 return g;
}
var h = f(1);
alert(h()); 

这段代码中变量h指向了f中的那个匿名函数(由g返回)。

假设函数h的作用域是在执行alert(h())确定的,那么此时h的作用域链是:h的活动对象->alert的活动对象->window对象。
假设函数h的作用域是在定义时确定的,就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候,h的作用域链为:h的活动对象->f的活动对象->window对象。
如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。

运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。

四、闭包的应用场景
保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。

在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。
通过保护变量的安全实现JS私有属性和私有方法(不能被外部访问)
私有属性和方法在Constructor外是无法被访问的

function Constructor(...) { 
 var that = this; 
 var membername = value; 
 function membername(...) {...}
}

以上3点是闭包最基本的应用场景,很多经典案例都源于此。

五、Javascript的垃圾回收机制

在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。

六、结语

理解了aScript的闭包的解释和运行机制才能写出更为安全和优雅的代码,希望对大家的学习有所帮助。

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