Vue.js Internals: How computed properties work

by gongzza

HTML

<div id="app"></div>

JavaScript

// https://medium.com/@kelly.kh.woo/%EB%B2%88%EC%97%AD-vue-js-internals-how-computed-properties-work-366d22c6f0e7
// http://jsbin.com/vevupup/embed?js,console

const app = document.getElementById('app')
const log = (...args) => {
	console.log(...args)
	app.innerText += args + '\n'
}

const Dep = { target: null }

function defineReactive(obj, key, val) {
	const deps = []
	Object.defineProperty(obj, key, {
  	get() {
    
    	if (Dep.target && !~deps.indexOf(Dep.target)) {
      	deps.push(Dep.target)
      }
      
    	return val
    },
    set(newVal) {
    	val = newVal
      for (const dep of deps) {
      	dep()
      }
    }
  })
}

function defineComputed(obj, key, computeFunc, callback) {
	function onDependencyUpdated() {
  	log(`${key} called`)
  	val = computeFunc()
    callback(val)
  }
  
  // Set current update callback 
  Dep.target = onDependencyUpdated;

  // Compute the value
  let val = computeFunc ();

  // Reset the target so no more property adds this as dependency
  Dep.target = null;
  
  Object.defineProperty(obj, key, {
  	get() {
    	// 다른 computed가 참조할 경우 compute()를 호출해서
      // 참조가 추가될 수 있도록 한다.
    	if (Dep.target) {
      	onDependencyUpdated()
      }
      return val
    }
  })
}

var person = {};
defineReactive (person, 'age', 16);
defineReactive (person, 'country', 'Brazil');

defineComputed (person, 'status', function () {
  if (person.age > 18) {
    return 'Adult'
  }
  else {
    return 'Minor'
  }
}, function (newValue) {
  log ("CHANGED!! The person's status is now: " + newValue)
});

defineComputed (person, 'isAdult', function () {
	return person.status === 'Adult'
}, function (newValue) {
  log ("CHANGED!! The person's isAdult is now: " + newValue)
})

log ("Current age: " + person.age)
log ("Current status: " + person.status)
log("Current isAdult: " + person.isAdult)

// change age
log ("Changing age");
person.age = 22;

log ("Current age: " + person.age)
log ("Current status: " + person.status)
log("Current isAdult: "...