index.js
function traverse (value, v) {
  var state = {}
  var visitor = v.visitor
  var seen = []

  /**
   * Recursively walk the value, dispatching visitor methods as values are encountered.
   *
   * @param {*} val Value
   * @param {Value|null} parent Parent value
   * @param {object} [opts] Additional options
   */
  function traverseValue (val, parent, opts) {
    var wrapper = Value(val, parent, opts)
    var enterFn = wrapper.type + 'Enter'
    var exitFn = wrapper.type + 'Exit'
    var keys

    if (wrapper.type === 'object' || wrapper.type === 'array') {
      if (seen.indexOf(wrapper.value) >= 0) {
        wrapper.isCircularRef = true
      }
      seen.push(wrapper.value)
    }

    if (wrapper.type === 'object') {
      keys = Object.keys(wrapper.value).sort()
      wrapper.length = keys.length
    }

    if (visitor[enterFn]) {
      visitor[enterFn](wrapper, state)
    } else if (visitor.otherEnter) {
      visitor.otherEnter(wrapper, state)
    }

    switch (wrapper.type) {
      case 'array':
        if (!wrapper.isCircularRef) {
          wrapper.value.forEach(function (child, i) {
            traverseValue(child, wrapper, { key: i })
          })
        }
        break

      case 'object':
        if (!wrapper.isCircularRef) {
          keys.forEach(function (key) {
            traverseValue(wrapper.value[key], wrapper, { key: key })
          })
        }
        break

      default:
        /* do nothing */
        break
    }

    if (visitor[exitFn]) {
      visitor[exitFn](wrapper, state)
    }

    if (wrapper.type === 'object' || wrapper.type === 'array') {
      seen.pop()
    }
  }

  if (v.pre) { v.pre.call(null, state) }
  traverseValue(value, null, null)
  if (v.post) { v.post.call(null, state) }
}
function traverse (value, v) {
  var state = {}
  var visitor = v.visitor
  var seen = []

  /**
   * Recursively walk the value, dispatching visitor methods as values are encountered.
   *
   * @param {*} val Value
   * @param {Value|null} parent Parent value
   * @param {object} [opts] Additional options
   */
  function traverseValue (val, parent, opts) {
    var wrapper = Value(val, parent, opts)
    var enterFn = wrapper.type + 'Enter'
    var exitFn = wrapper.type + 'Exit'
    var keys

    if (wrapper.type === 'object' || wrapper.type === 'array') {
      if (seen.indexOf(wrapper.value) >= 0) {
        wrapper.isCircularRef = true
      }
      seen.push(wrapper.value)
    }

    if (wrapper.type === 'object') {
      keys = Object.keys(wrapper.value).sort()
      wrapper.length = keys.length
    }

    if (visitor[enterFn]) {
      visitor[enterFn](wrapper, state)
    } else if (visitor.otherEnter) {
      visitor.otherEnter(wrapper, state)
    }

    switch (wrapper.type) {
      case 'array':
        if (!wrapper.isCircularRef) {
          wrapper.value.forEach(function (child, i) {
            traverseValue(child, wrapper, { key: i })
          })
        }
        break

      case 'object':
        if (!wrapper.isCircularRef) {
          keys.forEach(function (key) {
            traverseValue(wrapper.value[key], wrapper, { key: key })
          })
        }
        break

      default:
        /* do nothing */
        break
    }

    if (visitor[exitFn]) {
      visitor[exitFn](wrapper, state)
    }

    if (wrapper.type === 'object' || wrapper.type === 'array') {
      seen.pop()
    }
  }

  if (v.pre) { v.pre.call(null, state) }
  traverseValue(value, null, null)
  if (v.post) { v.post.call(null, state) }
}
function traverse ( value , v ) { var state = { } var visitor = v . visitor var seen = [ ] /**
   * Recursively walk the value, dispatching visitor methods as values are encountered.
   *
   * @param {*} val Value
   * @param {Value|null} parent Parent value
   * @param {object} [opts] Additional options
   */ function traverseValue ( val , parent , opts ) { var wrapper = Value ( val , parent , opts ) var enterFn = wrapper . type + 'Enter' var exitFn = wrapper . type + 'Exit' var keys if ( wrapper . type === 'object' || wrapper . type === 'array' ) { if ( seen . indexOf ( wrapper . value ) >= 0 ) { wrapper . isCircularRef = true } seen . push ( wrapper . value ) } if ( wrapper . type === 'object' ) { keys = Object . keys ( wrapper . value ) . sort ( ) wrapper . length = keys . length } if ( visitor [ enterFn ] ) { visitor [ enterFn ] ( wrapper , state ) } else if ( visitor . otherEnter ) { visitor . otherEnter ( wrapper , state ) } switch ( wrapper . type ) { case 'array' : if ( ! wrapper . isCircularRef ) { wrapper . value . forEach ( function ( child , i ) { traverseValue ( child , wrapper , { key : i } ) } ) } break case 'object' : if ( ! wrapper . isCircularRef ) { keys . forEach ( function ( key ) { traverseValue ( wrapper . value [ key ] , wrapper , { key : key } ) } ) } break default : /* do nothing */ break } if ( visitor [ exitFn ] ) { visitor [ exitFn ] ( wrapper , state ) } if ( wrapper . type === 'object' || wrapper . type === 'array' ) { seen . pop ( ) } } if ( v . pre ) { v . pre . call ( null , state ) } traverseValue ( value , null , null ) if ( v . post ) { v . post . call ( null , state ) } }
Traverse a value with a visitor object.

@param {*} value Value to traverse
@param {Visitor} visitor Visitor instance
Traverse a value with a visitor object .