R001-防抖,节流,闭包

在前端开发过程中,在处理用户交互时, 最常用的操作就是加上防抖和节流.
作用: 
     一是为了防止用户频繁操作;
     二是为了节约一定的服务器资源,减少资源浪费的情况。

一.防抖

指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。也就是说,短时间内不重复触发一个事件
//  防抖函数debounce   利用闭包的原理
//    参数 delay 延迟, 默认1s
const debounce = function (fn, delay = 1000) {
  // 1. 声明一个定时器变量
  let timer = null
  // 将debounce处理结果当做函数返回
  // 4. 使用闭包  函数调用函数之外的一个参数     ...args可变数组   args数组
  return function (...args) {
    // 5. 保存当前对象的this
    const that = this
    // 2. 在触发请求时, 根据timer进行判断
    // 当timer不为空, 需要清除定时器
    if (timer) {
      //清除定时器
      clearTimeout(timer)
    }
    // 3. (清除后)重新计时
    // timer = setTimeout(fn, delay)
    timer = setTimeout(function(){
      // 改变this指向, 改变当前对象, 一直传下去
      fn.apply(that, args)   
    },delay)
  }
}

二.节流

点击事件,在一段事件内连续点击,指定时间内只触发一次
//  节流函数throttle   利用闭包的原理
//    参数 delay 延迟, 默认1s
const throttle = function (fn, delay = 1000) {
  // 1. 声明一个定时器变量
  let timer = null

  // 将throttle处理结果当做函数返回
  //触发事件回调时执行这个返回函数
  return function(){
    const that  = this
    // 2. 存在timer, 表示时间还没到
    if(timer){
      return
    }
    // 开始设定一个新的定时器, 定时器执行结束后执行传入的函数 fn
    timer = setTimeout(()=> {
      timer = null
    },delay)
    // 相当于执行函数
    fn.apply(that, args)
  } 
}

三.闭包

1.什么是闭包(Closure)

闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。(简单理解,就是函数套函数)

2.闭包的特点

  • 让外部访问函数内部变量成为可能

  • 局部变量会常驻在内存中

  • 可以避免使用全局变量,防止全局变量污染

3.闭包的好处与坏处

  • 好处:可以读取其他函数内部的变量,并将其一直保存在内存中

  • 坏处:可能会造成内存泄漏或溢出

// 闭包  本质上,闭包是将函数内部和函数外部连接起来的
function fun1() {
  var num1 = 5
  console.log(num1)
}
// console.log('fun1外部调用num1',num1);   //无法访问

// 闭包(函数内部的函数)     封装到小的作用域中去
//  可解决函数体外无法访问函数体内定义的局部变量的问题, 则返回变量写在闭包中外部即可访问
function fun2() {
  var num2 = 10
  // 返回函数, 即可使用局部作用域中的变量
  return function () {
    return num2
  }
}
//  调用fun2() 返回的是一个函数. 故需要再调用一下   变量的生命周期相当于没有结束
//写法1
console.log(fun2()())
//写法2
const fn = fun2() //fn是个函数
console.log(fn())

// 通过定时器也可以实现闭包 (立即执行函数)

/* 
闭包特点
    让外部访问函数内部的变量
    局部变量会常驻与内存之中
    避免使用全局变量, 防止全局变量污染
闭包优缺点
好处: 可读取其他函数内部的变量, 并将其一直保存在内存中
坏处: 可能造成内存泄漏或溢出
 */
function add(x) {
  return function (y) {
    return x + y
  }
}

// 闭包,共享相同的函数定义, 但值互不影响, 函数作用域不同
let sum1 = add(2) // 第一个函数作用域中x=2常驻内存(一直保存在内存中),并且一致保存在内存中, 返回函数,故sum1是函数 function(y){}
let sum2 = add(10)
console.log(sum1(10)) //12    // 调用sum1,故y=12, 返回2+10
console.log(sum2(20)) //30
// 释放内存, 防止内存泄漏或污染
sum1 = null
sum2 = null

// 立即执行函数   也是一个闭包
;(function (x, y) {
  return console.log(x - y) // 8
})(20, 12)

// 打印 10个10
var arr1 = []
for (var i = 0; i < 10; i++) {
  // 数组中的每个元素都是函数
  arr1[i] = function () {
    return i
  }
}
for (var j = 0; j < 10; j++) {
  console.log(arr1[j]()) //打印10个10   立即执行
}

// 打印 0-9   闭包
//方式1    闭包使用立即执行函数包裹, 将i传给函数   闭包只能取得包含函数中任何变量的最后一个值
var arr2 = []
for (var i = 0; i < 10; i++) {
  // 数组中的每个元素都是函数  闭包使用立即执行函数包裹, 将i传给函数
  arr2[i] = (function (num) {
    return function () {
      return num
    }
  })(i)
}
for (var j = 0; j < 10; j++) {
  console.log('rrrr===>', arr2[j]) //打印10个10   立即执行
}

// 方式2  立即执行函数
var arr3 = []
for (var i = 0; i < 10; i++) {
  // 数组中的每个元素都是函数
  arr3[i] = (function () {
    return i
  })()
}
for (var j = 0; j < 10; j++) {
  console.log(arr3[j]) //打印10个10   立即执行
}
代码说明:
创建闭包 最常见方式,就是在一个函数内部创建另一个函数.
闭包的作用域链 包含着它自己的作用域,以及包含它的函数的作用域和全局作用域.
闭包的注意事项: 通常,函数的作用域及其所有变量都会在函数执行结束后被销毁。但是,在创建了一个闭包以后,这个函数的作用域就会一直保存到闭包不存在为止.
闭包只能取得包含函数中任何变量的最后一个值.
释放内存 e.g: num=null

4.闭包中的this

// 闭包中的this
const obj = {
  name: '内---mm',
  getName: function () {
    return this.name
  },
}
// 普通函数 this指向 函数调用者
console.log('getName===>', obj.getName()) // 内---mm
const getNameFun = obj.getName
// this指向window  getNameFun 是一个函数  调用时,没有其他调用者,那么调用者即为Window对象, const声明的变量在window上没有,故为undefined,  若为var声明则为var声明的变量值
console.log('getNameFun==>', getNameFun()) //undefined

const obj1 = {
  name: '内1111---mm',
  getName: function () {
    return function () {
      return this.name
    }
  },
}
// this指向window   以下两种意思调用实则一样
// obj1.getName获得一个函数function(){ return function(){ return this.name }, ()执行一次,又得到一个函数function(){return this.name},再()执行返回this.name, 此时没有其他调用者调用
console.log('getName1===>', obj1.getName()()) //undefined
const getNameFun1 = obj1.getName()
console.log('getNameFun1==>', getNameFun1()) //undefined

const obj2 = {
  name: '内1111---mm',
  getName: function () {
     //保存环境,保存this, 保存当前调用者   that常驻内存
    const that = this 
    // 闭包    函数套函数, 内部函数可访问函数体外的变量
    return function () {
      return that.name
    }
  },
}
// 利用that保存this, 使得this指向obj函数调用者, 故将结果均为obj.name的值 
console.log('getName2===>', obj2.getName()()) //内1111---mm
const getNameFun2 = obj2.getName()
console.log('getNameFun2==>', getNameFun2()) //内1111---mm

5.闭包的应用

应用闭包的主要场合是:设计私有的方法和变量

  • 私有变量包括:函数的参数, 局部变量, 函数内部定义的其他函数(闭包)

  • 特权方法(privileged method): 有权访问私有变量和私有函数的公有方法

严格来说,JavaScript没有私有成员的概念,所有对象属性都是公共的。不过JavaScript有私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数外面访问这些变量。
// 私有变量 n1,n2,sum  普通函数,在外部无法访问函数内部私有变量
function add1(n1,n2){
    const sum1 = n1 + n2
    return sum1
}
const res = add1(3,4)
// console.log(n1,n2,sum1)  无法访问函数内部私有变量
console.log(res);   // 7
//通过特权方法, 向外提供函数内部私有变量, 也可修改
function add2(n3,n4){
    var n = 100 
    // 特权方法   通过公有方法,向外提供函数内部私有变量, 也可修改
    this.getN = function(){
        return n
    }
    this.setN = function(val){
        n = val
    }
    const sum2 = n3 + n4
    return sum2
}
// 通过new关键字,构造函数创建对象
let number = new add2()
console.log(number.getN()); // 100
// 通过特权方法, 修改函数内部私有变量
number.setN(1000000)
console.log(number.getN());  // 1000000

// 立即执行函数, !只是为了与其他分隔开
!(function(){
 var uname = ''   //静态变量
//  使用node运行js文件, 全局对象是global, 如果加var声明Person,反而会报错
 Person = function(val){
    uname = val
 }
 Person.prototype.getName = function(){
    return uname
 }
 Person.prototype.setName = function(val){
    uname = val
 }
})()
console.log('PerSon===>',Person); //PerSon===> [Function: Person]
//通过特权方法访问函数内部的变量
const per1 = new Person('mmmm')
console.log(per1.getName());   // mmmm
const per2 = new Person('tttt') 
console.log(per2.getName());   // tttt
// 两个对象共享一个方法    当赋值时, 会修改函数中静态属性
console.log(per1.getName === per2.getName);  // true
const per3 = new Person('11')
const per4 = new Person('22')
console.log(per3.getName());  // 22
console.log(per4.getName());  // 22

猜你喜欢

转载自blog.csdn.net/qq_54379580/article/details/129540603