범위
범위는 변수와 함수의 함수 범위입니다. JavaScript의 함수로 선언 된 모든 변수는 항상 기능 본문에서 볼 수 있습니다. JavaScript에는 글로벌 범위와 로컬 스코프가 있지만 블록 레벨 범위는 없습니다. 로컬 변수의 우선 순위는 글로벌 변수보다 높습니다. 몇 가지 예를 통해, 우리는 JavaScript의 범위의 "무언의 규칙"을 이해할 수 있습니다 (이것은 종종 프론트 엔드 인터뷰에서 질문하는 질문이기도합니다).
1. 변수 선언 미리
Example 1:
var scope = "global"; function scopetest () {console.log (scope); var scope = "local"} scopetest (); //한정되지 않은여기의 출력은 정의되지 않았으며 오류가 없습니다. 위에서 언급 한 기능의 선언은 항상 기능 본문에서 볼 수 있기 때문입니다. 위의 함수는 다음과 같습니다.
var scope = "global"; function scopetest () {var scope; Console.log (범위); scope = "local"} scopetest (); //현지의var를 잊어 버린 경우 변수는 글로벌 변수로 선언됩니다.
2. 블록 레벨 범위가 없습니다
우리가 일반적으로 사용하는 다른 언어와 달리 JavaScript에는 블록 수준 범위가 없습니다.
함수 scopetest () {var scope = {}; if (객체의 스코프 인스턴스) {var j = 1; for (var i = 0; i <10; i ++) {//console.log(i); } console.log (i); // output 10} console.log (j); // output 1}JavaScript에서 변수의 함수 범위는 함수 수준입니다.
var scope = "hello"; function scopetest () {console.log (scope); // ① var scope = "no"; Console.log (스코프); // ②}at의 값 출력은 실제로 정의되지 않았으며 미쳤습니다. 우리는 글로벌 변수의 값을 정의했습니다. 이 장소가 인사가되어서는 안됩니까? 실제로 위의 코드는 다음과 같습니다.
var scope = "hello"; function scopetest () {var scope; console.log (scope); // ① scope = "no"; Console.log (스코프); // ②}초기 및 글로벌 변수를 선언하는 것은 로컬 변수보다 우선 순위가 낮습니다. 이 두 규칙에 따르면, 출력이 정의되지 않은 이유를 이해하는 것은 어렵지 않습니다.
스코프 체인
JavaScript에서는 각 기능에는 고유 한 실행 컨텍스트가 있습니다. 이 환경에서 코드가 실행되면 가변 객체의 범위 체인이 생성됩니다. 스코프 체인은 객체 목록 또는 객체 체인으로 가변 객체에 순서대로 액세스 할 수 있습니다.
스코프 체인의 프론트 엔드는 현재 코드 실행 환경의 가변 객체이며 종종 "활성 객체"라고합니다. 변수 검색은 첫 번째 체인의 객체에서 시작됩니다. 객체에 가변 속성이 포함되어 있으면 검색이 중지됩니다. 그렇지 않은 경우, 검색은 글로벌 객체가 발견 될 때까지 우수한 스코프 체인을 계속 검색합니다.
스코프 체인을 단계별로 검색하면 프로그램의 성능에도 영향을 미칩니다. 변수의 범위 체인이 길수록 성능에 미치는 영향이 커집니다. 이것은 또한 우리가 글로벌 변수를 사용하지 않으려는 주요 이유입니다.
폐쇄
기본 개념
범위는 폐쇄를 이해하기위한 전제 조건입니다. 클로저는 현재 범위 내에서 외부 범위에서 변수에 액세스하는 기능을 나타냅니다.
함수 createClosure () {var name = "jack"; return {setstr : function () {name = "Rose"; }, getStr : function () {return name + ": hello"; }}} var builder = new createClosure (); builder.setStr (); console.log (vuilder.getstrs ()); // 로즈 : 안녕하세요위의 예는 함수의 두 개의 클로저를 반환하는데, 둘 다 외부 범위에 대한 참조를 유지하므로 외부 함수의 변수는 호출되는 곳마다 항상 액세스 할 수 있습니다. 함수 내부에서 정의 된 함수는 외부 함수의 활성 객체를 자체 스코프 체인에 추가합니다. 따라서 위의 예에서 내부 함수는 내부 함수를 통해 외부 함수의 속성에 액세스 할 수 있습니다. 이것은 또한 JavaScript가 개인 변수를 시뮬레이션하는 방법입니다.
참고 : 클로저에는 추가 기능 스코프가 있기 때문에 (내부 익명 함수는 외부 기능의 범위를 전달 함), 클로저는 다른 기능보다 더 많은 메모리 공간을 차지하며 과도한 사용은 메모리 사용을 증가시킬 수 있습니다.
폐쇄의 변수
클로저를 사용할 때, 범위 체인 메커니즘의 영향으로 인해 폐쇄는 내부 기능의 마지막 값 만 얻을 수 있습니다. 이것의 부작용 중 하나는 내부 함수가 루프에 있으면 변수의 값이 항상 마지막 값이라는 것입니다.
//이 인스턴스는 합리적이지 않으며 특정 지연 계수가 있습니다. 이것은 주로 폐쇄 루프 함수의 문제를 설명하기위한 것입니다. timemanage () {for (var i = 0; i <5; i ++) {settimeout (function () {console.log (i);}, 1000)}; }위의 프로그램은 예상대로 숫자 1-5를 입력하지 않고 5 번 모두 출력합니다. 다른 예를 살펴 보겠습니다.
함수 createClosure () {var result = []; for (var i = 0; i <5; i ++) {result [i] = function () {return i; }} 반환 결과;}CreateClosure () [0] ()을 반환하고 createClosure () [4] () 값을 반환합니다 값은 여전히 5입니다. 위의 두 예에서, 우리는 루프와 함께 내부 함수를 사용할 때 클로저가 존재한다는 문제를 볼 수 있습니다. 각 함수의 스코프 체인은 외부 기능 (TimeManage, createClosure)에 대한 활성 객체를 저장하기 때문에 동일한 변수를 참조합니다. 외부 함수가 반환되면 현재 I의 값은 5이므로 각 내부 기능 i의 값도 5입니다.
그렇다면이 문제를 해결하는 방법은 무엇입니까? 익명 래퍼 (익명 자체 이행 기능 표현)를 통해 예상 결과의 반환을 강요 할 수 있습니다.
function timemanage () {for (var i = 0; i <5; i ++) {(function (num) {settimeout (function () {console.log (num);}, 1000);} (i); }}또는 Closure 익명 기능에서 익명 함수 할당을 반환하십시오.
function timemanage () {for (var i = 0; i <10; i ++) {settimeout ((function (e) {return function () {console.log (e);}}) (i), 1000)}} // timemanager (); 출력 1,2,3,4,5 기능 CreateClosure () {var result = []; for (var i = 0; i <5; i ++) {result [i] = function (num) {return function () {console.log (num); } }(나); } return result;} // createClosure () [1] () 출력 1; CreateClosure () [2] () 출력 2원칙적으로 익명의 래퍼 또는 중첩 익명 함수이든 원칙적으로 함수는 값으로 전달되므로 변수의 값은 실제 매개 변수 NUM에 복사되며 익명 함수는 NUM 내부에 생성되므로 각 함수는 NUM의 사본을 갖지 않도록합니다.
이것은 폐쇄 중입니다
약간 부주의로 인해 문제가 발생할 수 있으므로 클로저에서 이것을 사용할 때 특별한주의를 기울이십시오. 일반적으로 우리는이 객체가 런타임에 함수별로 묶여 있음을 이해합니다. 글로벌 기능 에서이 객체는 창 객체입니다. 함수가 객체의 메소드로 불리는 경우, 이것은이 객체와 동일합니다 (TODO는 이것에 대해 정렬 프로세스를 수행합니다). 익명 함수의 범위는 전역 이므로이 클로저는 일반적으로 전역 객체 창을 가리 킵니다.
var scope = "global"; var object = {scope : "local", getScope : function () {return function () {return this.scope; }}}Calling Object.getScope () ()는 우리가 예상 한 로컬 대신 값을 반환합니다. 우리는 이전에 폐쇄의 내부 익명 함수가 외부 기능의 범위를 전달할 것이라고 말했습니다. 그렇다면 외부 기능을 얻지 않겠습니까? 각 함수가 호출되면이 기능은 자동으로 생성됩니다. 내부 익명 함수를 검색 할 때 활성 객체에서 원하는 변수를 검색합니다. 따라서 외부 함수 검색을 중지하면 외부 함수에서 변수에 직접 액세스 할 수 없습니다. 요컨대, 함수가 폐쇄에서 객체의 메소드로 호출되면, 메소드 내의 익명 함수에서이를 전역 변수를 가리킨다는 특별한주의를 기울이는 것이 중요합니다.
다행 스럽게도이 문제를 매우 간단하게 해결할 수 있습니다.이 문제는 외부 함수의 범위에 폐쇄로 액세스 할 수있는 변수에 저장할 수 있습니다.
var scope = "global"; var object = {scope : "local", getscope : function () {var that = this; return function () {return that.scope; }}} object.getScope () () () 값을 로컬로 반환합니다.기억과 성능
클로저에는 함수 런타임 컨텍스트와 동일한 스코프 체인 참조가 포함되므로 특정 음의 효과가 있습니다. 기능의 활성 객체와 런타임 컨텍스트가 파괴되면 활성 객체에 대한 참조가 여전히 있기 때문에 활성 객체는 파괴 될 수 없으므로 클로저는 일반 함수보다 더 많은 메모리 공간을 차지하며 IE 브라우저에서 메모리 누출을 유발할 수 있음을 의미합니다.
function bindevent () {var target = document.getElementById ( "elem"); target.onclick = function () {console.log (target.name); }}위의 예에서 익명 함수는 외부 객체 대상에 대한 참조를 생성합니다. 익명 함수가 존재하는 한, 기준이 사라지지 않으며 외부 함수의 대상 객체가 파괴되지 않아 순환 참조가 생성됩니다. 해결책은 target.name의 사본을 만들어 외부 변수에 대한 원형 참조를 줄이는 것입니다.
function bindevent () {var target = document.getElementById ( "elem"); var name = target.name; target.onclick = function () {console.log (이름); } target = null; }폐쇄에서 외부 변수에 액세스 할 수있는 경우 식별자 검색 경로가 의심 할 여지없이 추가되며 특정 상황에서는 성능 손실도 발생합니다. 앞에서 언급했습니다. 스코프 체인의 검색 길이를 줄이기 위해 외부 변수를 로컬 변수에 저장하십시오.
요약 : 폐쇄는 JavaScript에 고유하지 않지만 JavaScript에서 고유 한 고유 한 표현이 있습니다. 클로저를 사용하여 일부 개인 변수를 JavaScript에서 정의하고 블록 레벨 범위를 모방 할 수도 있습니다. 그러나 폐쇄를 사용하는 동안 불필요한 문제를 피하기 위해 기존 문제를 이해해야합니다.