存取器技术

存取器技术现在是热点。因为需要 ES5 getter/ setter 功能而未受广泛支持,但是它因语法优雅而弥补了这个弱点。

语法

定义提供者需要特殊的语法。简单的提供者对象必须被传入 observable 函数,这个函数可以用来把提供者对象转化为一个被监听的对象。

  1. const provider = observable({
  2. greeting: 'Hello',
  3. subject: 'World'
  4. })
  5. const consumer = document.createElement('p')

简单的 handler() 映射语法弥补了这种小的不便。使用脏检查,我们不得不如下显式地定义每个被监听的属性。

  1. observe(provider, 'greeting', greeting => {
  2. consumer.innerHTML = greeting + ' ' + provider.subject
  3. })
  4. observe(provider, 'subject', subject => {
  5. consumer.innerHTML = provider.greeting + ' ' + subject
  6. })

这显得很啰嗦和笨拙。访问器技术可以自动检测在 handler() 函数中使用过的提供者属性,这样允许我们简化以上代码。

  1. observe(() => {
  2. consumer.innerHTML = provider.greeting + ' ' + provider.subject
  3. })

observe() 的实现和脏检查的不同。它只是执行了传入的 handler() 函数,并且当handler()运行的时候把函数标识为目前激活状态。

  1. let activeHandler
  2. function observe(handler) {
  3. activeHandler = handler
  4. handler()
  5. activeHandler = undefined
  6. }

注意,我们现在利用了 JavaScript 的单线程特性,使用唯一的 activeHandler 变量来记录目前运行的 handler() 的函数。

监听变化

这就是存取器技术名字的由来。提供者使用 getters/setters 来扩展,这两个方法在后台进行复杂的工作。思路是以如下方式拦截提供者属性的存取操作。

  • get: 如果有一个 activeHandler 在运行,保存 (provider, property) -> activeHandler 映射以备后用。
  • set: 运行所有由 (provide, property) 映射的 handler() 函数。

存取器技术 - 图1

以下代码展示了一个提供者的属性变化的简单的实现过程。

  1. function observableProp(provider, prop) {
  2. const value = provider[prop]
  3. Object.defineProperty(provider, prop, {
  4. get () {
  5. if (activeHandler) {
  6. provider._handlers[prop] = activeHandler
  7. }
  8. return value
  9. },
  10. set (newValue) {
  11. value = newValue
  12. const handler = obj._handlers[prop]
  13. if (handler) {
  14. activeHandler = handler
  15. handler()
  16. activeHandler = undefined
  17. }
  18. }
  19. })
  20. }

在前面一节提到的 observable() 函数递归遍历提供者的属性,并且使用 observableProp() 函数来转化所有属性为可监测。

  1. function observable (provider) {
  2. for (let prop in provider) {
  3. observableProp(provider, prop)
  4. if (typeof provider[prop] === 'object') {
  5. observable(provider[prop])
  6. }
  7. }
  8. }

这是一个非常简单的实现,但是对于比较这两种技术已经足够了。