模拟手写SyncHook源码
const SyncHook=require('./SyncHook.js') let hook=new SyncHook(["name","age"]) hook.tap('fn1',function(name,age){ console.log('fn1--->',name,age) }) hook.tap('fn2',function(name,age){ console.log('fn2--->',name,age) }) hook.call('lw',18)
class Hook { constructor(args = []) { this.args = args this.taps = [] // 将来用于存放组装好的 {} this._x = undefined // 将来在代码工厂函数中会给 _x = [f1, f2, f3....] } tap(options, fn) { if (typeof options === 'string') { options = { name: options } } options = Object.assign({ fn }, options) // { fn:... name:fn1 } // 调用以下方法将组装好的 options 添加至 [] this._insert(options) } _insert(options) { this.taps[this.taps.length] = options } call(...args) { // 01 创建将来要具体执行的函数代码结构 let callFn = this._createCall() // 02 调用上述的函数(args传入进去) return callFn.apply(this, args) } _createCall() { return this.compile({ taps: this.taps, args: this.args }) } } module.exports = Hook
let Hook = require('./Hook.js') class HookCodeFactory { args() { return this.options.args.join(',') // ["name", "age"]===> name, age } head() { return `var _x = this._x;` } content() { let code = `` for (var i = 0; i < this.options.taps.length; i++) { code += `var _fn${i} = _x[${i}];_fn${i}(${this.args()});` } return code } setup(instance, options) { // 先准备后续需要使用到的数据 this.options = options // 这里的操作在源码中是通过 init 方法实现,而我们当前是直接挂在了 this 身上 instance._x = options.taps.map(o => o.fn) // this._x = [f1, f2, ....] } create() { //核心就是创建一段可执行的代码体然后返回 let fn // fn = new Function("name, age", "var _x = this._x, var _fn0 = _x[0]; _fn0(name, age);") fn = new Function( this.args(), this.head() + this.content() ) return fn } } let factory = new HookCodeFactory() class SyncHook extends Hook { constructor(args) { super(args) } compile(options) { // {taps: [{}, {}], args: [name, age]} factory.setup(this, options) return factory.create(options) } } module.exports = SyncHook