Search code examples
javascripttwitter-bootstrapclassecmascript-6private

Why does Bootstrap 4 use private methods in es6 classes?


I was looking inside the source code of Bootstrap 4, and I discovered they're using es6 classes coupled with some revealing module pattern of some sort.

Here is a simplified example of code taken from here.

const Modal = (($) => {


  const NAME                         = 'modal'
  const VERSION                      = '4.0.0-alpha.3'
  ...

  const Default = {
    ...
  }


  class Modal {

    constructor(element, config) {
      this._config              = this._getConfig(config)
      this._element             = element
      ...
    }


    // public

    toggle(relatedTarget) {
      ...
    }

    show(relatedTarget) {
      ...
    }

    hide(event) {
      ...
    }

    dispose() {
      ...
    }


    // private

    _getConfig(config) {
      ...
    }

    _showElement(relatedTarget) {
      ...
    }

    _enforceFocus() {
      ...
    }

    _setEscapeEvent() {
      ...
    }

    _setResizeEvent() {
      ...
    }

  }

  return Modal

})(jQuery)

export default Modal

This would result in every method or property being exposed, including the private ones. However, this does not happen in the final product. For example, something like $('#myModal').modal('_getConfig') would not work. What is happening?


Solution

  • It's only adding one function to the jQuery prototype _jQueryInterface:

      $.fn[NAME]             = Modal._jQueryInterface
      $.fn[NAME].Constructor = Modal
      $.fn[NAME].noConflict  = function () {
        $.fn[NAME] = JQUERY_NO_CONFLICT
        return Modal._jQueryInterface
      }
    
      return Modal
    
    })(jQuery)
    

    If you look at the code of _jQueryInterface you'll see:

    static _jQueryInterface(config, relatedTarget) {
      return this.each(function () {
        let data    = $(this).data(DATA_KEY)
        let _config = $.extend(
          {},
          Modal.Default,
          $(this).data(),
          typeof config === 'object' && config
        )
    
        if (!data) {
          data = new Modal(this, _config)
          $(this).data(DATA_KEY, data)
        }
    
        if (typeof config === 'string') {
          if (data[config] === undefined) {
            throw new Error(`No method named "${config}"`)
          }
          data[config](relatedTarget)
        } else if (_config.show) {
          data.show(relatedTarget)
        }
      })
    }
    

    If we look closely, you'll see that the instance of the class Modal is being saved as data:

        if (!data) {
          data = new Modal(this, _config)
          $(this).data(DATA_KEY, data)
        }
    

    You can access it in the same fashion the script does it (but only after creating it for the first time):

    let data    = $(this).data(DATA_KEY)
    

    DATA_KEY is bs.modal

    Edit:

    $('#myModal').modal('_getConfig');
    

    The function _getConfig is actually being called, it's just that function is returning the jQuery object not whatever the result of _getConfig is.