优秀的编程知识分享平台

网站首页 > 技术文章 正文

JavaScript基础之闭包(javascript中的闭包)

nanyue 2024-08-22 17:38:47 技术文章 6 ℃
{
  var x = 100
  function fn (y) {
    var x = 200
    return function(y) {
      console.log(y + x++)
    }
  }
  var f = fn()
  f(10)
  f(20)
}

{
  let x = 5
  function fn(x) {
    return function(y) {
      console.log(y + (++x))
    }
  }
  let f = fn(6)
  f(7)
  // f(7)
  fn(18)(9)
  f(10)
  console.log(x)
}

{
  let a = 0, b = 0
  function A(a) {
    A = function(b) {
      console.log(a + b++)
    }
    console.log(a++)
  }
  A(1)
  A(2)
}

// 简述你对闭包的理解,以及其优缺点?
// + 基本介绍:ECStack、EC、VO、AO、SCOPE、SCOPE-CHAIN、GC
// + 优缺点:保存和保护、性能消耗(内存泄露)
// + 实战应用:
//  - 项目实战应用:循环事件绑定(突出:事件委托)、let和var
//  - 插件组件封装:JS高级编程技巧(单例设计模式、惰性函数、科里化函数、compose组合函数)
//  - 源码阅读应用:lodash源码(函数的防抖和节流)、JQ源码、redux、react-redux(高阶组件)
//  - ...
// + 自己的思想和理解(一句话概括)

// 闭包的概念:在一个函数执行完后,它内部的变量会被释放,但是如果其他的函数有引用它内部的变量,就形成了闭包
// 闭包的优点:
//    1. 读取另一个函数作用域中的变量。
//    2. 保存数据,因为闭包的数据不会被释放,所以可以保存数据
//    3. 封装对象的私有属性和私有方法(对外提供访问数据的接口)
// 闭包的缺点:过度使用闭包可能会导致内存占用过多的问题,在IE浏览器中可能会导致内存泄露
// 产生闭包的场景:
//    1. 立即执行函数(IIFE)
//    2. 返回一个函数
//    3. 定时器、事件监听、AJAX请求等的回调
//    4. 作为函数参数传递
// 闭包的应用:早期的模块化(IIFE)、防抖节流(返回一个函数)、科里化函数(返回一个函数)、compose组合函数(返回一个函数)、循环事件绑定

## 应用

/**
 * JS高阶编程技巧:利用闭包的机制,实现出来的一些高阶编程技巧
 *  + 模块化思想
 *  + 惰性函数
 *  + 科里化函数
 *    1. 高阶组件 -> React
 *    2. 防抖、节流
 *    3. bind
 *    4. ...
 *  + compose组合函数
 *  + ...
 */

// 模块化思想 单例 -> AMD(require.js) -> CMD(sea.js) -> CommonJS(Node) -> ESModule

// 对象的特点:每一个对象都是一个单独的堆内存空间(单独的实例 -> Object),这样即使多个对象中的成员名字相同,也互不影响
// 每一个对象都是一个单独的实例,用来管理自己的私有信息,即使名字相同,也互不影响,其实这就是 “JS中的单例设计模式”

// ------------------- 高级单例模式:闭包 + 单例的结合,样式最早期的JS模块化思想
/* var moduleA = (function () {
  var time = Date.now()
  function queryData() {}
  return {
    time,
    queryData
  }
})() */

/* function getCss (el, attr) {
  if (window.getComputedStyle) {
    return window.getComputedStyle(el)[attr]
  }
  return el.currentStyle(attr)
} */

// ------ 优化思想:第一次执行 getCss 方法时,已经知道是否兼容了,再次执行的 getCss 方法,不想再处理判断兼容的逻辑了,这种思想就是“惰性思想”
/* function getCss (el, attr) {
  if (window.getComputedStyle) {
    getCss = function getCss (el, attr) {
      return window.getComputedStyle(el)[attr]
    }
  } else {
    getCss = function getCss (el, attr) {
      return el.currentStyle(attr)
    }
  }
  return getCss(el, attr)
}
const body = document.body
console.log(getCss(body, 'height'))
console.log(getCss(body, 'width'))
console.log(getCss(body, 'margin')) */

// 函数科里化:
/* function curring (x) {
  return function (...args) {
    return args.reduce((prev, next) => prev + next, x)
  }
}

console.log(curring(10)(20))
console.log(curring(10)(20, 30)) */

// compose组合函数
/* const add = x => x + 1
const multi = x => x * 3
const divide = x => x / 2
function compose (...funcs) {
  return function (x) {
    if (funcs.length === 0) return x
    return funcs.reduceRight((prev, next) => {
      if (typeof next !== 'function') return prev
      return next(prev)
    }, x)
  }
}
const operate = compose(divide, multi, add)
console.log(operate(0))
 */

function add (...params) {
  const proxy = (...args) => {
    params = params.concat(args)
    return proxy
  }
  proxy.toString = () => params.reduce((prev, next) => prev + next)
  return proxy
}

console.log(add(1)(2)(3))
console.log(add(1, 2, 3)(4))
console.log(add(1)(2)(3)(4, 5))

### 防抖节流

function debounce (fn, delay, immediate) {
  let timer = null
  // return function (...args) {
  //   timer && clearTimeout(timer)
  //   timer = setTimeout(() => {
  //     fn(...args)
  //   }, delay)
  // }
  return function (...args) {
    let context = this
    if (immediate) {
      fn.call(context, ...args)
      immediate = false
    }
    timer && clearTimeout(timer)
    timer = setTimeout(function () {
      fn.call(context, ...args)
    }, delay)
  }
}

function throttle (fn, delay, immediate) {
  let timer = null
  return function (...args) {
    let context = this
    if (timer) return
    timer = setTimeout(function () {
      fn.call(context, ...args)
      timer = null
    }, delay)
  }
}

function fn (e) {
  console.log('resize', e)
}

// window.addEventListener('resize', debounce(fn, 1000))
window.addEventListener('resize', throttle(fn, 1000))
最近发表
标签列表