Create a reset of javascript Array prototype when Array.prototype has been modified?

后端 未结 2 788
情深已故
情深已故 2020-11-30 09:09

General question: When a default Javascript prototype like Array has been modified, hacked, changed and twisted to the point of being unusable, is there any

2条回答
  •  無奈伤痛
    2020-11-30 09:37

    OP's probably already figured something out by now, but for anyone else coming in from a Google search or wherever, here's a function that returns the unmodified version of any default constructor passed to it:

    // Note: the double name assignment below is intentional.
    // Only change this part if you want to use a different variable name.
    //  │││││ The other one here needs to stay the same for internal reference.
    //  ↓↓↓↓↓            ↓↓↓↓↓
    var reset = function reset(constructor) {
        if (!(constructor.name in reset)) {
            var iframe = document.createElement('iframe');
            iframe.src = 'about:blank';
            document.body.appendChild(iframe);
            reset[constructor.name] = iframe.contentWindow[constructor.name];
            document.body.removeChild(iframe);
        } return reset[constructor.name];
    }
    

    Usage goes like this:

    The Problem

    Somebody does something stupid to a default prototype...

    Array.prototype.push = function () {
        var that = this;
        [].forEach.call(arguments, function (argument) {
            that.splice(Math.round(Math.random()*that.length), 0, argument)
        }); return 'Trolololo';
    }
    

    ...and your code becomes a broken mess.

    var myArray = new Array(0, 1, 2, 3);
    //-> undefined
        // Ok, I made an array.
    myArray;
    //-> [0, 1, 2, 3]
        // So far so good...
    myArray.push(4, 5);
    //-> "Trolololo"
        // What?
    myArray;
    //-> [5, 0, 1, 2, 4, 3]
        // WHAT!?
    

    The Solution

    So you throw that function into the mix...

    var reset = function reset(constructor) {
        if (!(constructor.name in reset)) {
            var iframe = document.createElement('iframe');
            iframe.src = 'about:blank';
            document.body.appendChild(iframe);
            reset[constructor.name] = iframe.contentWindow[constructor.name];
            document.body.removeChild(iframe);
        } return reset[constructor.name];
    }
    

    ...and put it to use like so.

    var myArray = new reset(Array)(0, 1, 2, 3);
    //-> undefined
        // Looks the same
    myArray;
    //-> [0, 1, 2, 3]
        // Still looks the same
    myArray.push(4, 5);
    //-> 6
        // Hey, it returned what it's supposed to...
    myArray;
    //-> [0, 1, 2, 3, 4, 5]
        // ...and all's right with the world again!
    

    Also, because each reset constructor is cached the first time it's returned, you can save a character if you want to by referencing the cache directly (reset.Array) instead of through the function (reset(Array)) every time after that.


    Good luck!

提交回复
热议问题