Rumah  >  Artikel  >  hujung hadapan web  >  Melihat lebih dekat pada penutupan dalam JavaScript

Melihat lebih dekat pada penutupan dalam JavaScript

PHPz
PHPzke hadapan
2016-05-16 16:32:511993semak imbas

Artikel ini akan memperkenalkan anda kepada penutupan dalam JavaScript. Ia mempunyai nilai rujukan tertentu Rakan-rakan yang memerlukan boleh merujuk kepadanya.

Melihat lebih dekat pada penutupan dalam JavaScript

Penutup - konsep yang sangat penting tetapi sukar untuk difahami. Memahami penutupan boleh dilihat sebagai kelahiran semula dalam erti kata - "Anda tidak tahu Js》<.> Walaupun semua orang mungkin bosan melihat penutupan, saya masih mahu cuba meringkaskannya! ! !

1. Apakah itu penutupan

Seperti namanya, apabila kita menghadapi masalah, kita mula-mula bertanya mengapa cara kita berfikir cuba jawab:

  • Penutupan ialah sub-fungsi di dalam fungsi -

    等于没说

  • Ia berlaku apabila fungsi boleh mengingati dan akses skop leksikal yang berada di dalamnya. Penutupan, walaupun fungsi tersebut dilaksanakan di luar skop leksikal semasa. ——

    靠谱

  • Penutupan ialah fungsi yang boleh membaca pembolehubah dalaman fungsi lain Ia pada asasnya merupakan jambatan antara pautan dalaman dan luaran fungsi——

    靠谱

  • Sesuatu fungsi dan rujukan kepada keadaan sekelilingnya (persekitaran leksikal) disatukan untuk membentuk penutup -

    很靠谱

Jom cuba gunakan Gunakan kod untuk menerangkan jawapan di atas dan lihat yang mana satu anda

kegemaran~

1.1 Penutupan ialah sub-fungsi dalam fungsi

Pertama Lihat kod ini:

function foo(params) {
    var a = &#39;余光&#39;;

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

foo(); // 余光
Berdasarkan peraturan carian skop leksikal,

boleh berjaya mencetak pembolehubah bar函数, dan ia juga merupakan sub-fungsi a, tetapi Secara tegasnya ia tidak Konsep penutupan tidak dinyatakan dengan jelas Adalah lebih tepat untuk mengatakan bahawa foo fungsi bersarang boleh mengakses pembolehubah diisytiharkan dalam skop luar yang besar.

1.2 Penutupan ialah fungsi yang boleh membaca pembolehubah dalaman fungsi lain Ia pada asasnya merupakan jambatan antara fungsi dalaman dan pautan luaran fungsi

Lihat. pada contoh berikut: Keputusan

function foo(params) {
    var a = &#39;余光&#39;;

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

var res = foo();
res(); // 余光
adalah konsisten kerana

ialah rujukan res yang dikembalikan apabila melaksanakan fungsi foo, dan fungsi bar dapat menyelamatkan persekitaran leksikalnya. bar

1.3 Fungsi dan rujukan kepada keadaan sekelilingnya (persekitaran leksikal) digabungkan untuk membentuk penutupan

Mari kita lihat kod berikut:

var name = &#39;余光&#39;;

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

foo(); //余光
Konteks foo disimpan secara statik dan ia disimpan apabila fungsi dibuat. Mari sahkan di bawah:

var name = &#39;余光&#39;;

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

(function (func) {
    var name = &#39;老王&#39;;

    func()
})(foo); // 余光
Di sini kita boleh faham - penutupan terbentuk selepas fungsi dibuat. Mereka menyimpan rantai skop konteks atas dan menyimpannya dalam

Jika konsep anda [[scope]] sudah samar-samar, anda mungkin mengambil masa beberapa minit untuk membaca artikel "Konteks Pelaksanaan dalam JavaScript". [[scope]]

1.4 Ringkasan

Nota: Pernyataan bahawa penutupan ialah subfungsi yang dikembalikan di dalam fungsi adalah betul dengan sendirinya, tetapi ia bergantung pada apa Titik permulaan:

Dalam ECMAScript, penutupan merujuk kepada:

  • Dari perspektif teori: semua fungsi. Kerana mereka semua menyimpan data konteks atas apabila ia dibuat. Ini adalah benar walaupun untuk pembolehubah global yang mudah, kerana mengakses pembolehubah global dalam fungsi adalah bersamaan dengan mengakses pembolehubah bebas Pada masa ini, skop paling luar digunakan.

  • Dari perspektif praktikal: fungsi berikut dianggap sebagai penutup:

      Ia masih wujud walaupun konteks di mana ia dicipta telah dimusnahkan (untuk contoh, fungsi dalam adalah Dikembalikan dalam fungsi)
    • merujuk kepada pembolehubah bebas dalam kod

Ringkasan:

    Tertutup Blok kod pakej ialah gabungan data dalam konteks di mana blok kod dicipta
  • Penutupan ialah fungsi yang boleh membaca pembolehubah dalaman fungsi lain pada asasnya jambatan antara pautan dalaman dan luaran fungsi
  • Perspektif yang berbeza mempunyai tafsiran yang berbeza tentang penutupan
Nota: Ini bukan semua penutupan, sama seperti apabila anda ditanya - apakah adalah penutupan, jawapan anda di atas tidak Kegagalan untuk menamatkan topik ini selalunya akan membawa kepada lebih banyak topik.

Melihat lebih dekat pada penutupan dalam JavaScript

2 Cuba analisa penutupan

Ia masih kod klasik:

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}

var foo = checkscope();
foo(); // local scope
Pertama. kami Untuk menganalisis perubahan dalam tindanan konteks pelaksanaan dan konteks pelaksanaan dalam kod ini.

  • Masukkan kod global, buat konteks pelaksanaan global dan tolak konteks pelaksanaan global ke dalam tindanan konteks pelaksanaan

  • Pengamatan konteks pelaksanaan global

  • Laksanakan fungsi checkscope, cipta konteks pelaksanaan fungsi checkscope, konteks pelaksanaan checkscope ditolak ke dalam timbunan konteks pelaksanaan

  • pemulaan konteks pelaksanaan checkskop, buat pembolehubah objek, rantai skop , ini, dsb.

  • Fungsi checkscope dilaksanakan dan konteks pelaksanaan checkskop muncul daripada tindanan konteks pelaksanaan

  • 执行 f 函数,创建 f 函数执行上下文,f 执行上下文被压入执行上下文栈

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

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

Melihat lebih dekat pada penutupan dalam JavaScript

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

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

因为这个作用域链:

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

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

Melihat lebih dekat pada penutupan dalam 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视频教程

Kenyataan:
Artikel ini dikembalikan pada:csdn.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam