一、 redux出现的动机

1. Javascript 需要管理比任何时候都要多的state
2. state 在什么时候,由于什么原因,如何变化已然不受控制。
3. 来自前端开发领域的新需求
4. 我们总是将两个难以理清的概念混淆在一起:变化和异步。
5. Redux 视图让state 的变化变得可预测。

二、 核心概念

1. 想要更新state中的数据,你需要发起一个action,Action就是一个普通的JavaScript 对象用来描述发生了什么。为了把actin 和state串起来开发一些函数,就是redcer。

三、 三大原则

1. 单一数据源 整个应用的state被存储在一棵objecttree中, 并且这个 object tree 只 存在于一个唯一的store 中。

2. state 是只读的,唯一改变state的方法就是触发action,action 是一个用于描述已发 生事件的普通对象。(确保了视图和网络请求不能直接修改state,只能表达想要修改的意图)

3. 使用纯函数来执行修改为了描述action如何改变state tree ,你需要编写reducers。

四、 源码解析

1. 入口文件index.js
import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes' /*
* This is a dummy function to check if the function name has been altered by minification.
* If the function has been minified and NODE_ENV !== 'production', warn the user.
*/ // 判断文件是否被压缩了
function isCrushed() {} if (
process.env.NODE_ENV !== 'production' &&
typeof isCrushed.name === 'string' &&
isCrushed.name !== 'isCrushed'
) {
warning(
'You are currently using minified code outside of NODE_ENV === "production". ' +
'This means that you are running a slower development build of Redux. ' +
'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' +
'or setting mode to production in webpack (https://webpack.js.org/concepts/mode/) ' +
'to ensure you have the correct code for your production build.'
)
}
/*
从入口文件可以看出 redux 对外暴露了5个API。
createStore ,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
*/
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes
}
2. 对外暴露的第一个API createStore => createStore.js
 import $$observable from 'symbol-observable'

 import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject' /**
* 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.
*
* @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.
*
* @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()`.
*
* @returns {Store} A Redux store that lets you read the state, dispatch actions
* and subscribe to changes.
*/
/*
从源码上可以看出 createStore 是一个函数。接收三个参数 reducer, preloadeState, enhancer
*/
export default function createStore(reducer, preloadedState, enhancer) {
// 如果 preloadeState 是一个函数 && enhancer未定义preloadeState 和 enhancer交换位置
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
//
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 上面两个判断是为了确保 enchancer是个函数
return enhancer(createStore)(reducer, preloadedState)
} // reducer必须是 个函数,如果不是个函数给出友好的 提示
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
} let currentReducer = reducer // 把reducer 暂存起来
let currentState = preloadedState // 把preloadeState暂存起来
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false //判断是否正处于dispatch中 // 如果 nextListeners 和 currrentListeners 都指向一个内存空间的时候, 深复制一份出来。确保两个之间不会相互影响。
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.
*/
// 获取目前的 state的值。
function getState() {
if (isDispatching) {
throw new Error(
'You may not call store.getState() while the reducer is executing. ' +
'The reducer has already received the state as an argument. ' +
'Pass it down from the top reducer instead of reading it from the store.'
)
} return currentState
} /**
* 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 the listener to be a function.')
} if (isDispatching) {
throw new Error(
'You may not call store.subscribe() while the reducer is executing. ' +
'If you would like to be notified after the store has been updated, subscribe from a ' +
'component and invoke store.getState() in the callback to access the latest state. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
// 闭包的经典应用 每次订阅一个事件的时候,都有一个内部状态, 用于后续的取消订阅
let isSubscribed = true
// 深复制一份监听对象
ensureCanMutateNextListeners()
// 把每个监听对象都放置于一个数组中,保存下来,(精华之处,对闭包的使用登峰造极)
nextListeners.push(listener)
// 当注册一个监听事件的返回一个函数,调用这个函数可以取消订阅,具体操作方法就是从监听的数组中移出掉。
return function unsubscribe() {
// 防止重复取消订阅
if (!isSubscribed) {
return
} if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
// 对应上面那条,防止重复取消订阅
isSubscribed = false ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
// 删除数组中某一项的方法 splice
nextListeners.splice(index, 1)
}
} /**
* 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) {
// p、判断action是否是个对象
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
// 严格控制 action 的书写格式 { type: 'INCREMENT'}
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
} if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
// isDipatching 也是闭包的经典用法
/*
触发 dispatch的时候 把 isDispatching 改为 true。 照应全篇中对 dispatching 正在触发的时候的判断
finally 执行完毕的时候 置为 false
*/
try {
isDispatching = true
// 获取最新 的state 值。 currentState 经典之处闭包
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
} // 对监听对象从新赋值 其实里面每个listener都是一个函数。 于subscribe相对应
// 当每发生一个dispatch 事件的时候, 都循环调用,触发监听事件
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
} 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}
*/
// 替换 reducer 用 新的 reducer替换以前的reducer 参数同样必须是函数
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
} currentReducer = nextReducer
dispatch({ type: ActionTypes.REPLACE })
} /**
* Interoperability point for observable/reactive libraries.
* @returns {observable} A minimal observable of state changes.
* For more information, see the observable proposal:
* https://github.com/tc39/proposal-observable
*/
// 观察模式
function observable() {
const 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' || observer === null) {
throw new TypeError('Expected the observer to be an object.')
} function observeState() {
if (observer.next) {
observer.next(getState())
}
} observeState()
const 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.
// 触发一state 树
dispatch({ type: ActionTypes.INIT })
/*
由此可以看出 调用 createStore(); 后。对外暴露的方法
1. dispatch
2. subscribe
3. getState
4. replaceReducer
5.观察模式
const store = createStore(reducer, preloadedState, enchancer);
store.dispatch(action);
store.getState(); // 为什么这个方法能够获得 state的值。因为 currentState 的闭包实现。
store.subscribe(() => console.log(store.getState()));
store.replaceReducer(reducer);
总结:纵观createStore方法的实现,其实都是建立在闭包的基础之上。可谓是把闭包用到了极致。
*/
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
}
3. combineReducers.js
 import ActionTypes from './utils/actionTypes'
import warning from './utils/warning'
import isPlainObject from './utils/isPlainObject' function getUndefinedStateErrorMessage(key, action) {
const actionType = action && action.type
const actionDescription =
(actionType && `action "${String(actionType)}"`) || 'an action' return (
`Given ${actionDescription}, reducer "${key}" returned undefined. ` +
`To ignore an action, you must explicitly return the previous state. ` +
`If you want this reducer to hold no value, you can return null instead of undefined.`
)
} function getUnexpectedStateShapeWarningMessage(
inputState,
reducers,
action,
unexpectedKeyCache
) {
const reducerKeys = Object.keys(reducers)
const argumentName =
action && action.type === ActionTypes.INIT
? 'preloadedState argument passed to createStore'
: 'previous state received by the reducer' if (reducerKeys.length === 0) {
return (
'Store does not have a valid reducer. Make sure the argument passed ' +
'to combineReducers is an object whose values are reducers.'
)
} if (!isPlainObject(inputState)) {
return (
`The ${argumentName} has unexpected type of "` +
{}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
`". Expected argument to be an object with the following ` +
`keys: "${reducerKeys.join('", "')}"`
)
} const unexpectedKeys = Object.keys(inputState).filter(
key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
) unexpectedKeys.forEach(key => {
unexpectedKeyCache[key] = true
}) if (action && action.type === ActionTypes.REPLACE) return if (unexpectedKeys.length > 0) {
return (
`Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
`"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
`Expected to find one of the known reducer keys instead: ` +
`"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
)
}
} function assertReducerShape(reducers) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key]
const initialState = reducer(undefined, { type: ActionTypes.INIT }) if (typeof initialState === 'undefined') {
throw new Error(
`Reducer "${key}" returned undefined during initialization. ` +
`If the state passed to the reducer is undefined, you must ` +
`explicitly return the initial state. The initial state may ` +
`not be undefined. If you don't want to set a value for this reducer, ` +
`you can use null instead of undefined.`
)
} if (
typeof reducer(undefined, {
type: ActionTypes.PROBE_UNKNOWN_ACTION()
}) === 'undefined'
) {
throw new Error(
`Reducer "${key}" returned undefined when probed with a random type. ` +
`Don't try to handle ${
ActionTypes.INIT
} or other actions in "redux/*" ` +
`namespace. They are considered private. Instead, you must return the ` +
`current state for any unknown actions, unless it is undefined, ` +
`in which case you must return the initial state, regardless of the ` +
`action type. The initial state may not be undefined, but can be null.`
)
}
})
} /**
* Turns an object whose values are different reducer functions, into a single
* reducer function. It will call every child reducer, and gather their results
* into a single state object, whose keys correspond to the keys of the passed
* reducer functions.
*
* @param {Object} reducers An object whose values correspond to different
* reducer functions that need to be combined into one. One handy way to obtain
* it is to use ES6 `import * as reducers` syntax. The reducers may never return
* undefined for any action. Instead, they should return their initial state
* if the state passed to them was undefined, and the current state for any
* unrecognized action.
*
* @returns {Function} A reducer function that invokes every reducer inside the
* passed object, and builds a state object with the same shape.
*/ /*
combineReducers 顾名思义就是合并reduces的一个方法。
1. 为了项目便于维护与管理我们就需要拆按模块拆分reducers。
2. 而combineReducers就是为了解决这个的问题的。 */
export default function combineReducers(reducers) { // 参数reducers 是一个对象
const reducerKeys = Object.keys(reducers) // 获取reducers的k
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
warning(`No reducer provided for key "${key}"`)
}
} // 深复制一份reducers出来, 防止后续操作出现不可控因素
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers) let unexpectedKeyCache
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
} let shapeAssertionError
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
// 闭包的运用, 把合并的 reducer保存下来。
return function combination(state = {}, action) {
if (shapeAssertionError) {
throw shapeAssertionError
} if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(
state,
finalReducers,
action,
unexpectedKeyCache
)
if (warningMessage) {
warning(warningMessage)
}
} let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
// 把合并的时候的key值作为Key值为标准。 在循环遍历的时候取出对应的 reducers 触发 reducer函数。
/*
其实对应的createStore.js中的
try {
isDispatching = true
// 获取最新 的state 值。 currentState 经典之处闭包
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
*/
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
/**
* 使用方法
* const reducers = combineReducers({ reducer1, reducer2 });
* const store = createStore(reducers, preloadedState, enchancer);
*/
4. bindActionCreators.js
 // 主要这个函数
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
} /**
* Turns an object whose values are action creators, into an object with the
* same keys, but with every function wrapped into a `dispatch` call so they
* may be invoked directly. This is just a convenience method, as you can call
* `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
*
* For convenience, you can also pass a single function as the first argument,
* and get a function in return.
*
* @param {Function|Object} actionCreators An object whose values are action
* creator functions. One handy way to obtain it is to use ES6 `import * as`
* syntax. You may also pass a single function.
*
* @param {Function} dispatch The `dispatch` function available on your Redux
* store.
*
* @returns {Function|Object} The object mimicking the original object, but with
* every action creator wrapped into the `dispatch` call. If you passed a
* function as `actionCreators`, the return value will also be a single
* function.
*/ /*
接受两个参数,一个action creator, 一个是 value的 action creator的对象。
dispatch 。 一个由 Store 实列 提供的dispatch的函数。 看createStore.js源码就可以做知道其中原理。
*/
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
} if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${
actionCreators === null ? 'null' : typeof actionCreators
}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
} const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
/*
一个与原对象类似的对象,只不过这个对象的 value 都是会直接 dispatch 原 action creator 返回的结果的函数。
如果传入一个单独的函数作为 actionCreators,那么返回的结果也是一个单独的函数。
本来触发 action 的方法是 store.dispatch(action);
经过这个方法封装后 可以直接调用函数名字
aa('参数');
*/
return boundActionCreators
}

5. applyMiddleware.js  在redux 中最难理解的一个函数。

import compose from './compose'
import createStore from "./createStore"; /**
* Creates a store enhancer that applies middleware to the dispatch method
* of the Redux store. This is handy for a variety of tasks, such as expressing
* asynchronous actions in a concise manner, or logging every action payload.
*
* See `redux-thunk` package as an example of the Redux middleware.
*
* Because middleware is potentially asynchronous, this should be the first
* store enhancer in the composition chain.
*
* Note that each middleware will be given the `dispatch` and `getState` functions
* as named arguments.
*
* @param {...Function} middlewares The middleware chain to be applied.
* @returns {Function} A store enhancer applying the middleware.
*/
// 通过看源码知道 applyMiddleware返回一个高阶函数。
/*
applyMiddleware的使用地方
const store = createStore(reducer,{}, applyMiddleware(...middlewares)); 由此可以看出 applyMiddlewares 的使用方法主要是和 createStore.js 中 createStore方法的第三个参数对应。翻开源码
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
// 上面两个判断是为了确保 enchancer是个函数
return enhancer(createStore)(reducer, preloadedState)
}
*/
export default function applyMiddleware(...middlewares) {
// createSotre 中 的第三个参数 enhancer
return createStore => (...args) => {
// 通过对应的代码可以发现其实 ...aregs 对应的是 reducer, preloadedState
const store = createStore(...args)
let dispatch = () => {
throw new Error(
`Dispatching while constructing your middleware is not allowed. ` +
`Other middleware would not be applied to this dispatch.`
)
}
// 定义中间件必须满足的条件。 API getState, dispatch();
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI)) /**
* export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
} if (funcs.length === 1) {
return funcs[0]
} return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
compose函数 主要是 利用数组 reducer 方法对参数的处理。 */
dispatch = compose(...chain)(store.dispatch)
// 通过这个返回值我们可以知道 在createStore.js中enchancer的返回值。
return {
...store,
dispatch
}
}
} /**
* applyMiddlewares函数比较难理解。 多看几个中间件,比如 logger 和 redux-thunk 等。对该方法能够更深的理解。
*/

五、 redux的总结

通过阅读redux的源码,印象最深的就是如何手动写个订阅模式,数据改变的时候,如何触发所有监听事件。闭包的运用登峰造极。其中最难的两个函数 applyMiddlewares 和compose.js 还需要细细体会。没有真正领悟到其精华之处。

谢谢大家。

redux的源码解析的更多相关文章

  1. redux源码解析(深度解析redux+异步demo)

    redux源码解析 1.首先让我们看看都有哪些内容 2.让我们看看redux的流程图 Store:一个库,保存数据的地方,整个项目只有一个 创建store Redux提供 creatStore 函数来 ...

  2. Redux系列x:源码解析

    写在前面 redux的源码很简洁,除了applyMiddleware比较绕难以理解外,大部分还是 这里假设读者对redux有一定了解,就不科普redux的概念和API啥的啦,这部分建议直接看官方文档. ...

  3. Redux异步解决方案之Redux-Thunk原理及源码解析

    前段时间,我们写了一篇Redux源码分析的文章,也分析了跟React连接的库React-Redux的源码实现.但是在Redux的生态中还有一个很重要的部分没有涉及到,那就是Redux的异步解决方案.本 ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  5. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  6. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  7. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  8. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  9. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

随机推荐

  1. 【洛谷】【动态规划/01背包】P1734 最大约数和

    [题目描述:] 选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大. [输入格式:] 输入一个正整数S. [输出格式:] 输出最大的约数之和. [算法分析:] 01背包,每个数 ...

  2. dp HDU - 5074

    按题意推表达式 #include<cstdio> #include<cstring> #define max(a, b) (a)>(b)?(a):(b) ][], num ...

  3. day16 Python filter函数

    前戏 movie_people = ["alex","charon","pluto","liu","sb&qu ...

  4. 安装docker后访问外网失败

    服务器网段是(172.17)与docker默认桥接的网桥号段相同 docker version 17.03.2-ce 1.centOS6 原文链接 http://www.bubuko.com/info ...

  5. P2-Centos中安装vsftpd

    1. 卸载已有版本 yum remove vsftpd 2. 安装 yum install -y vsftpd 3. 创建文件服务器根目录 mkdir /ftpfile 4. 创建ftp服务器用户 u ...

  6. 你所不了解的javascript操作DOM的细节知识点(一)

    你所不了解的javascript操作DOM的细节知识点(一) 一:Node类型 DOM1级定义了一个Node接口,该接口是由DOM中的所有节点类型实现.每个节点都有一个nodeType属性,用于表明节 ...

  7. HTML 浏览器抓包

    1.浏览器 2.抓包 3.查看get/post/cookie 一 谷歌浏览器 二 抓包查看get/post数据.cookie 截图:

  8. C++基类的析构函数定义为虚函数的原因

    1:每个析构函数只会清理自己的成员(成员函数前没有virtual).2:可能是基类的指针指向派生类的对象,当析构一个指向派生类的成员的基类指针,这时程序不知道这么办,可能会造成内存的泄露,因此此时基类 ...

  9. luogu P4403 [BJWC2008]秦腾与教学评估

    题目 一道神奇的题qwq 首先看题很容易想到把所有的点存下来然后暴力枚举...于是RE 20分 所以要找一种不用开那么大的数组的解法(然而我自己是不可能想出来的qwq 注意一个地方,人数为奇数的位置“ ...

  10. tiled卷积神经网络(tiled CNN)

    这个结构是10年Quoc V.Le等人提出的,这里的tiled,按照 Lecun的解释是Locally-connect non shared.即是局部连接,而且不是共享的,这是针对于权重来说的.本文翻 ...