Hook/Hijack Function

 · 3 mins read

I came across some issues in a project of my work, which needs me to modify the code logics but I don’t want to do so, because my group mates have been using the original functions all the time. So I just hook/hijack the original function to add some another process before they use them, without any extra edition for my group mates. It’s like AOP programming, at least I think it is.

So I uploaded my code to Github. Here it is.

hijack-js

This is a tiny simple version of implemention of hijacking functions.

Import:

in Browser:

<script type="text/javascript" src="hijack.js"></script>

in Node:

var hijack = require("hijack");

Usage:

//default context is window || global 
hijack.[pre|post|around]("funcName",newfunction[, context])

//undo
hijack.strip("funcName"[, context])

//back to initial function
hijack.restore("funcName"[,context])

Example:

var obj = {
    print : function(str){
        console.log(str)
    }
};


hijack.pre("print",function(){
    console.log("pre");
},obj);


hijack.post("print",function(){
    console.log("post");
},obj);


hijack.around("print",function(func,args){
    console.log("apre");
    func.apply(this,args);
    console.log("apost");
},obj);

obj.print("test")
// command output:
// apre 
// pre  
// test 
// post 
// apost

hijack.strip("print",obj);
hijack.strip("print",obj);
obj.print("test again")
// command output:
// pre
// test again


hijack.restore("print",obj);
obj.print("only this sentence")
// command output:
// only this sentence


SOURCE CODE

/*!
 * https://github.com/Steven6977/hijack-js
 *
 * @author: yangluhao
 */

(function (global, factory){
    "use strict"

    if ( typeof module === "object" && typeof module.exports === "object" ) {
        module.exports = factory( global )
    } else {
        global.hijack = factory( global )
    }


})(this, function(g){

    function isFunction(f){
        //newest method
        return typeof f === "function"
    }

    function isString(s){
        return typeof s === "string"
    }

    function isUndef (v) {
      return v === undefined || v === null
    }

    function error(str){
        //alert(str)
        console.error(str)
    }

    function validation(fname,newf,context){
        var _context = context || g
        if(!isString(fname)){
            error("The first param is supposed to be a string")
            return false
        }

        if(!isFunction(newf)){
            error("The secdond param is supposed to be a function")
            return false
        }

        if(isUndef(_context[fname])){
            error("could not find "+fname+" in the given context")
            return false
        }else{
            if(!isFunction(_context[fname])){
                error(fname + "is not a function")
                return false
            }
        }
        return true
    }

    var hookChain = function(){
        var map = Object.create(Object.prototype)
        function Node(type,func){
            this.type = type
            this.func = func
        }
        Node.prototype = null

        return {
            add : function(key,func,type){
                if(!Array.isArray(this[key])){
                    this[key] = new Array()
                }
                this[key].push(new Node(type,func))

            },
            remove : function(key){
                if(!Array.isArray(this[key]))
                    return 
                else
                    return this[key].pop()
            },
            first : function(key){
                if(!Array.isArray(this[key]))
                    return 
                else
                    return this[key][0]
            },
            destory : function(key){
                delete this[key]
                this[key] = null
            }

        }
    }()


    return {

        pre : function(fname,newf,context){
            if(!validation(fname,newf,context))
                return false

            var _context = context || g
            var oldf = _context[fname]
            _context[fname] = function(){
                newf.apply(this,Array.prototype.slice.call(arguments))
                oldf.apply(this,Array.prototype.slice.call(arguments))
            }
            hookChain.add(fname,oldf,"pre")
            return true
        },

        post : function(fname,newf,context){
            if(!validation(fname,newf,context))
                return false

            var _context = context || g
            var oldf = _context[fname]
            _context[fname] = function(){
                oldf.apply(this,Array.prototype.slice.call(arguments))
                newf.apply(this,Array.prototype.slice.call(arguments))
            }
            hookChain.add(fname,oldf,"post")
            return true

        },

        around : function(fname,returnFunc,context){
            if(!validation(fname,returnFunc,context))
                return false

            var _context = context || g
            var oldf = _context[fname]
            _context[fname] = function(){
                returnFunc.call(this,oldf,Array.prototype.slice.call(arguments))
            }
            hookChain.add(fname,oldf,"around")
            return true
        },

        strip : function(fname,context){
            if(!validation(fname,function(){},context))
                return false

            var _context = context || g 
            var node = hookChain.remove(fname)
            if(node && node.func){
                _context[fname] = node.func
                return true
            }else{
                return false
            }

        },

        restore : function(fname,context){
            if(!validation(fname,function(){},context))
                return false

            var _context = context || g
            var node = hookChain.first(fname)
            if(node && node.func){
                _context[fname] = node.func
                hookChain.destory(fname)
                return true
            }else{
                return false
            }

        }

    }

    }

)