You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
function createObject() {
var privateProperty = 'I am private';
return {
getPrivateProperty() {
return privateProperty;
},
};
}
var obj = createObject();
console.log(obj.getPrivateProperty()); // "I am private"
console.log(obj.privateProperty); // undefined
延长变量生命周期 缓存数据
防抖
function debounce(fn, delay) {
let timer;
return function (...arg) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(fn.bind(this, ...arg), delay);
};
}
function test(...args){
console.log('test args', args, this)
return args;
}
const dTest = debounce(test, 200);
dTest.call({name: 'call'}, 1,2,3,4,5);
先有问题再有答案
闭包是什么
有什么特性,怎么用?
闭包有哪些应用场景
内存泄漏与闭包
内存泄漏的本质是什么?
闭包为什么对js这么重要
什么是闭包
wiki定义:
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是在支持
头等函数
的编程语言中实现词法绑定
的一种技术。闭包在实现上是一个结构体,它存储了一个函数(通常是其入口地址)和一个关联的环境(相当于一个符号查找表)。环境里是若干对符号和值的对应关系,它既要包括约束变量(该函数内部绑定的符号),也要包括自由变量
(在函数外部定义但在函数内被引用),有些函数也可能没有自由变量。闭包跟函数最大的不同在于,当捕捉闭包的时候,它的自由变量会在捕捉时被确定,这样即便脱离了捕捉时的上下文,它也能照常运行。简化下:
闭包是由 函数 和 创建该函数的 静态词法作用域 组合而成的。
在这个定义下 一个函数只要有能力访问外部作用域的变量 即形成闭包。
所以任何js函数都可以成为闭包 因为js是
静态词法作用域
。 函数的作用域链在解析阶段就已经确定了。任何函数都具备访问外层数据的能力。mdn定义:
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从
内部函数访问外部函数的作用域
。在 JavaScript 中,闭包会随着函数的创建而被同时创建。在mdn定义下需要满足几个条件:
我们这里以mdn的定义为标准,同时这也是浏览器判断是否为闭包的标准...
闭包判断
test2 访问了外部作用域的变量 但是没有外层函数 所以不是一个闭包
debug调试也可以证明 浏览器并没有识别这个函数是一个闭包。
这个函数比较让人迷惑 大家可能会认为inner是一个闭包函数 仔细看我们会发现 无论是outer还是inner 都没有访问外部作用域的变量 inner函数只是生命周期被延长了 但这并不是构成闭包的条件。
我们通过chrome调试也可以看到,并没有形成闭包。
在inner函数内部访问outer函数作用域中的对象outObj.
可以看到 此时chrome显示有一个闭包 并且被访问的外部变量是outObj.
这个例子满足静态词法作用域 满足内层函数&外层函数 但是内层函数没有访问外层函数数据 而是直接访问了全局的
gObj
对象 这情况chrome会认为是闭包嘛?可以看到浏览器认为这并不形成闭包。
这里outer执行后返回一个新对象 对象内部引用了inner函数。
同样形成了闭包。
这里通过直接赋值给
Global_inner
,看下是否可以形成闭包这里如图 浏览器认为是一个闭包。
关于闭包定义的总结
闭包必须要有内层函数外层函数嘛?
答:根据mdn定义和浏览器的实测表现 必须要有内外函数
闭包必须要返回一个函数嘛?
答: 非必须,只是比较常见的使用方式而已
闭包必须要访问函数中的变量嘛?
答:根据浏览器的表现 必须要访问另一个函数的数据 才能形成闭包。
函数访问全局中的变量会形成闭包嘛?
答:根据浏览器的表现 不会形成闭包 参考case5。
所以通过实践我们再次确认了满足闭包的三个条件是缺一不可的
一句话概括:
在一个函数中访问另一个函数的数据 即形成闭包。
函数的其他形式
从上面的例子中我们可以知道
闭包是函数的一种表现形式
。在js中函数有多种表现形式,例如作为
普通函数
,对象的方法
,构造函数
,立即执行的iife函数
,箭头函数
,作为函数的入参
,作为函数的返回值
,作为一个函数对象
,生成器函数
,闭包函数
。函数对于js非常重要。我们知道js中可以有多种编程范式或者编程风格,例如 面向对象编程,元编程, 异步编程 ,但是和js最为契合的一定是函数式编程, 因为
js中函数是第一公民
如何理解闭包
闭包是函数的一种表现形式 那么他和普通函数在执行上会有什么差异?我们可以通过执行栈和堆内存的引用情况分析下。
普通函数执行机制
执行栈和内存情况:
闭包函数执行机制
区别
普通函数执行完成 会出栈 内部数据会被清空。
闭包函数 外层函数执行完成 内层函数持有外层函数的作用域链 因此外层函数中的数据不会被清空 依然可以被内层函数访问到。只有清空了内层函数的引用 才能释放内存。
应用场景
控制数据访问权限
js实现私有属性的六种方式 文章中我们通过闭包函数,实现了数据的私有化,达到了控制变量访问权限的作用。
延长变量生命周期 缓存数据
缓存计时器字段,每次调用判断run函数 可以获取到外层的timer是否存在 存在清空计时器。可以实现防抖功能。
缓存上一次执行时间。
相信每个刚刚接触react + hooks的同学都有这样的疑问 点击按钮视图正常更新,为什么定时器里获取到的count一直是0。
闭包与内存泄漏
内存泄漏的本质:不被使用的变量没有被及时释放,这通常是因为
变量的生命周期大于其作用域
导致的。闭包可以延迟变量的生命周期,这是闭包的主要用途之一,因此闭包容易引起内存泄漏。
闭包为什么对js这么重要
闭包贯穿了多个核心基础概念。
首先,闭包的本质是函数,捕获了其静态词法作用域,从而形成了作用域链。这使得函数能够跨越其定义环境访问外部变量,是对作用域链和内存引用管理的深刻理解。
其次,闭包揭示了 JavaScript 函数作为一等公民的重要特征。函数不仅可以作为值传递,还能保留其定义时的上下文,这为函数式编程提供了强大支持。
通过闭包,我们能够创建私有变量、实现数据封装和模块化,提升代码的安全性和可维护性。因此,掌握闭包是全面理解和高效使用 JavaScript 的关键。
相关文章
一:函数式编程
js三座大山之函数1
js三座大山之函数-静态词法作用域
js三座大山之函数-运行时this的四种指向
二:面向对象编程
js三座大山之对象,继承,类,原型链
三:异步编程:
js三座大山之异步一单线程,event loope,宏任务&微任务
js三座大山之异步二异步方案
js三座大山之异步三promise本质
js三座大山之异步四-Promise的同步调用消除异步的传染性
js三座大山之异步五基于异步的js性能优化
js三座大山之异步六实现微任务的N种方式
js三座大山之异步七实现宏任务的N种方式
The text was updated successfully, but these errors were encountered: