在mobx-react中看到的代码,记录下来,以学习之
采用了侵入式的方法,给对象新增一个mixins对象,用于保存mixins,然后更改对象的方法为访问器属性,在访问器属性getter中完成mixins逻辑
const mixinKey = Symbol('mixin')
const patchMixinKey = Symbol('patchedMixin')
function getMixins(target, methodName) {
const mixins = (target[mixinKey] = target[mixinKey] || {})
const methodMixins = (mixins[methodName] = mixins[methodName] || {})
methodMixins.locks = methodMixins.locks || 0
methodMixins.methods = methodMixins.methods || []
return methodMixins
}
function wrapper(realMethod, mixins, ...args) {
mixins.locks++
try {
let retVal
if (realMethod !== undefined && realMethod !== null) {
retVal = realMethod.apply(this, args)
}
return retVal
} finally {
mixins.locks--
if (mixins.locks === 0) {
mixins.methods.forEach(mx => {
mx.apply(this, args)
})
}
}
}
function wrapFunction(realMethod, mixins) {
const fn = function (...args) {
wrapper.call(this, realMethod, mixins, ...args)
}
return fn
}
function createDefinition(
target,
methodName,
enumerable,
mixins,
originalMethod
) {
let wrappedFunc = wrapFunction(originalMethod, mixins)
return {
[patchMixinKey]: true,
enumerable: enumerable,
configurable: true,
get: function () {
return wrappedFunc
},
set: function (value) {
if (this === target) {
wrappedFunc = wrapFunction(value, mixins)
} else {
const newDefinition = createDefinition(this, methodName, enumerable, mixins, value)
Object.defineProperty(this, methodName, newDefinition)
}
}
}
}
function patch(target, methodName, mixinMethod) {
const mixins = getMixins(target, methodName)
if (mixins.methods.indexOf(mixinMethod) < 0) {
mixins.methods.push(mixinMethod)
}
const oldDefinition = Object.getOwnPropertyDescriptor(target, methodName)
if (oldDefinition && oldDefinition[patchMixinKey]) {
return
}
const originalMethod = target[methodName]
const newDefinition = createDefinition(
target,
methodName,
oldDefinition ? oldDefinition.enumerable : undefined,
mixins,
originalMethod
)
Object.defineProperty(target, methodName, newDefinition)
}
const testObj = {
foo() {
console.log('original foo')
}
}
patch(testObj, 'foo', (testArg) => {
console.log('mixin1', testArg)
})
patch(testObj, 'foo', (testArg) => {
console.log('mixin2', testArg)
})
testObj.foo('bar')