15

一般的な質問: Array のようなデフォルトの Javascript プロトタイプが変更、ハッキング、変更、ねじれられて使用できなくなった場合、変更されていない元のプロトタイプのインスタンスを作成 (または再実装) する方法はありますか?


私の場合:コンテンツの「編集」モードのインターフェース用の JavaScript が原因で、(恐ろしい、独自の、クローズド ソースの) コンテンツ管理システムの「編集」モードで失敗するコードがいくつかあります。管理システムは、Arrayプロトタイプから絶対的な生き地獄をハッキングします。

私のコードは CMS の非編集モードで動作しますが、そこに到達するために「編集」モードでテストされています。プロトタイプが変更されているかどうかをテストすることができます。デフォルトの 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;
4

2 に答える 2

21

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

また、各リセット コンストラクターは最初に返されたときにキャッシュされるため、必要に応じて、その後は毎回reset.Array関数 ( ) を使用する代わりにキャッシュを直接参照 ( ) することで、文字を保存できます。reset(Array)


幸運を!

于 2013-02-28T23:55:38.370 に答える
6

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]";
}
于 2012-12-21T12:48:03.187 に答える