Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
220 views
in Technique[技术] by (71.8m points)

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

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 way to create instances of (or re-implement) the original, un-modified prototype?


My case: I've got some code that is failing in the 'edit' mode of a (horrible, proprietary, closed source...) content management system, because the javascript for the interface of the 'edit' mode of the content management system hacks the absolute living hell out of the Array prototype.

My code will work in the non-edit mode of the CMS, but, to get there, it has be tested in the 'edit' mode. It's possible to test if a prototype has been modified. Is it possible to re-implement the default Array prototype so I could do something like this:

var hasArrayBeenTrashed = // boolean based on https://stackoverflow.com/questions/574584/
var normalArray.prototype = // based on answer to this question 
var myArray = !hasArrayBeenTrashed ? [] : new normalArray;
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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!


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...