views:

727

answers:

2

What's the best method to check if the protoype of a method has been changed?

+1  A: 

It depends on what you mean by "changed" if you mean changed between when your code gets loaded and some later time, you can just store a reference to the function, a la

var oldFunc = SomeType.prototype.someFunction;
...
if (oldFunc === someInstance.someFunction) // unchanged, note the use of strict equality

But if you mean changed from the default native implementation there's no real way to tell.

olliej
So some libraries like Prototype modify the core object prototypes, like Array.prototype.push or pop. So I'm wondering how you can check if the default prototype method for a native object has been changed.
Geuis
+4  A: 

If you do a toString() on a function you get the source code of the function. For native functions, FF, IE, Opera and Chrome returns a function with the body [native code]. However, Chrome has most functions implemented in javascript and will return the source for most functions (Object.constructor is one of the few native functions in Chrome that does return [native code])

Below you find a function with a regexp that checks for [native code]. (there is no need to call toString() since it is done automatically when the function isn't called). It is tested with FF3, IE7, Opera 9.6 and Chrome 1. But as I said, since Chrome does return the real source code for most functions it isn't useful to test it in that browser.

function isNative(func) {
    return /^\s*function[^{]+{\s*\[native code\]\s*}\s*$/.test(func);
}


alert(isNative(Array.prototype.push));

Update

The above code will of course not detect if a native method is replaced with some other native method, like Array.prototype.push = Math.abs. If you want to detect that kind of change, or if methods of your own objects are changed, you must store the original method in a variable, then run the function that you suspect changes it, followed by a compare with the stored methods.

However, after reading the comment from the op on olliej answer, it is quite clear that the OP wanted to know how to detect if methods on the native objects has been changed. If they are changed they are usually not replaced with another native function but with some new code, usually to add methods that the browser don't have natively, or to change the behavior to be compatible with the expected standard. In that case the code above will work in FF, IE and Opera but not Crome.

If you want to detect any type of changes of the methods the following code might be of use. The following function creates an object with two methods: save and compare. save is automatically called if arguments is supplied when the object is created. save expects two or more arguments where the first is the object and the rest is the method names to be saved. The reason you must supply the names of the methods is because most internal objects has the "do not enumerate"-flag set on the methods.

function Cmpobj() {
    if (this.constructor !== arguments.callee){
        throw SyntaxError("Constructor called as function");
    }
    var srcobj, methods=[];
    this.save=function(obj) {
        var undef; //Local undefined
        srcobj=obj;
        for (var i=arguments.length -1; i>0; --i) {
            var name = arguments[i];
            //Push an object on the array without using push
            methods[methods.length] = {
                name:name,
                func:typeof obj[name] === "function" ? obj[name]:undef
            };
        }
    }
    this.compare=function(obj) {
        var changed=[];
        obj = obj || srcobj;
        for (var i=methods.length-1; i>=0; --i) {
            if (methods[i].func !== obj[methods[i].name]) {
                changed[changed.length]=methods[i].name;
            }
        }
        return changed;
    }
    if (arguments.length) this.save.apply(this,arguments);
}

// Creating a compare object. The first parameter is the object,
// followed by up to 254 method names.    
var saved = new Cmpobj(Array.prototype,"pop","push","slice");

//Do some change
Array.prototype.pop = Array.prototype.push;

// Compare if something is changed    
alert(saved.compare().join(", "));
some
This doesn't work if you do `Array.push = Math.abs` that's why i didn't suggest it in my answer.
olliej
@olliej: That was implied, but I have updated the answer anyway.
some