Mini Data Observer
by harps116
HTML
<!-- logging outside the console -->
<pre><script>
function log(arg){
document.writeln(arg);
document.write('<br/>')
}
</script></pre>
JavaScript
// This is a greatly simplified version of the dependency tracking
// system used in Vue, Knockout, MobX and Meteor Tracker (each with different
// implementation details, of course). It doesn't cover all the possible edge
// cases in detection, nor does it handle Arrays; The goal is simply to better
// understand how the tracking takes place and how data becomes "reactive".
class Dep {
constructor () {
this.subscribers = new Set()
}
depend () {
if (activeUpdate) {
this.subscribers.add(activeUpdate)
}
}
notify () {
this.subscribers.forEach(sub => sub())
}
}
function observe (obj) {
// iterate through all properties on the object
// and convert them into getter/setters with
// Object.defineProperty()
Object.keys(obj).forEach(key => {
let internalValue = obj[key]
// each property gets a dependency instance
const dep = new Dep()
Object.defineProperty(obj, key, {
// The getter is responsible for registering subscribers
get () {
dep.depend()
return internalValue
},
// The setter is responsible for notifying change
set (newVal) {
const changed = internalValue !== newVal
internalValue = newVal
// triggering re-computation
if (changed) {
dep.notify()
}
}
})
})
return obj
}
let activeUpdate = null
function autorun (update) {
// wrap the raw update function into a "job" function that registers and
// unregisters itself as the current active job when invoked
const wrappedUpdate = () => {
activeUpdate = wrappedUpdate
update()
activeUpdate = null
}
wrappedUpdate()
}
const state = {
count: 0
}
observe(state)
autorun(() => {
log(state.count) // notice that log is called everytime the state.count changes
})
// should immediately log "count is: 0"
state.count++
// should log "count is: 1"