Javascript闭包Closure

javascipt变量作用域分为两种:局部作用域(函数作用域),全局作用域。
一、变量的作用域
函数内部可以读取全局变量

点击查看代码
var n = 100 //全局变量;
function fn(){
  console.log(n)
} 
fn() // 100

全局作用域无法读取局部作用域中的局部变量

点击查看代码
function fn(){
  var n = 100;  //一定要带上var申明,否则是全局变量
}
console.log(n);//错误
二、如何获取局部作用域中的局部变量和方法
点击查看代码
function fn1(){
  var n =100;
  function fn2(){
    console.log(n);//100
  }
}
函数fn2在fn1内部,fn1所有的局部变量对fn2都是可见的。因为JavaScript的“链式作用域”,当前作用域中变量找不到会向其父作用域中一层一层寻找。 当将fn2作为fn1的返回值return出来的时候,就可以在fn1的外部访问fn1的变量

三、闭包
有权访问另一个局部作用域(函数作用域)中的变量 的函数。简单来说就是函数在执行过程中,返回了另一个函数或引用类型。使得可以在函数外部访问函数内部变量。

四、闭包的必要条件
1.函数调用时返回了另一个函数或引用类型
2.返回的函数或引用类型中,使用了父作用域中的变量或方法
3.函数外有变量或其他数据类型引用了返回值

五、闭包的优缺点
优点:1.保存关联的AO对象,即父作用域
2.允许函数外部访问函数内部变量
3.可以有效减少全局变量的使用
缺点:1.父作用域关联的AO对象会一直存储在内存中得不到释放,易导致内存泄漏。
2.由于函数外部可以访问函数内部变量,数据不安全

六、闭包的作用
1.循环绑定点击事件。绑定点击事件是异步操作。for循环是同步操作,当页面加载成功就会直接进入循环,等到点击时,i的值已经变为selector.length了。使用闭包来保存关联的AO对象,即每一个循环的父作用域

点击查看代码
for(var i = 0;i < selector.length;i++){
  (function (i){
      selector[i].onclick = function(){
          console.log(lis[i].textContent);
      }
  })(i)
2.函数防抖,如果函数高频率触发,只触发一次(最后一次) 如:点击上传,多次点击会进行多次上传操作
点击查看代码
let btn = document.querySelector("button");
    btn.onclick = function () {
      console.log("上传");
  }
解决办法: a.设置设置延时器,点击前先清除上一个计时器,开启下一个计时器,多次点击只会在最后一个起作用
点击查看代码
let timer = null;
btn.onclick = function () {
  clearTimeout(timer);
  timer = setTimeout(function () {
    console.log("上传");
  }, 300)
}
b.使用闭包
点击查看代码
btn.onclick = function () {`
  let timer = null;
  return function () {
    clearTimeout(timer);
    timer = setTimeout(function () {
      console.log("上传");
     }, 300)
    }
}()
闭包封装
function debounceFn(callback, delay) {
  let timer = null;
  return function (...list) {
    clearTimeout(timer);
    let that = this;
    timer = setTimeout(function () {
      callback.apply(that, list)
      }, delay);
   }
}
btn.onclick = debounceFn(function (e) {
  console.log("上传");
}, 300);

3.函数节流 如果函数高频率触发,降低触发的频率
例如:滚动条滚动事件。
window.onscroll = function () {
console.log("滚动了"); //一滚动便触发
}
解决办法:

点击查看代码
a.
        let start = Date.Now(); // 页面加载成功的时间
        window.onscroll = function () {
            let Now = Date.Now(); // 开始滚动时间
            if (Now - start >= 300) {
                console.log("正在滚动");
                start = Now;
            }
        }
        b.闭包
        window.onscroll = (function () {
            let start = Date.Now();
            return function () {
                let Now = Date.Now();
                if (Now - start >= 300) {
                    console.log("正在滚动");
                    start = Now;
                }
            }
        })()
        // 闭包封装
        function throttle(callback, delay) {
            let start = Date.Now();
            return function (...list) {
                let Now = Date.Now();
                if (Now - start >= delay) {
                    callback.apply(this, list);
                    start = Now;
                }
            }
        }
        window.onscroll = throttle(function (e) {
            console.log("滚动了")
        }, 300)

相关文章

前言 做过web项目开发的人对layer弹层组件肯定不陌生,作为l...
前言 前端表单校验是过滤无效数据、假数据、有毒数据的第一步...
前言 图片上传是web项目常见的需求,我基于之前的博客的代码...
前言 导出Excel文件这个功能,通常都是在后端实现返回前端一...
前言 众所周知,js是单线程的,从上往下,从左往右依次执行,...
前言 项目开发中,我们可能会碰到这样的需求:select标签,禁...