>  기사  >  웹 프론트엔드  >  JavaScript의 클로저 자세히 살펴보기

JavaScript의 클로저 자세히 살펴보기

PHPz
PHPz앞으로
2016-05-16 16:32:511954검색

이 글에서는 JavaScript의 클로저에 대해 소개합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.

JavaScript의 클로저 자세히 살펴보기

클로저 - 매우 중요하지만 파악하기 어려운 개념입니다. 클로저를 이해하는 것은 어떤 의미에서는 재탄생이라고 볼 수 있습니다. - "J를 모르세요" 🎜> 비록 클로저를 보시는 분들은 다들 지치시겠지만, 그래도 정리해보려고 합니다! ! !

1. 클로저란 무엇인가

이름에서 알 수 있듯이 문제가 발생하면 먼저 우리의 평소 사고방식이 왜 그러냐고 묻자. 대답해 보세요:

  • 클로저는 함수 내의 하위 함수입니다. -

    等于没说

  • 함수가 기억하고 해당 함수가 속한 어휘 범위에 액세스합니다. 함수가 현재 어휘 범위 외부에서 실행되는 경우에도 폐쇄됩니다. ——

    靠谱

  • 클로저는 다른 함수의 내부 변수를 읽을 수 있는 함수입니다. 본질적으로 함수의 내부 링크와 외부 링크를 연결하는 역할을 합니다.——

    靠谱

  • 함수와 주변 상태(어휘 환경)에 대한 참조가 함께 묶여 클로저를 형성합니다. -

    很靠谱

Use code를 사용하여 위 답변을 설명하고 어떤 답변이

가장 마음에 드는지~

1.1 클로저는 함수 내의 하위 함수입니다

먼저 이 코드를 보세요:

function foo(params) {
    var a = '余光';

    function bar() {
        console.log(a);
    }
    bar()
}

foo(); // 余光
어휘 범위의 검색 규칙에 따라

bar函数 변수를 성공적으로 인쇄할 수 있으며 a의 하위 기능이기도 합니다. 엄밀히 말하면 클로저의 개념이 명확하게 표현되어 있지 않습니다. foo 중첩 함수가 큰 외부 범위에 선언된 변수에 액세스할 수 있다고 말하는 것이 더 정확합니다.

1.2 클로저는 다른 함수의 내부 변수를 읽을 수 있는 함수입니다. 본질적으로 내부 함수와 함수의 외부 링크를 연결하는 역할을 합니다.

보세요. 다음 예에서:

function foo(params) {
    var a = '余光';

    function bar() {
        console.log(a);
    }
    return bar;
}

var res = foo();
res(); // 余光
의 결과는

res 함수를 실행할 때 반환된 foo 참조이고 bar 함수가 어휘 환경을 저장할 수 있기 때문에 일관성이 있습니다. bar

1.3 함수와 주변 상태(어휘 환경)에 대한 참조가 함께 묶여 클로저를 형성합니다.

다음 코드를 살펴보겠습니다.

var name = '余光';

function foo() {
  console.log(name); // 余光 
}

foo(); //余光
foo의 컨텍스트는 정적으로 저장되며, 함수 생성 시 저장됩니다. 아래에서 확인해 보겠습니다.

var name = '余光';

function foo() {
  console.log(name); // 余光
}

(function (func) {
    var name = '老王';

    func()
})(foo); // 余光
여기서 이해할 수 있습니다. 클로저는 함수가 생성된 후에 형성되며,

의 개념이라면 상위 컨텍스트의 범위 체인을 저장합니다. > 이미 모호하므로 "JavaScript의 실행 컨텍스트" 기사를 읽는 데 몇 분 정도 시간이 걸릴 수도 있습니다. [[scope]][[scope]]

1.4 요약

참고: 클로저가 함수 내에서 반환된 하위 함수라는 설명은 그 자체로 정확하지만

무엇 시작점: ECMAScript에서 클로저는 다음을 의미합니다.

    이론적 관점에서 보면 모든 기능입니다. 왜냐하면 모두 생성 시 상위 컨텍스트의 데이터를 저장하기 때문입니다. 이는 간단한 전역 변수의 경우에도 마찬가지입니다. 함수에서 전역 변수에 액세스하는 것은 자유 변수에 액세스하는 것과 동일하기 때문입니다.
  • 실용적인 관점에서 보면 다음 함수는 클로저로 간주됩니다.
  • 생성된 컨텍스트가 파괴되더라도 여전히 존재합니다(예: , 내부 함수가 함수에 반환됨)
    • 은 코드의 자유 변수를 나타냅니다.
요약:

Closed 패키지 코드 블록은 코드 블록의 컨텍스트에서 데이터의 조합을 생성합니다.
  • 클로저는 본질적으로 다른 함수의 내부 변수를 읽을 수 있는 함수입니다. 함수의 내부 및 외부 링크
  • 관점에 따라 클로저에 대한 해석도 달라집니다.
  • 참고: 클로저가 무엇인지 묻는 질문과 마찬가지로 클로저가 모두 클로저인 것은 아닙니다. 위의 답변은 그렇지 않습니다. 이 주제를 끝내지 못하면 더 많은 주제로 이어지는 경우가 많습니다.

JavaScript의 클로저 자세히 살펴보기

2. 클로저를 분석해 보세요

아직도 고전적인 코드입니다.

먼저 우리는 이 코드에서 실행 컨텍스트 스택과 실행 컨텍스트의 변경 사항을 분석합니다.
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}

var foo = checkscope();
foo(); // local scope

    전역 코드 입력, 전역 실행 컨텍스트 생성, 전역 실행 컨텍스트를 실행 컨텍스트 스택에 푸시
  • 전역 실행 컨텍스트 초기화
  • checkscope 함수 실행, checkscope 함수 실행 컨텍스트 생성, checkscope 실행 컨텍스트가 실행 컨텍스트 스택으로 푸시됨
  • checkscope 실행 컨텍스트 초기화, 변수 생성 object,scope chain,this 등
  • checkscope 함수가 실행되고, checkscope 실행 컨텍스트가 실행 컨텍스트 스택에서 팝됩니다
  • 执行 f 函数,创建 f 函数执行上下文,f 执行上下文被压入执行上下文栈

  • f 执行上下文初始化,创建变量对象、作用域链、this等

  • f 函数执行完毕,f 函数上下文从执行上下文栈中弹出

JavaScript의 클로저 자세히 살펴보기

f 函数执行的时候,checkscope 函数上下文已经被销毁了啊(即从执行上下文栈中被弹出),怎么还会读取到 checkscope 作用域下的 scope 值呢?

当我们了解了具体的执行过程后,我们知道 f 执行上下文维护了一个作用域链:

因为这个作用域链:

  • f 函数依然可以读取到 checkscopeContext.AO 的值;
  • f 函数引用了 checkscopeContext.AO 中的值的时候,即使 checkscopeContext 被销毁了,JavaScript 依然会让 checkscopeContext.AO 活在内存中;
  • f 函数依然可以通过 f 函数的作用域链找到它,正是因为 JavaScript 做到了这一点,从而实现了闭包这个概念。

多么浪漫的思想——只要你需要我,那我我本应该被销毁,你也能找到我~

JavaScript의 클로저 자세히 살펴보기

三、经典问题

3.1 多个对象引用同一个[[Scope]],你遇到过吗?

直接上代码:

var child1;
var child2;
function parent() {
    var x = 1;

    child1 = function () {
        console.log(++x)
    };
    child2 = function () {
        console.log(--x)
    };
}
parent();
child1(); // 2
child1(); // 3
child2(); // 2

大家可能不理解,child1child他们两个函数在创建后都保存了上层上下文,万万没想到,同一个上下文创建的闭包是共用一个[[scope]]属性的,某个闭包对其中[[Scope]]的变量做修改会影响到其他闭包对其变量的读取。

3.2 闭包轻松解决的经典问题

大家一定对下面这段代码很眼熟:

var arr = []
for(var i = 0; i < 10; i++){
    arr[i] = function () {
        console.log(i)
    }
}
arr[0](); // 10
arr[1](); // 10
arr[2](); // 10
arr[3](); // 10

我们这么解释它:同一个上下文中创建的闭包是共用一个[[Scope]]属性的

因此上层上下文中的变量i是可以很容易就被改变的。

arr[0],arr[1]…arr[9]他们共用一个[[scope]],最终执行的时候结果当然一样。

如何利用闭包来解决这个问题呢?

var arr = []
for(var i = 0; i < 10; i++){
    arr[i] = (function (i) {
        return function () {
            console.log(i);
        }
    })(i)
}
arr[0](); // 0
arr[1](); // 1
arr[2](); // 2
arr[3](); // 3

我们通过立即执行匿名函数的方式隔离了作用域,当执行 arr[0] 函数的时候,arr[0] 函数的作用域链发生了改变:

arr[0]Context = {
    Scope: [AO, 匿名函数Context.AO globalContext.VO]
}

匿名函数执行上下文的AO为:

匿名函数Context = {
    AO: {
        arguments: {
            0: 0,
            length: 1
        },
        i: 0
    }
}

我们看到,这时函数的[[Scope]]属性就有了真正想要的值了,为了达到这样的目的,我们不得不在[[Scope]]中创建额外的变量对象。要注意的是,在返回的函数中,如果要获取i的值,那么该值还是会是10。

3.3 总结

  • 函数内的所有内部函数都共享一个父作用域,因此创建的闭包是共用的。
  • 利用闭包隔离作用域的特性可以解决共享作用域的问题

推荐学习:《PHP视频教程

성명:
이 기사는 csdn.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제