Jadual Kandungan
前言
如何定义闭包
闭包涉及哪些知识点
作用域与作用域链
词法作用域
题外话,如何欺骗词法作用域?
闭包能干啥?
如何区分闭包?
闭包真的会导致内存泄漏?
Rumah hujung hadapan web tutorial js 一起认识闭包

一起认识闭包

Sep 08, 2020 pm 01:25 PM
penutupan

一起认识闭包

相关学习推荐:javascript视频教程

前言

闭包 永远都是前端开发者绕不过去的一个坎,不管你喜欢与否,在工作和面试中,都会遇到。每个人对闭包的理解都不尽相同,这里笔者谈谈自身对闭包的理解。(如果与您的理解有出入,请以您自己为准  )

如何定义闭包

在给出定义之前,不妨看看别人是如何定义闭包的:

函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包” -- JavaScript权威指南(第六版)

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。 -- JavaScript高级程序设计(第三版)

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。 -- 你不知道的JavaScript(上卷)

虽然上面的几段话描述起来并不一样,但是您细细品味后还是能找出一些共同点。其中最重要的是不同作用域之间的联系。当然了,您可以直接引用上面的定义(毕竟上面几个定义还是比较权威的),这里笔者比较喜欢最后一段的定义,同时力推《你不知道的JavaScript(上卷)》这本书,值得反复细读。

闭包涉及哪些知识点

光给出定义是远远不够的,还必须探讨内部涉及了哪些知识点。下面是笔者认为有用到的知识点。

作用域与作用域链

嗯,其实笔者知道你们都想到了这点(不会吧,不会有人没想到这点吧)。既然大家都了解作用域。这里就简单描述一下,过一下场即可。

作用域:根据名称查找变量的一套规则。分为三种类型:全局作用域;函数作用域;块作用域。

需要注意的是块作用域,ES6新增的规范。 在花括号{}里面使用let,const定义的变量,都会绑定到该作用范围内,花括号以外的地方无法访问。注意:在花括号开始 到 let变量声明之前,存在暂时性死区(该点不在本文讨论范围)。

作用域链:当不同的作用域 (混~淆~在~一~起~ 呸,不小心出戏了) 圈套在一起时,就形成了作用域链。注意的是,查找方向是从内到外的。

为什么作用域的查找方向是从内到外的呢?这是个很有趣的问题。个人觉得是跟js执行函数的入栈方式决定的(感觉有点偏题了,有兴趣的小伙伴可以去查一下资料)。

词法作用域

函数之所以 可以访问另一个函数作用域的变量(或者说记住当前的作用域并在当前以外的地方访问)的关键点就是词法作用域在起作用。这一点很重要,但不是所有人都知道这个知识点,这里简单探讨一下。

在编程界中,存在两种作用域工作模式,一种是被大多数编程语言所采用的词法作用域;另一种就是与其相反的动态作用域(这个不在本文的讨论范围)。

词法作用域: 变量和块的作用域 在 您编写代码的阶段 就已经确定好了,不会随着调用的对象或者地方的不同而改变(感觉跟this相反)。

要不,举个栗子看看吧:

let a = 1;
function fn(){
    let a = 2;
    function fn2(){
        console.log(a);
    }
 return fn2;
}

let fn3 = fn();
fn3();
Salin selepas log masuk

从上面的定义可以知道,fn是一个闭包函数,fn3拿到了fn2的指针地址,当fn3执行的时候,其实是执行fn2,而里面的a变量,根据作用域链的查找规则,找到的是fn作用域内的变量a,所以最终的输出是2,不是1。(可以看下图)

一起认识闭包

题外话,如何欺骗词法作用域?

虽然词法作用域是静态的,但依然有办法可以欺骗它,达到动态的效果。

第一种方法是使用eval. eval可以把字符串解析成一个脚本来运行,由于在词法分析阶段,无法预测eval运行的脚本,所以不会对其进行优化分析。

第二种方法是with. with通常被当作重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。with本身比较难掌握,使用不当容易出现意外情况(如下例子),不推荐使用 -.-

function Fn(obj){
    with(obj){
        a = 2;
    }
}

var o1 = {
    a:1
}
var o2 = {
    b:1
}

Fn(o1);
console.log(o1.a); //2
Fn(o2);
console.log(o2.a); //undefined;
console.log(a); //2 a被泄漏到全局里面去了
// 这是with的一个副作用, 如果当前词法作用域没有该属性,会在全局创建一个
Salin selepas log masuk

闭包能干啥?

闭包的使用场景可多了,平时使用的插件或者框架,基本上都有闭包的身影,可能您没留意过罢了。下面笔者列举一些比较常见的场景。

  1. 模拟私有变量和方法,进一步来说可以是模拟模块化;目前常用的AMD,CommonJS等模块规范,都是利用闭包的思想;

  2. 柯里化函数或者偏函数;利用闭包可以把参数分成多次传参。如下面代码:

// 柯里化函数
function currying(fn){
    var allArgs = [];

    function bindCurry(){
        var args = [].slice.call(arguments);
        allArgs = allArgs.concat(args);
        return bindCurry;
    }
    bindCurry.toString = function(){
        return fn.apply(null, allArgs);
    };

    return bindCurry;
}
Salin selepas log masuk
  1. 实现防抖或者节流函数;

  2. 实现缓存结果(记忆化)的辅助函数:

// 该方法适合缓存结果不易改变的函数
const memorize = fn => {
    let memorized = false;
    let result = undefined;
    return (...args) => {
        if (memorized) {
            return result;
        } else {
            result = fn.apply(null,args); 
            memorized = true;
            fn = undefined;
            return result;
        }
    };
};
Salin selepas log masuk

如何区分闭包?

说了那么多,我怎么知道自己写的代码是不是闭包呢?先不说新手,有些代码的确隐藏的深,老鸟不仔细看也可能发现不了。 那有没有方法可以帮助我们区分一个函数是不是闭包呢?答案是肯定的,要学会善于利用周边的工具资源,比如浏览器。

打开常用的浏览器(chrome或者其他),在要验证的代码中打上debugger断点,然后看控制台,在scope里面的Closure(闭包)里面是否有该函数(如下图)。

一起认识闭包

闭包真的会导致内存泄漏?

答案是有可能。内存泄漏的原因在于垃圾回收(GC)无法释放变量的内存,导致运行一段时候后,可用内存越来越少,最终出现内存泄漏的情况。常见的内存泄漏场景有4种:全局变量;闭包引用;DOM事件绑定;不合理使用缓存。其中,闭包导致内存泄漏都是比较隐蔽的,用肉眼查看代码判断是比较难,我们可用借助chrome浏览器的Memory标签栏工具来调试。由于篇幅问题,不展开说明了,有兴趣自己去了解一下如何使用。

想了解更多编程学习,敬请关注php培训栏目!

Atas ialah kandungan terperinci 一起认识闭包. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
2 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Repo: Cara menghidupkan semula rakan sepasukan
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Cara mendapatkan biji gergasi
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Apakah maksud penutupan dalam ungkapan lambda C++? Apakah maksud penutupan dalam ungkapan lambda C++? Apr 17, 2024 pm 06:15 PM

Dalam C++, penutupan ialah ungkapan lambda yang boleh mengakses pembolehubah luaran. Untuk membuat penutupan, tangkap pembolehubah luar dalam ungkapan lambda. Penutupan memberikan kelebihan seperti kebolehgunaan semula, penyembunyian maklumat dan penilaian malas. Ia berguna dalam situasi dunia sebenar seperti pengendali acara, di mana penutupan masih boleh mengakses pembolehubah luar walaupun ia dimusnahkan.

Bagaimana untuk melaksanakan penutupan dalam ungkapan C++ Lambda? Bagaimana untuk melaksanakan penutupan dalam ungkapan C++ Lambda? Jun 01, 2024 pm 05:50 PM

Ungkapan C++ Lambda menyokong penutupan, yang menyimpan pembolehubah skop fungsi dan menjadikannya boleh diakses oleh fungsi. Sintaks ialah [capture-list](parameters)->return-type{function-body}. capture-list mentakrifkan pembolehubah untuk ditangkap Anda boleh menggunakan [=] untuk menangkap semua pembolehubah tempatan mengikut nilai, [&] untuk menangkap semua pembolehubah tempatan melalui rujukan, atau [variable1, variable2,...] untuk menangkap pembolehubah tertentu. Ungkapan Lambda hanya boleh mengakses pembolehubah yang ditangkap tetapi tidak boleh mengubah suai nilai asal.

Apakah kelebihan dan kekurangan penutupan dalam fungsi C++? Apakah kelebihan dan kekurangan penutupan dalam fungsi C++? Apr 25, 2024 pm 01:33 PM

Penutupan ialah fungsi bersarang yang boleh mengakses pembolehubah dalam skop fungsi luar Kelebihannya termasuk pengkapsulan data, pengekalan keadaan dan fleksibiliti. Kelemahan termasuk penggunaan memori, kesan prestasi dan kerumitan penyahpepijatan. Selain itu, penutupan boleh mencipta fungsi tanpa nama dan menyerahkannya kepada fungsi lain sebagai panggilan balik atau hujah.

Selesaikan masalah kebocoran memori yang disebabkan oleh penutupan Selesaikan masalah kebocoran memori yang disebabkan oleh penutupan Feb 18, 2024 pm 03:20 PM

Tajuk: Kebocoran memori disebabkan oleh penutupan dan penyelesaian Pengenalan: Penutupan ialah konsep yang sangat biasa dalam JavaScript, yang membenarkan fungsi dalaman mengakses pembolehubah fungsi luaran. Walau bagaimanapun, penutupan boleh menyebabkan kebocoran memori jika digunakan secara tidak betul. Artikel ini akan meneroka masalah kebocoran memori yang disebabkan oleh penutupan dan menyediakan penyelesaian serta contoh kod khusus. 1. Kebocoran memori disebabkan oleh penutupan Ciri penutupan ialah fungsi dalaman boleh mengakses pembolehubah fungsi luaran, yang bermaksud pembolehubah yang dirujuk dalam penutupan tidak akan dikumpul sampah. Jika digunakan secara tidak betul,

Kesan penunjuk fungsi dan penutupan pada prestasi Golang Kesan penunjuk fungsi dan penutupan pada prestasi Golang Apr 15, 2024 am 10:36 AM

Kesan penunjuk fungsi dan penutupan pada prestasi Go adalah seperti berikut: Penunjuk fungsi: Lebih perlahan sedikit daripada panggilan langsung, tetapi meningkatkan kebolehbacaan dan kebolehgunaan semula. Penutupan: Biasanya lebih perlahan, tetapi merangkumi data dan tingkah laku. Kes praktikal: Penunjuk fungsi boleh mengoptimumkan algoritma pengisihan dan penutupan boleh mencipta pengendali acara, tetapi ia akan membawa kerugian prestasi.

Bagaimanakah penutupan dilaksanakan di Jawa? Bagaimanakah penutupan dilaksanakan di Jawa? May 03, 2024 pm 12:48 PM

Penutupan dalam Java membenarkan fungsi dalaman mengakses pembolehubah skop luar walaupun fungsi luar telah keluar. Dilaksanakan melalui kelas dalaman tanpa nama, kelas dalam memegang rujukan kepada kelas luar dan memastikan pembolehubah luar aktif. Penutupan meningkatkan fleksibiliti kod, tetapi anda perlu sedar tentang risiko kebocoran memori kerana rujukan kepada pembolehubah luaran oleh kelas dalaman tanpa nama memastikan pembolehubah tersebut hidup.

Bagaimana untuk mengelakkan kebocoran memori dengan berkesan dalam penutupan? Bagaimana untuk mengelakkan kebocoran memori dengan berkesan dalam penutupan? Jan 13, 2024 pm 12:46 PM

Bagaimana untuk mengelakkan kebocoran memori dalam penutupan? Penutupan ialah salah satu ciri yang paling berkuasa dalam JavaScript, yang membolehkan sarang fungsi dan enkapsulasi data. Walau bagaimanapun, penutupan juga terdedah kepada kebocoran memori, terutamanya apabila berurusan dengan tak segerak dan pemasa. Artikel ini menerangkan cara untuk mengelakkan kebocoran memori dalam penutupan dan menyediakan contoh kod khusus. Kebocoran memori biasanya berlaku apabila objek tidak lagi diperlukan tetapi memori yang didudukinya tidak dapat dilepaskan atas sebab tertentu. Dalam penutupan, apabila fungsi merujuk kepada pembolehubah luaran, dan pembolehubah ini

Panggilan berantai dan penutupan fungsi PHP Panggilan berantai dan penutupan fungsi PHP Apr 13, 2024 am 11:18 AM

Ya, kesederhanaan dan kebolehbacaan kod boleh dioptimumkan melalui panggilan berantai dan penutupan: panggilan berantai memaut fungsi panggilan ke antara muka yang lancar. Penutupan mewujudkan blok kod yang boleh digunakan semula dan pembolehubah akses di luar fungsi.

See all articles