惰性函数

2.8k words

惰性函数是一种函数式编程技巧,它通过在首次调用时动态重写自身,避免后续调用中的重复条件判断,从而提高执行效率。这种技术特别适用于需要环境检测或初始化配置的场景,如浏览器特性检测、API兼容性处理等。

工作原理

  1. 首次调用时执行判断:函数首次运行时进行必要的条件检测
  2. 函数重写:根据检测结果重写函数自身
  3. 返回适当的实现:返回符合当前环境的实现
  4. 后续调用直接使用优化版本:避免重复判断,提高性能

适用场景

  • 浏览器特性检测和兼容性处理
  • 只需执行一次的初始化操作
  • API适配器模式实现
  • 配置参数的一次性处理

🌰 示例

初始版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function createXHR() {
var xhr = null

// 如果是现代浏览器
if (typeof window['XMLHttpRequest'] !== undefined) {
xhr = new XMLHttpRequest()

// 重写函数本身,后续调用直接使用此分支
createXHR = function () {
return new XMLHttpRequest()
}
}
// 如果是 IE6, IE5
else {
xhr = new ActiveXObject('Microsoft.XMLHTTP')

// 重写函数本身,后续调用直接使用此分支
createXHR = function () {
return new ActiveXObject('Microsoft.XMLHTTP')
}
}

return xhr
}

优化后版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 惰性函数 - 优化版本
function createXHR() {
let xhr

// 根据环境一次性重写函数
if (typeof XMLHttpRequest !== 'undefined') {
createXHR = function () {
return new XMLHttpRequest()
}
} else if (typeof ActiveXObject !== 'undefined') {
createXHR = function () {
return new ActiveXObject('Microsoft.XMLHTTP')
}
} else {
createXHR = function () {
throw new Error('当前浏览器不支持XMLHttpRequest')
}
}

// 立即调用重写后的函数
return createXHR()
}

🌰 拓展应用

1. 事件绑定兼容性处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function addEvent(element, type, handler) {
if (element.addEventListener) {
addEvent = function (element, type, handler) {
element.addEventListener(type, handler, false)
}
} else if (element.attachEvent) {
addEvent = function (element, type, handler) {
element.attachEvent('on' + type, handler)
}
} else {
addEvent = function (element, type, handler) {
element['on' + type] = handler
}
}

return addEvent(element, type, handler)
}

2. 缓存计算结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function fibonacci(n) {
const cache = {}

function calculateFib(num) {
if (cache[num]) return cache[num]

if (num <= 1) return num

const result = calculateFib(num - 1) + calculateFib(num - 2)
cache[num] = result
return result
}

return calculateFib(n)
}

3. 配置参数处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function createAPI(config) {
// 首次调用时处理配置
const baseURL = config.baseURL || ''
const timeout = config.timeout || 5000
const headers = config.headers || {}

// 重写函数,后续调用不再需要处理配置
createAPI = function (endpoint, options) {
// 使用已处理的配置
return fetch(`${baseURL}${endpoint}`, {
timeout,
headers: { ...headers, ...(options?.headers || {}) },
...options,
})
}

// 返回重写后的函数
return createAPI
}

// 使用方式
const api = createAPI({
baseURL: 'https://api.example.com',
timeout: 3000,
headers: { 'Content-Type': 'application/json' },
})

// 后续调用不再需要处理配置
api('/users', { method: 'GET' })

性能考量

  • 优点:避免重复条件判断,提高执行效率
  • 缺点:首次调用性能略低,需要额外的函数重写操作
  • 适用场景:调用频率高、判断条件复杂的场景

最佳实践

  1. 只在真正需要惰性处理的场景使用
  2. 确保重写后的函数逻辑与原函数一致
  3. 考虑添加错误处理机制
  4. 在需要多次调用的场景下使用,单次调用场景可能不值得
Comments