{
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))