이론적인 부분을 논의한 후 ECMAScript에서 클로저가 어떻게 구현되는지 소개하겠습니다. 여기서 다시 강조할 가치가 있습니다. ECMAScript는 정적(어휘적) 범위만 사용합니다(Perl과 같은 언어는 변수 선언에 정적 범위와 동적 범위를 모두 사용할 수 있습니다).
var x = 10; function foo() { alert(x); } (function (funArg) { var x = 20; // 变量"x"在(lexical)上下文中静态保存的,在该函数创建的时候就保存了 funArg(); // 10, 而不是20 })(foo);
기술적으로 말하면 함수를 생성한 상위 컨텍스트의 데이터는 함수의 내부 속성 [[Scope]]에 저장됩니다. [[범위]]가 무엇인지 모른다면, [[범위]]에 대해 매우 자세하게 소개되어 있는 이전 장을 먼저 읽어 보시기 바랍니다. [[Scope]] 및 범위 체인 지식을 완전히 이해했다면 클로저도 완전히 이해할 것입니다.
함수 생성 알고리즘에 따르면 ECMAScript에서 모든 함수는 생성 시 상위 컨텍스트의 범위 체인을 저장하기 때문에 클로저임을 알 수 있습니다(예외 제외). 나중에 활성화됨 - [[Scope]]는 함수가 생성될 때 존재합니다.):
var x = 10; function foo() { alert(x); } // foo是闭包 foo: <FunctionObject> = { [[Call]]: <code block of foo>, [[Scope]]: [ global: { x: 10 } ], ... // 其它属性 };
우리가 말했듯이 최적화 목적으로 함수가 자유 변수를 사용하지 않는 경우 그렇다면 구현이 부작용 도메인 체인에 보관되어야 합니다. 그러나 ECMA-262-3 사양에는 아무 것도 언급되어 있지 않습니다. 따라서 일반적으로 모든 매개변수는 생성 단계에서 [[Scope]] 속성에 저장됩니다.
일부 구현에서는 클로저 범위에 직접 액세스할 수 있습니다. 예를 들어 Rhino의 경우 함수의 [[Scope]] 속성에는 비표준 __parent__ 속성이 있습니다.
var global = this; var x = 10; var foo = (function () { var y = 20; return function () { alert(y); }; })(); foo(); // 20 alert(foo.__parent__.y); // 20 foo.__parent__.y = 30; foo(); // 30 // 可以通过作用域链移动到顶部 alert(foo.__parent__.__parent__ === global); // true alert(foo.__parent__.__parent__.x); // 10
모든 개체는 [[Scope]]
<🎜를 참조합니다. >here 또한 ECMAScript에서는 동일한 상위 컨텍스트에서 생성된 클로저가 [[Scope]] 속성을 공유한다는 점에 유의하세요. 즉, [[Scope]] 변수에 대한 특정 클로저의 수정은 다른 클로저의 변수 읽기에 영향을 미칩니다.var firstClosure; var secondClosure; function foo() { var x = 1; firstClosure = function () { return ++x; }; secondClosure = function () { return --x; }; x = 2; // 影响 AO["x"], 在2个闭包公有的[[Scope]]中 alert(firstClosure()); // 3, 通过第一个闭包的[[Scope]] } foo(); alert(firstClosure()); // 4 alert(secondClosure()); // 3
var data = []; for (var k = 0; k < 3; k++) { data[k] = function () { alert(k); }; } data[0](); // 3, 而不是0 data[1](); // 3, 而不是1 data[2](); // 3, 而不是2
activeContext.Scope = [ ... // 其它变量对象 {data: [...], k: 3} // 活动对象 ]; data[0].[[Scope]] === Scope; data[1].[[Scope]] === Scope; data[2].[[Scope]] === Scope;
var data = []; for (var k = 0; k < 3; k++) { data[k] = (function _helper(x) { return function () { alert(x); }; })(k); // 传入"k"值 } // 现在结果是正确的了 data[0](); // 0 data[1](); // 1 data[2](); // 2
data[0].[[Scope]] === [ ... // 其它变量对象 父级上下文中的活动对象AO: {data: [...], k: 3}, _helper上下文中的活动对象AO: {x: 0} ]; data[1].[[Scope]] === [ ... // 其它变量对象 父级上下文中的活动对象AO: {data: [...], k: 3}, _helper上下文中的活动对象AO: {x: 1} ]; data[2].[[Scope]] === [ ... // 其它变量对象 父级上下文中的活动对象AO: {data: [...], k: 3}, _helper上下文中的活动对象AO: {x: 2} ];
var data = []; for (var k = 0; k < 3; k++) { (data[k] = function () { alert(arguments.callee.x); }).x = k; // 将k作为函数的一个属性 } // 结果也是对的 data[0](); // 0 data[1](); // 1 data[2](); // 2
function getElement() { [1, 2, 3].forEach(function (element) { if (element % 2 == 0) { // 返回给函数"forEach"函数 // 而不是返回给getElement函数 alert('found: ' + element); // found: 2 return element; } }); return null; }
var $break = {}; function getElement() { try { [1, 2, 3].forEach(function (element) { if (element % 2 == 0) { // // 从getElement中"返回" alert('found: ' + element); // found: 2 $break.data = element; throw $break; } }); } catch (e) { if (e == $break) { return $break.data; } } return null; } alert(getElement()); // 2
다시 말하지만, 범위 체인으로 인해 모든 함수는 클로저입니다(함수 유형에 관계없이 익명 함수, FE, NFE 및 FD는 모두 클로저입니다).
Function 생성자를 통해 생성된 함수를 제외하고 함수 유형은 한 가지뿐입니다. [[Scope]]에는 전역 개체만 포함되기 때문입니다. 이 문제를 더 명확하게 하기 위해 ECMAScript에서 클로저에 대한 두 가지 올바른 버전 정의를 제공합니다.
ECMAScript에서 클로저는 다음을 의미합니다.
이론적 관점에서: 모든 기능. 왜냐하면 모두 생성 시 상위 컨텍스트의 데이터를 저장하기 때문입니다. 이는 간단한 전역 변수의 경우에도 마찬가지입니다. 함수에서 전역 변수에 액세스하는 것은 자유 변수에 액세스하는 것과 동일하기 때문입니다.
실용적인 관점에서 볼 때 다음 함수는 클로저로 간주됩니다.
생성된 컨텍스트가 파괴되어도 여전히 존재합니다(예를 들어 내부 함수가 상위 함수에서 반환됨). 함수)
코드에서 자유 변수를 참조합니다
위는 JavaScript 클로저의 두 번째 부분인 클로저 구현입니다. 더 많은 관련 내용을 보려면 PHP 중국어 웹사이트( www.php.cn)!