【问题标题】:Create a reset of javascript Array prototype when Array.prototype has been modified?修改 Array.prototype 后创建 javascript Array 原型的重置?
【发布时间】:2012-12-09 00:52:42
【问题描述】:

一般问题:当像 Array 这样的默认 Javascript 原型被修改、破解、更改和扭曲到无法使用的程度时,有没有办法创建(或重新实现)的实例原始的、未修改的原型?


我的情况:我有一些代码在(可怕的、专有的、封闭源代码的......)内容管理系统的“编辑”模式下失败,因为用于内容管理系统的“编辑”模式界面破解了Array原型的绝对生活地狱。

我的代码可以在 CMS 的非编辑模式下工作,但是,为了实现这一点,它已经在“编辑”模式下进行了测试。 It's possible to test if a prototype has been modified。是否可以重新实现默认的 Array 原型,以便我可以执行以下操作:

var hasArrayBeenTrashed = // boolean based on https://stackoverflow.com/questions/574584/
var normalArray.prototype = // based on answer to this question 
var myArray = !hasArrayBeenTrashed ? [] : new normalArray;

【问题讨论】:

  • 您始终可以创建一个空的iframe 来创建新的 JavaScript 上下文并从那里获取对未修改结构的引用。但是有些东西,比如arr instanceof Array,会失败。我想以前有人问过类似的问题,会尝试找到它。
  • 在您需要的环境中可能没有太多用处,但您可以利用 WebWorker 创建新上下文,在安全环境中执行操作,然后将结果发布回来。
  • @FelixKling 好主意!我会试试看。我确实需要arr instanceof Array,但如果我只是将Array 克隆为normalArray,即使它没有被修改,我仍然可以使用arr instanceof normalArray?我确实首先寻找受骗者,所以如果有的话,他们可能使用不同的搜索词。

标签: javascript arrays object prototype


【解决方案1】:

OP 现在可能已经想通了,但是对于从 Google 搜索或其他任何地方进入的任何其他人,这里有一个函数返回传递给它的 any 默认构造函数的未修改版本:

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

另外,因为每个重置构造函数在第一次返回时都会被缓存,如果你想通过直接引用缓存(reset.Array)而不是通过函数(reset(Array))来保存一个字符之后的时间。


祝你好运!

【讨论】:

  • 这个重置函数会抛出错误 IE 10,11 中预期的函数
  • 我正在覆盖Promise 构造函数的页面上尝试此操作,我尝试在Chrome 中运行reset(Promise),它告诉我Uncaught TypeError: Promise is not a constructor
【解决方案2】:

你可以从 iframe 中复制 Array 方法:

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);

​ 这是适用于chrome和IE9的jsfiddle,没有时间弄清楚IE7-8。 http://jsfiddle.net/jMUur/1/

不依赖引用检查对象是否为数组:

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

【讨论】:

  • 看起来不错 - 但我将如何修改它以创建基于数组的新型对象,而不是覆盖数组?我想撤消 CMS 的 Array 原型黑客攻击会破坏 CMS - 假设我想使用基于 Array 的新类 normalArray,而不是 Array.prototype[methodName] = method; 行中的 Array?谢谢
  • 我意识到这与问题的标题有点不同,编辑以更好地反映意图。
  • @user568458 不太好用,在某些浏览器中,iframed 数组仍会返回主窗口的数组实例,如方法 .slice
猜你喜欢
  • 1970-01-01
  • 2017-11-16
  • 2015-12-16
  • 1970-01-01
  • 2020-04-24
  • 2019-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多