闭包与C语言静态变量,深入了解闭包

本文深入探讨JavaScript闭包的概念,通过不同书籍的定义进行阐述,并介绍了闭包的形成原理、作用及应用场景,如模仿块级作用域、保持变量状态。同时,文章还提及闭包与C语言静态变量的相似之处,讨论了两者在保持变量持久化方面的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是闭包?下面本篇文章就来给大家全面详解一下JavaScript闭包。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

3f34025d3a44c80154a8657c27d82bf3.png

什么是闭包

最原始定义

闭包(closure),是指函数变量可以保存在函数作用域内,因此看起来是函数将变量“包裹”了起来。//根据定义,包含变量的函数就是闭包

function foo() {

var a = 0;

}

cosole.log(a)

// Uncaught ReferenceError: a is not defined

《JavaScript高级程序设计》对闭包定义

闭包是指有权访问另一个函数作用域中的变量的函数//访问上层函数的作用域的内层函数就是闭包

function foo() {

var a = 2;

function bar() {

console.log(a);

}

bar();

}

foo();

《JavaScript权威指南》对闭包定义

函数对象可以通过作用域链相互关联起来,函数体内部变量可以保存在函数作用域内,这就是闭包。var global = "global scope"; //全局变量

function checkscope() {

var scope = "local scope"; //局部变量

function f() {

return scope; //在作用域中返回这个值

};

return f();

}

checkscope(); // 返回 "local scope"

《你不知道的JavaScript》这样描述

当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。//fn3就是fn2函数本身。执行fn3能正常输出name

//这不就是fn2能记住并访问它所在的词法作用域,而且fn2函数的运行还是在当前词法作用域之外了。

function fn1() {

var name = 'iceman';

function fn2() {

console.log(name);

}

return fn2;

}

var fn3 = fn1();

fn3();

MDN 上面这么说:

闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

简单说就是指那些能够访问自由变量的函数。

严格来说,闭包需要满足三个条件:

【1】访问所在作用域;

【2】函数嵌套;

【3】在所在作用域外被调用

闭包的形成原理

先了解JavaScript的垃圾回收机制

Javascript 会找出不再使用的变量,不再使用意味着这个变量生命周期的结束。

Javascript 中存在两种变量——全局变量和局部变量,全部变量的声明周期会一直持续,直到页面卸载而局部变量声明在函数中,它的声明周期从执行函数开始,直到函数执行结束。在这个过程中,局部变量会在堆或栈上被分配相应的空间以存储它们的值,函数执行结束,这些局部变量也不再被使用,它们所占用的空间也就被释放。

但是有一种情况的局部变量不会随着函数的结束而被回收,那就是局部变量被函数外部的变量所使用,其中一种情况就是闭包,因为在函数执行结束后,函数外部的变量依然指向函数内的局部变量,此时的局部变量依然在被使用,所以也就不能够被回收var scope = 'global scope';

function checkScope() {

var scope = 'local scope';

return function() {

console.log(scope);

}

}

var result = checkScope();

result(); // local scope checkScope变量对象中的scope,非全局变量scope

此匿名函数的作用域链包括checkScope的活动对象和全局变量对象, 当checkScope函数执行完毕后,checkScope的活动对象并不会被销毁,因为匿名函数的作用域链还在引用checkScope的活动对象,也就是checkScope的执行环境被销毁,但是其活动对象没有被销毁,留存在堆内存中,直到匿名函数销毁后,checkScope的活动对象才会销毁

从作用域链理解闭包的形成从理论角度:所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此,因为函数中访问全局变量就相当于是在访问自由变量,这个时候使用最外层的作用域。

从实践角度:以下函数才算是闭包:

i. 即使创建它的上下文已经销毁,它仍然存在(比如,内部函数从父函数中返回)

ii. 在代码中引用了自由变量var scope = "global scope";

function checkscope(){

var scope = "local scope";

function f(){

return scope;

}

return f;

}

var foo = checkscope();

foo();

fContext = {//f函数的执行上下文

Scope: [AO, checkscopeContext.AO, globalContext.VO],

}

对的,就是因为这个作用域链,f函数在声明的时候压入了上层的变量对象,f 函数依然可以读取到 checkscopeContext.AO 的值,并且如果当 f 函数引用了 checkscopeContext.AO 中的值的时候,即使 checkscopeContext 被销毁了,但是 JavaScript 依然会让 checkscopeContext.AO 活在内存中(和垃圾回收机制有关下文会说),f 函数依然可以通过 f 函数的作用域链找到它,正是因为 JavaScript 做到了这一点,从而实现了闭包这个概念。

闭包的作用-模仿块级作用域,封装私有变量任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数外部访问这些变量。

私有变量包括函数的参数、局部变量和函数内定义的其他函数。function module() {

var arr = [];

function add(val) {

if (typeof val == 'number') {

arr.push(val);

}

}

function get(index) {

if (index < arr.length) {

return arr[index]

} else {

return null;

}

}

return {

add: add,

get: get

}

}

var mod1 = module();

mod1.add(1);

mod1.add(2);

mod1.add('xxx');

console.log(mod1.get(2));//外部是无法直接拿到arr的只能通过get来拿

闭包的作用-使变量保存在内存中不被销毁

实例1-计数器

我们来实现一个计数器,每调用一次计数器返回值加一:var counter = 0;

function add() {

return counter += 1;

}

add();

add();

add();// 计数器现在为 3

问题:全局变量容易被其他代码改变

如果我需要同时用两个计数器,但这种写法只能满足一个使用,另一个还想用的话就要再写个counter2函数,再定义一个counter2的全局变量。

那我们把counter放在add函数里面不就好了么?function add() {

var counter = 0;

return counter += 1;

}

add();

add();

add();// 本意是想输出 3, 但输出的都是 1

所以这样做的话,每次调用add函数,counter的值都要被初始化为0,还是达不到我们的目的。

使用闭包来写就会解决这些问题function add() {

var index = 1;

function counter() {

return index ++;

}

return counter;

}

// test

var addA = add() ;

var addB = add() ;

addA(); // 1

addA(); // 2

addB(); // 1

addB(); // 2

实例2-延时打印

这样打印出来的全部都是10,原因是for循环是同步的会在延时1000毫秒的过程中一直执行

等function执行的时候变量i指向的是同一个内存地址,且值已经变成的10for (var i = 1; i <= 10; i++) {

setTimeout(function () {

console.log(i);

}, 1000);

}

改进,用自执行的函数创建简单的闭包,让每一次for循环的i都在不同的内存地址中且不被销毁for (var i = 1; i <= 10; i++) {

(function () {

var j = i;

setTimeout(function () {

console.log(j);

}, 1000);

})();

}

优化写法for (var i = 1; i <= 10; i++) {

(function (j) {

setTimeout(function () {

console.log(j);

}, 1000);

})(i);

}

联系Static静态变量

闭包的作用主要就是让变量的值始终保持在内存中。

C++或C语言还有Java中都有static静态变量也是让变量始终保存在内存中。

这样来看好像闭包好像有点static静态变量的意思。

总结

闭包就是子函数可以有权访问父函数的变量、父函数的父函数的变量、一直到全局变量。

归根结底,就是利用js得词法(静态)作用域,即作用域链在函数创建的时候就确定了。

子函数如果不被销毁,整条作用域链上的变量仍然保存在内存中,这样就形成了闭包

本文转载自:https://segmentfault.com/a/1190000020006541

更多web前端开发知识,请查阅 HTML中文网 !!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值