闭包
常见格式:外部函数内部有1个变量和1个内部函数
function fn() {
let num=10;
return function(){
num++;
console.log(num);
}
}
闭包的意义:外部可以访问num,但是无法修改num
闭包案例:多个点赞按钮可以独立计数
因为重复调用上述的fn,会生成不同的num分别独立存在
<script>
window.onload=function(){
function setNum() {
let num=0;
return function(){
num++;
this.value=`点赞(${num})`;
}
}
let btn=document.querySelectorAll(".btn");
for(let i=0;i<btn.length;i++){
btn[i].onclick=setNum();
}
}
</script>
</head>
<body>
<input type="button" value="点赞(0)" class="btn">
<input type="button" value="点赞(0)" class="btn">
<input type="button" value="点赞(0)" class="btn">
<input type="button" value="点赞(0)" class="btn">
</body>
</html>
柯里化
所谓的柯里化,我认为也是闭包的一个应用,可以实现将多个函数输入值转变为单一的函数输入
window.onload = function () {
function checkReg(reg) {
return function (str) {
return reg.test(str);
};
}
let myCheck=checkReg(/\d{6}/);
console.log(myCheck("666666"));
console.log(myCheck("12345"));
let myCheck2=checkReg(/\w+/);
console.log(myCheck2(""));
console.log(myCheck2("abc"));
console.log(myCheck2("abc-"));//true
};
比如构建正则检验的函数,对于同一条检验规则,这样写似乎能省点力
同理,网上流传的一个面试题也能这样解决
window.onload = function () {
function f(a) {
return function (b) {
return function (c) {
return function (d) {
return a+b+c+d;
};
};
};
}
console.log(f(1));
console.log(f(1)(2));
console.log(f(1)(2)(3));
console.log(f(1)(2)(3)(4));
};
防抖和节流
对于这两个我的理解是
防抖:用户点击后,1s后执行操作,期间如果再次点击,计时重置,以最后一次点击为准,防止多次点击造成多次执行。
节流:无论用户点击速度多快,我1s只会执行1次
<script>
//防抖
window.onload=function(){
//f1是我们点击按钮需要执行的程序
//我们期望f1的this指向是事件源btn
const f1=function(){
console.log(this);
console.log("防抖函数!");
}
const debounce=function(fn,delay){
//闭包的一个经典写法
//共用一个公共变量,比如timer
//然后返回一个函数
let timer=null;
return function(){
//设定定时器之前先清除,没啥好说的
clearTimeout(timer);
timer=setTimeout(() => {
fn.call(this);
console.log(this);
}, delay);
}
}
//最终目的:给btn绑定事件函数,
//要求函数在100毫秒之后执行f1
//如果在执行前多次点击btn
//则计时会被重置,以最后一次点击为准
btn.onclick=debounce(f1,1000)
function f2() {
let scrollTopValue=document.documentElement.scrollTop || document.body.scrollTop;
console.log(scrollTopValue);
console.log(window.scrollY || window.pageYOffset);
}
window.onscroll=debounce(f2,1000);
}
</script>
<script>
//节流
window.onload = function () {
//f1是我们点击按钮需要执行的程序
//我们期望f1的this指向是事件源btn
const f1 = function () {
console.log(this);
console.log("防抖函数!");
};
const throttle = function (fn, delay) {
//闭包的一个经典写法
//共用一个公共变量,比如timer
//然后返回一个函数
let timer = null;
return function () {
if (!timer) {
timer = setTimeout(() => {
//让fn的this指向事件源
fn.call(this);
console.log(this);
timer = null;
}, delay);
}
};
};
btn.onclick = throttle(f1, 1000);
function f2() {
let scrollTopValue =
document.documentElement.scrollTop || document.body.scrollTop;
console.log(scrollTopValue);
console.log(window.scrollY || window.pageYOffset);
}
window.onscroll = throttle(f2, 1000);
};
</script>
上述的f1实现了点击事件的防抖和节流,而f2实现了窗口滚动
最后,关于闭包应用GPT给出了解释
闭包是JavaScript中非常重要的概念,它指的是可以访问自由变量的函数。由于JavaScript中的函数嵌套和作用域嵌套的特性,可以很方便地实现闭包,进而实现一些特定的功能,比如隐藏变量、模块化等。
以下是JS闭包的一些常见应用场景及代码演示:
1. 隐藏数据
闭包可以用来隐藏数据,只暴露出不需要隐藏的接口,可以有效地保护数据的安全性。例如:
function counter() {
let count = 0;
return {
increment: function() {
count++;
console.log(count);
},
reset: function() {
count = 0;
console.log(count);
}
}
}
const counter1 = counter();
counter1.increment() // 输出1
counter1.increment() // 输出2
counter1.reset() // 输出0
在这个例子中,我们通过闭包实现了一个计数器函数,该函数中定义了一个局部变量count,并返回了一个包含increment和reset方法的对象。这两个方法访问了count变量,但是由于该变量只在函数内被定义,因此不会被外界访问到,从而保证了数据的安全性。
2. 延迟执行
通过闭包延迟执行函数,可以在特定的时间或者条件下再执行某些函数。例如:
function debounce(fn, delay) {
let timer = null;
return function() {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(function() {
fn.apply(context, args);
}, delay);
}
}
function doSomething() {
console.log('Do something');
}
const debouncedFn = debounce(doSomething, 1000);
debouncedFn(); // 在1s后输出'Do something'
在这个例子中,我们通过闭包实现了一个防抖函数debounce,该函数返回一个新的函数,该函数在一定的时间后执行原来的函数。闭包中定义了一个局部变量timer,用来标记延迟的时间,并且在原函数执行前清除之前的计时器。
3. 缓存结果
闭包可以用来缓存一些计算结果,避免重复计算带来的性能开销。例如:
function memoize(fn) {
const cache = {};
return function() {
const argString = JSON.stringify(arguments);
if (cache[argString]) {
return cache[argString];
}
const result = fn.apply(this, arguments);
cache[argString] = result;
return result;
}
}
function fibonacci(n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
const memoizedFibonacci = memoize(fibonacci);
console.log(memoizedFibonacci(10)); // 输出55,将会缓存计算过的值
console.log(memoizedFibonacci(10)); // 输出55,直接返回缓存中的值
在这个例子中,我们通过闭包实现了一个缓存函数memoize,该函数接受一个函数作为参数,返回一个新的函数。新函数的作用是执行原函数并缓存结果,如果已经缓存了该参数的结果,就直接返回缓存中的值,否则就执行原函数,计算结果并缓存结果。由于缓存了结果,这样可以避免在重复调用时导致的重复计算带来的性能开销。