一个函数被当作值返回时,也就相当于返回了一个通道,这个通道可以访问这个函数词法作用域中的变量,即函数所需要的数据结构保存了下来,数据结构中的值在外层函数执行时创建,外层函数执行完毕时理因销毁,但由于内部函数作为值返回出去,这些值得以保存下来。而且无法直接访问,必须通过返回的函数。这也就是私有性。本来执行过程和词法作用域是封闭的,这种返回的函数就好比是一个虫洞,使得外部可以访问。
闭包的形成很简单,在执行过程完毕后,返回函数,或者将函数得以保留下来,即形成闭包。闭包是引用了另一个函数作用域变量的函数,通常是在嵌套函数中实现的。
function debounce(fn, interval) {
let timer = null; // 定时器
return function() {
// 清除上一次的定时器
clearTimeout(timer);
// 拿到当前的函数作用域
let _this = this;
// 拿到当前函数的参数数组
let args = Array.prototype.slice.call(arguments, 0);
// 开启倒计时定时器
timer = setTimeout(function() {
// 通过apply传递当前函数this,以及参数
fn.apply(_this, args);
// 默认300ms执行
}, interval || 300)
}}
function throttle(fn, interval) {
let timer = null; // 定时器
let firstTime = true; // 判断是否是第一次执行
// 利用闭包
return function() {
// 拿到函数的参数数组
let args = Array.prototype.slice.call(arguments, 0);
// 拿到当前的函数作用域
let _this = this;
// 如果是第一次执行的话,需要立即执行该函数
if(firstTime) {
// 通过apply,绑定当前函数的作用域以及传递参数
fn.apply(_this, args);
// 修改标识为null,释放内存
firstTime = null;
}
// 如果当前有正在等待执行的函数则直接返回
if(timer) return;
// 开启一个倒计时定时器
timer = setTimeout(function() {
// 通过apply,绑定当前函数的作用域以及传递参数
fn.apply(_this, args);
// 清除之前的定时器
timer = null;
// 默认300ms执行一次
}, interval || 300)
}}
var arr =['aa','bb','cc'];
function incre(arr){
var i=0;
return function(){
//这个函数每次被执行都返回数组arr中 i下标对应的元素
return arr[i++] || '数组值已经遍历完';
}
}
var next = incre(arr);
console.log(next());//aa
console.log(next());//bb
console.log(next());//cc
console.log(next());//数组值已经遍历完
var fn=(function(){
var cache={};//缓存对象
var calc=function(arr){//计算函数
var sum=0;
//求和
for(var i=0;i<arr.length;i++){
sum+=arr[i];
}
return sum;
}
return function(){
var args = Array.prototype.slice.call(arguments,0);//arguments转换成数组
var key=args.join(",");//将args用逗号连接成字符串
var result , tSum = cache[key];
if(tSum){//如果缓存有
console.log('从缓存中取:',cache)//打印方便查看
result = tSum;
}else{
//重新计算,并存入缓存同时赋值给result
result = cache[key]=calc(args);
console.log('存入缓存:',cache)//打印方便查看
}
return result;
}
})();
fn(1,2,3,4,5);
fn(1,2,3,4,5);
fn(1,2,3,4,5,6);
fn(1,2,3,4,5,8);
fn(1,2,3,4,5,6);
function fn(){
var name='hello'
setName=function(n){
name = n;
}
getName=function(){
return name;
}
//将setName,getName作为对象的属性返回
return {
setName:setName,
getName:getName
}}
var fn1 = fn();//返回对象,属性setName和getName是两个函数
console.log(fn1.getName());//getter
fn1.setName('world');//setter修改闭包里面的name
console.log(fn1.getName());//getter
function curryingCheck(reg) {
return function(txt) {
return reg.test(txt)
}}
var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)
hasNumber('test1') // true
hasNumber('testtest') // false
hasLetter('21212') // false
var p1 = "ss";
var p2 = "jj";
function testSetTime(para1,para2){
return (function(){
console.log(para1 + "-" + para2);
})}
var test = testSetTime(p1, p2);
setTimeout(test, 1000);
setTimeout(function(){
console.log(p1 + "-" + p2)
},1000)
var Singleton = (function () {
var instance;
function createInstance() {
return new Object("I am the instance");}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();}
return instance;
}};
})();
闭包是一种强大的编程概念,它具有许多优点,但也有一些潜在的缺点。
优点:
保持状态: 闭包能够保持函数内的状态,允许在函数调用之间保留和共享信息,这有助于实现一些需要记住状态的任务,比如计数器、缓存等。
数据封装: 闭包允许将数据封装在私有作用域内,这有助于实现模块化和隐藏实现细节,提高代码的可维护性和安全性。
灵活性: 闭包使得函数可以作为参数传递,这为实现高阶函数、回调函数、装饰器等提供了灵活性,使代码更加模块化和可复用。
实现装饰器: 闭包是实现装饰器的基础,能够方便地为函数添加额外功能,而无需修改原函数代码。
回调函数: 闭包可用作回调函数,用于处理异步编程、事件处理等场景。
缺点:
内存占用: 闭包可能会占用较多的内存,因为它需要保持外部函数的局部变量和内部函数的引用,这可能导致内存泄漏的风险。
性能损耗: 由于闭包保持了外部函数的状态,可能导致额外的性能损耗,尤其是在需要频繁调用闭包的情况下。
复杂性: 过度使用闭包可能会导致代码变得复杂和难以理解,特别是在处理多层嵌套的闭包时。
不当使用可能导致 bug: 如果不小心修改了闭包中的变量,可能导致意外的行为,这种情况下很难追踪 bug。