text: import isPlainObject from "lodash/isPlainObject";
  import $$observable from "symbol-observable";
  
  /**
   * 私有的redux
   * 对于一个reducer来说,
   * 遇到未知的action时, 应该返回current state ( 原封不动返回 ), 遇到current state = undefined时, 应该返回初始状态
   *  
   * These are private action types reserved by Redux.
   * For any unknown actions, you must return the current state.
   * If the current state is undefined, you must return the initial state.
   * Do not reference these action types directly in your code.
   */
  export var ActionTypes = {
    INIT: "@@redux/INIT"
  };
  
  /**
   * 创建一个用来存储状态树的redux store ( 改变store中数据的唯一防范: 调用"dipsatch()" )
   * 
   * 应用中的store是唯一的:
   * 可以指定状态树的不同部分响应不同的action
   * 使用"combineReducers"将几个reducer组合成单一的reducer函数
   * 
   * Creates a Redux store that holds the state tree.
   * The only way to change the data in the store is to call `dispatch()` on it.
   *
   * There should only be a single store in your app. To specify how different
   * parts of the state tree respond to actions, you may combine several reducers
   * into a single reducer function by using `combineReducers`.
   *
   * @param {Function} reducer A function that returns the next state tree, given
   * the current state tree and the action to handle. 
   * 
   * 传递current state tree和待处理的action, 返回next state
   * (state,action)->nextStates
   *
   * @param {any} [preloadedState] The initial state. You may optionally specify it
   * to hydrate the state from the server in universal apps, or to restore a
   * previously serialized user session.
   * If you use `combineReducers` to produce the root reducer function, this must be
   * an object with the same shape as `combineReducers` keys.
   * 
   * 初始状态:
   *  从服务器端来的"hydrate"状态
   *  从会话中恢复的数据
   * 
   * 如果使用"combineReducres"产生root reducer, 那么初始状态的结构一定要和"combineReducers"的key保持一致
   *
   * @param {Function} enhancer The store enhancer. You may optionally specify it
   * to enhance the store with third-party capabilities such as middleware,
   * time travel, persistence, etc. The only store enhancer that ships with Redux
   * is `applyMiddleware()`.
   * 
   * store 增强器:
   * 使用第三方功能(如中间件,时间旅行,持久化等)增强store的 ( 例如 applyMiddleware就是一个增强器 )
   * 
   *
   * @returns {Store} A Redux store that lets you read the state, dispatch actions
   * and subscribe to changes.
   */
  export default function createStore(reducer, preloadedState, enhancer) {
    //
    // 处理preloadedState缺省的情况
    //
    if (typeof preloadedState === "function" && typeof enhancer === "undefined") {
      enhancer = preloadedState;
      preloadedState = undefined;
    }
    
    //
    // enhance store
    //
    if (typeof enhancer !== "undefined") {
      if (typeof enhancer !== "function") {
        throw new Error("Expected the enhancer to be a function.");
      }
  
      // 增强store
      return enhancer(createStore)(reducer, preloadedState);
    }
  
    //
    // check reducer
    //
    if (typeof reducer !== "function") {
      throw new Error("Expected the reducer to be a function.");
    }
  
    var currentReducer = reducer;
    var currentState = preloadedState;
    var currentListeners = [];
    var nextListeners = currentListeners;
    var isDispatching = false;
  
    /**
     * 
     */
    function ensureCanMutateNextListeners() {
      if (nextListeners === currentListeners) {
        nextListeners = currentListeners.slice();
      }
    }
  
    /**
     * Reads the state tree managed by the store.
     *
     * @returns {any} The current state tree of your application.
     */
    function getState() {
      return currentState;
    }
  
    /**
     * 添加一个change监听器
     * 
     * 
     * Adds a change listener. It will be called any time an action is dispatched,
     * and some part of the state tree may potentially have changed. You may then
     * call `getState()` to read the current state tree inside the callback.
     *
     * You may call `dispatch()` from a change listener, with the following
     * caveats:
     *
     * 1. The subscriptions are snapshotted just before every `dispatch()` call.
     * If you subscribe or unsubscribe while the listeners are being invoked, this
     * will not have any effect on the `dispatch()` that is currently in progress.
     * However, the next `dispatch()` call, whether nested or not, will use a more
     * recent snapshot of the subscription list.
     * 
     *
     * 2. The listener should not expect to see all state changes, as the state
     * might have been updated multiple times during a nested `dispatch()` before
     * the listener is called. It is, however, guaranteed that all subscribers
     * registered before the `dispatch()` started will be called with the latest
     * state by the time it exits.
     *
     * @param {Function} listener A callback to be invoked on every dispatch.
     * @returns {Function} A function to remove this change listener.
     */
    function subscribe(listener) {
      if (typeof listener !== "function") {
        throw new Error("Expected listener to be a function.");
      }
  
      var isSubscribed = true;
  
      ensureCanMutateNextListeners();
      nextListeners.push(listener);
  
      return function unsubscribe() {
        if (!isSubscribed) {
          return;
        }
  
        isSubscribed = false;
  
        ensureCanMutateNextListeners();
        var index = nextListeners.indexOf(listener);
        nextListeners.splice(index, 1);
      };
    }
  
    /**
     * 分发一个action( 它是触发状态改变的唯一方法 )
     * 1. nextState = reducer(currentState,action)
     * 2. nodify listeners
     *
     * Dispatches an action. It is the only way to trigger a state change.
     * 
     * 
     * The `reducer` function, used to create the store, will be called with the
     * current state tree and the given `action`. Its return value will
     * be considered the **next** state of the tree, and the change listeners
     * will be notified.
     * 
     * The base implementation only supports plain object actions. If you want to
     * dispatch a Promise, an Observable, a thunk, or something else, you need to
     * wrap your store creating function into the corresponding middleware. For
     * example, see the documentation for the `redux-thunk` package. Even the
     * middleware will eventually dispatch plain object actions using this method.
     *
     * @param {Object} action A plain object representing “what changed”. It is
     * a good idea to keep actions serializable so you can record and replay user
     * sessions, or use the time travelling `redux-devtools`. An action must have
     * a `type` property which may not be `undefined`. It is a good idea to use
     * string constants for action types.
     *
     * @returns {Object} For convenience, the same action object you dispatched.
     *
     * Note that, if you use a custom middleware, it may wrap `dispatch()` to
     * return something else (for example, a Promise you can await).
     */
    function dispatch(action) {
      //
      // check action
      //
      if (!isPlainObject(action)) {
        throw new Error(
          "Actions must be plain objects. " +
            "Use custom middleware for async actions."
        );
      }
  
      //
      // check action.type 
      //
      if (typeof action.type === "undefined") {
        throw new Error(
          'Actions may not have an undefined "type" property. ' +
            "Have you misspelled a constant?"
        );
      }
  
      // 
      // 确保同一时刻, 只能出现dispatch 一个action
      //
      if (isDispatching) {
        throw new Error("Reducers may not dispatch actions.");
      }
  
      //
      // 调用createStore时, 提供的reducer, 获得nextState
      //
      try {
        isDispatching = true;
        currentState = currentReducer(currentState, action);
      } finally {
        isDispatching = false;
      }
  
      // 
      // 通知订阅者
      //
      var listeners = (currentListeners = nextListeners);
      for (var i = 0; i < listeners.length; i++) {
        listeners[i]();
      }
  
      // 返回处理的action
      return action;
    }
  
    /**
     * Replaces the reducer currently used by the store to calculate the state.
     *
     * You might need this if your app implements code splitting and you want to
     * load some of the reducers dynamically. You might also need this if you
     * implement a hot reloading mechanism for Redux.
     *
     * @param {Function} nextReducer The reducer for the store to use instead.
     * @returns {void}
     */
    function replaceReducer(nextReducer) {
      if (typeof nextReducer !== "function") {
        throw new Error("Expected the nextReducer to be a function.");
      }
  
      currentReducer = nextReducer;
      dispatch({ type: ActionTypes.INIT });
    }
  
    /**
     * Interoperability point for observable/reactive libraries.
     * @returns {observable} A minimal observable of state changes.
     * For more information, see the observable proposal:
     * https://github.com/zenparsing/es-observable
     */
    function observable() {
      var outerSubscribe = subscribe;
      return {
        /**
         * The minimal observable subscription method.
         * @param {Object} observer Any object that can be used as an observer.
         * The observer object should have a `next` method.
         * @returns {subscription} An object with an `unsubscribe` method that can
         * be used to unsubscribe the observable from the store, and prevent further
         * emission of values from the observable.
         */
        subscribe(observer) {
          if (typeof observer !== "object") {
            throw new TypeError("Expected the observer to be an object.");
          }
  
          function observeState() {
            if (observer.next) {
              observer.next(getState());
            }
          }
  
          observeState();
          var unsubscribe = outerSubscribe(observeState);
          return { unsubscribe };
        },
  
        [$$observable]() {
          return this;
        }
      };
    }
  
    // When a store is created, an "INIT" action is dispatched so that every
    // reducer returns their initial state. This effectively populates
    // the initial state tree.
    dispatch({ type: ActionTypes.INIT });
  
    return {
      dispatch,
      subscribe,
      getState,
      replaceReducer,
      [$$observable]: observable
    };
  }
  