Создайте reset прототипа массива javascript, когда Array.prototype был изменен?

Общий вопрос: Когда прототип Javascript по умолчанию, такой как Array, был изменен, взломан, изменен и скручен до степени непригодности, существует ли способ создания экземпляров (или повторного внедрения) оригинальный, немодифицированный прототип?


Мой случай: У меня есть код, который не работает в режиме редактирования (ужасный, запатентованный, закрытый источник...), потому что javascript для интерфейс режима "редактирования" системы управления контентом взламывает абсолютный живый ад из прототипа Array.

Мой код будет работать в режиме без редактирования CMS, но, чтобы попасть туда, он был протестирован в режиме редактирования. Возможно проверить, был ли изменен прототип. Возможно ли повторить реализацию прототипа Array по умолчанию, чтобы я мог сделать что-то вроде этого:

var hasArrayBeenTrashed = // boolean based on /info/538819/javascript-check-if-method-prototype-has-been-changed
var normalArray.prototype = // based on answer to this question 
var myArray = !hasArrayBeenTrashed ? [] : new normalArray;

Ответ 1

OP, вероятно, уже что-то выдумал, но для любого другого, кто пришел из поиска Google или где бы то ни было, здесь функция, которая возвращает немодифицированную версию любого конструктора по умолчанию, переданную ей:

// 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];
}

Использование происходит следующим образом:

Проблема

Кто-то делает что-то глупое для прототипа по умолчанию...

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

... и ваш код становится неисправным.

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!?

Решение

Итак, вы бросаете эту функцию в микс...

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];
}

... и поместите его таким образом.

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 supposed to...
myArray;
//-> [0, 1, 2, 3, 4, 5]
    // ...and all right with the world again!

Кроме того, поскольку каждый конструктор reset кэшируется при первом его возврате, вы можете сохранить символ, если хотите, напрямую ссылаясь на кеш (reset.Array), а не через функцию (reset(Array)) каждый раз после этого.


Удачи!

Ответ 2

Вы можете просто скопировать методы массива из iframe:

Array.prototype.slice = function() {
    return "trololol";
};
var a = document.createElement("iframe");
a.src = "about:blank";
document.body.appendChild(a);
var prototype = a.contentWindow.Array.prototype;
var fn = ["toString", "toLocaleString", "join", "pop", "push", "concat", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach", "some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight"];
for (var i = 0; i < fn.length; ++i) {
    var methodName = fn[i];
    var method = prototype[methodName];
    if (method) {
        Array.prototype[methodName] = method;
    }
}
document.body.removeChild(a);

Здесь jsfiddle, который работает в chrome и IE9, не успевает разобраться в IE7-8. http://jsfiddle.net/jMUur/1/

Чтобы проверить, является ли объект массивом, не полагаясь на ссылки:

function isArray( obj ) {
     return {}.toString.call( obj ) === "[object Array]";
}