JS函数高级用法:函数唯一标识和自记忆函数

767 阅读3分钟

「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」。

在介绍 JS 里的函数唯一标识和自记忆函数之前,需要明确一个概念,函数可以像对象一样,动态创建和分配属性

函数也是对象

JS 里,一切皆是对象,函数也不例外,废话不多说,直接测试一下,

image.png

对象拥有的能力,函数都有

函数也是对象,对象能做的任何一件事,函数都能做。唯一的区别是函数是可调用的(invokable),即函数会被调用以便执行某项动作。

对象

  • 对象可以通过字面量来创建。
const obj = {}
  • 对象可以赋值给变量、数组项,或其他对象的属性。
const obj = {}    // 赋值给变量

const arr = []
arr.push(obj)     // 赋值给数组项

obj.data = {}     // 赋值给其他对象的属性
  • 对象可以作为函数的入参和出参。
function fn (obj) {
  return {
    obj
  }
}

fn({ name: 'lin'})
  • 对象可以动态创建和分配属性。
const obj = {}
obj.name = 'lin'

函数

  • 函数可以通过字面量来创建。
function fn () {}
  • 函数可以赋值给变量、数组项,或其他对象的属性。
const fn = function () {}   // 赋值给变量

const arr = []
arr.push(fn)     // 赋值给数组项

const obj = {}
obj.fn = fn     // 赋值给其他对象的属性
  • 函数可以作为函数的入参和出参。
function fn (callback) {
  return callback()
}

fn(() => {
  console.log('hello')
})
  • 函数可以动态创建和分配属性。
function fn () {}

fn.name = 'lin'
fn.age = 18

函数作为对象的一些用途

函数唯一标识

给函数设置一个 id 属性,作为唯一标识,在某些情况下可以提高性能,比如,

一个集合中的函数,不希望出现重复函数

const store = {
  nextId: 1,
  cache: {},
  add (fn) {
    if (!fn.id) {
      fn.id = this.nextId++
      this.cache[fn.id] = fn
    }
  }
}

测试一下,

function fn1 () {}
function fn2 () {}

store.add(fn1)  
store.add(fn1)       // add了两个相同的函数
store.add(fn2)
console.log(store.cache)

添加了两次 fn1 ,最后存储下来的只有一个,如下图,

image.png

这种写法可以用于管理事件发生后需要调用的回调函数集合,已经存在于集合中的函数就不要再存进去了,提高性能。

自记忆函数

通过给函数添加属性来记住一些计算结果,之后再调用的时候就可以不用重新计算了,提高性能,以计算素数为例,

function isPrime (value) {
  if (!isPrime.cache) {
    isPrime.cache = {}   // 给函数添加一个 cache 来记住计算结果
  }
  if (isPrime.cache[value] !== undefined) {
    console.log(`缓存里有${value},直接返回`)
    return isPrime.cache[value]  // 如果有值就直接返回
  }
  let prime = value > 2    // 计算
  for (let i = 2; i < value; i++) {
    if (value % i === 0) {
      prime = false
      break
    }
  }
  console.log(`缓存里没有${value},存进去`)
  return isPrime.cache[value] = prime  // 如果是第一次计算,就存值
}

测试一下,

isPrime(3)
isPrime(4)
isPrime(5)
isPrime(5)
isPrime(5)    // 多次计算 5

console.log('isPrime.cache :>> ', isPrime.cache)

image.png

计算素数只是简单的计算,但如果用于处理复杂的计算,比如动画中的计算、搜索不经常变化的数据、耗时的数学运算等,对性能的提升无疑是巨大的。

小结

给函数添加属性的写法有利有弊:

  • 好处是能把属于这个函数的功能集中在一起;
  • 坏处是纯粹主义者会认为逻辑混合太多,函数应该只需要把一件事做好 仁者见仁,智者见智,纯粹的函数式编程有人做大量实践,给函数添加属性也有人做大量实践,比如 React 源码中的 FunctionComponent,也在函数上定义了一大堆属性呢。

image.png

对于我们普通开发者而言,平时开发中能用就用,即使用不了,了解一下,开拓视野也挺好的。

如果我的文章对你有帮助,你的赞👍就是对我的最大支持^_^