克服 JS 的奇怪部分:闭包、外部环境参照、範围链

这些观念从"克服 JS 的奇怪部分"影片中学习的,这系列影片真的是好东西,所谓的闭包(closure),也就是说执行环境可以将那些它所要reference的外部变数给关住、包住,即便执行环境已经消失,此现象称作闭包(closure),在介绍闭包之前我们先来解释一下外部环境参照(Reference to Outer Environment)、範围链(scope chain)的东西。

外部环境参照

每个执行环境都有一个参照到他的外部环境,所谓的外部环境参照(Reference to Outer Environment)就是指说当使用变数时,js不只会在当前的执行环境中寻找变数,也会到外部环境参照中寻找变数,而js会依照物理位置来判断其外部参照环境为何

此处函数b及a之物理位置皆在全域环境底下,如下列例子,首先我们全域执行环境中有func b,func a,let myVar,呼叫a函数以后,创建函数a的执行环境,并且他的变数环境有myVar=2,接着,呼叫b函数,其执行环境消失,离开执行堆,但是变数myVar=2并不会消失,而是在记忆体空间保留,再来就创建函数b的执行环境,做console操作然后执行环境消失,离开执行堆,所以b的外部参照环境就是我们的全域执行环境,而a外部参照环境也是一样。

        let myVar = 1;        function b() {            console.log(myVar);        }        function a() {            let myVar = 2;            b();        }        a(); // 1

http://img2.58codes.com/2024/20126182fZ5pfWP233.png

但当我们将在函数a中所宣告的变数myVar给取消,也就是把let拿掉,那我们的变数myVar就会变成全域变数,此时结果就会变成2,儘管我们将在全域环境所设置的myVar放置在函数a后面,结果也会是一样,我们在a所宣告的全域变数myVar就会将原本的myVar所覆盖

       let myVar = 1;        function b() {            console.log(myVar);        }        function a() {            myVar = 2;            b();        }        // let myVar = 1;        a(); // 2

範围链

再来我们解释一下範围链,範围指的就是我所能取到变数的地方,而链是指我外部环境参照的连结,所谓範围链指的就是我们需要某个执行环境内程式码的变数,如果他在的当前环境下找不到变数,会往外部环境找,在执行堆中由上往下找,直到全域环境

而下列例子,我们将函数b包在函数a中,也就是b的物理位置在函数a中,所以当我们呼叫函数a,就会创建函数b,并且变数环境会创建一个myVar=2(会保留在记忆体空间),而后离开执行堆,并呼叫函数b,而由于函数b之物理位置位于a之中,因此他会认定函数b之外部参照环境为函数a,而就会获取到未消失的myVar=2,而a的外部参照环境仍为全域环境。

        let myVar = 1;        function a() {            function b() {                console.log(myVar); // 2            }            let myVar = 2;            b();        }        a();

http://img2.58codes.com/2024/20126182NM7wLsaobq.png

闭包

介绍完上述两个观念后,我们直接来介绍闭包并且使用例子,首先我们在全域环境会有两个变数分别为greetEnglish、
greetSpanish,还有一个func makeGreeting,接着逐行执行到greetEnglish呼叫makeGreeting函数,创造其执行环境,并创造变数环境将language="en"给设置进去,而后回传匿名函数,存入greetEnglish指向其函数,然后离开执行堆,记住我们执行环境的记忆体空间仍在(en不会消失),再来greetSpanish也是同样的步骤。

http://img2.58codes.com/2024/20126182sPjzxAqQeC.png

此时,我有两个不同执行环境的记忆体位置,而后,我们呼叫greetEnglish,也就是呼叫我们所回传的函数,而后我们在创建一个匿名函数的执行环境并创建其变数环境(firstname="John", lastname="Doe"),而此时外部参考环境会指向一个执行环境,js会知道我们第一个所创建的执行环境并指向它,也就是会获取到"en",而框起来的範围就是我们的闭包,而greetSpanish的步骤一样,再次强调,每当呼叫一个函数,就会创建他的执行环境,而在里面被创建的函数,就会指向那个执行环境、指向其记忆体空间

        function makeGreeting(language) {            return function (firstname, lastname) {                if (language == "en") {                    console.log("hi" + firstname + "" + lastname);                }                if (language == "es") {                    console.log("Hola" + firstname + "" + lastname);                }            }        }        let greetEnglish = makeGreeting("en");        let greetSpanish = makeGreeting("es");        greetEnglish("John", "Doe");        greetSpanish("John", "Doe");

http://img2.58codes.com/2024/2012618258x9Q7syA1.png


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章