Как очистить массив, как кросс-браузер (IE8)?

У меня есть объект, который похож на массив. Это означает, что он имеет числовые свойства (0, 1, 2...) и length имущество.

В его прототипе я объявляю метод для его очистки, например:

'clear': function() {
    Array.prototype.splice.call(this, 0, this.length);
    return this;
}

Это работает, как и ожидалось, в большинстве браузеров, но не в Internet Explorer.

Возьмите совершенно правильный объект, похожий на массив:

var arrayLike = {
    length: 3,
    '0': 'a',
    '1': 'b',
    '2': 'c'
};

И склеить это ясно:

Array.prototype.splice.call(arrayLike, 0, arrayLike.length);

В браузере, соответствующем стандартам, это правильный результат:

arrayLike.length ==     0    ;
arrayLike[0]     == undefined;
arrayLike[1]     == undefined;
arrayLike[2]     == undefined;

Но в IE 8 это то, что вы получаете:

arrayLike.length ==  0 ; // (!!)
arrayLike[0]     == 'a';
arrayLike[1]     == 'b';
arrayLike[2]     == 'c';

Да, он даже не работает или не работает, он работает столько, сколько хочет.

Теперь я думаю Array.prototype.splice.call это единственный способ изначально очистить массивоподобный объект. Возможно, я мог бы заполнить его в закрытой копии условно для IE8 (возможно, одноразовый удар по производительности для других браузеров, но только один раз).

Или, может быть, есть другой способ очистки объекта? Это тоже родное в IE8? Есть что-то, чего я не вижу?

Ps: JsFiddle для вас, чтобы работать в IE8.

3 ответа

IE запутался... Кажется, даже если вы расширяете объект массива, IE запутывает соединение. Смотрите мое тестирование ниже:

// create an object constructor
var arrayLike = function() {
    this.clear = function() {
        this.splice( 0, this.length );
    };
};
// use prototypical inheritance to inherit array functionality
arrayLike.prototype = [];
arrayLike.constructor = arrayLike;

// testing clear method fails...
var arrayLikeObject = new arrayLike();
arrayLikeObject.push( 'foo' );
console.log( arrayLikeObject[ 0 ] ); // outputs foo
arrayLikeObject.clear();
console.log( arrayLikeObject[ 0 ] ); // outputs foo

// yet the same method works on a normal array
var test = [];
test.push( 'foo2' );
test.splice( 0, test.length );
console.log( test[0] ); // outputs undefined

какого черта IE...

Я чувствую, что вы не должны использовать Array.prototype.splice.call на объекте, который не является Array , Мне это кажется хакерским.

Да, это, вероятно, работает в большинстве браузеров, но вы пытаетесь выполнить операцию, предназначенную для одного прототипа, над объектом, который не принадлежит этому прототипу. Я не фанат IE, но это выглядит для меня как хакерство, так что вы не можете заплакать, когда это не сработает.

Мой 2¢ либо создать новый " Array как объект Array в качестве прототипа, пример:

var ArrayLike = function(){
    this.push.apply(this, arguments);
};
ArrayLike.prototype = Array.prototype;
ArrayLike.constructor = function(){};
ArrayLike.prototype.clear = function(){
    // Array.prototype.splice.call(this, 0, this.length);
    this.splice(0, this.length); // all of Array's methods are available to ArrayLike
    return this;
}

var arrayLike = new ArrayLike('a', 'b', 'c');
console.log(arrayLike.length);  // 3
arrayLike.clear();              // clear
console.log(arrayLike.length);  // 0

... или напишите свой собственный метод.

Чувак, это смешно. Array.prototype.splice очень странно ломается в IE8.

Я должен показать вам это (пропустите, если хотите, решение ниже).

Как вы уже видели, это предупредит "а" в IE, верно?

// Create an array-like object.
var arrayLike = {
    length: 1,
    '0': 'a'
};

// Clean it with splice. This should work in ES5.
Array.prototype.splice.call(arrayLike, 0, 2);

// Note that IE does set the length to zero. We need to check an item.
alert(arrayLike[0]);

jsfiddle.

А что если мы установим недопустимую длину? Специально для этого эффекта "3" ("2" даст те же результаты в IE8).

Теперь, что это предупреждение в IE8?

var arrayLike = {
    length: 3,      // <--
    '0': 'a'
};

Array.prototype.splice.call(arrayLike, 0, 2);

alert(arrayLike[0]);

jsfiddle.

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

Во-первых, у вас есть ваш код, завернутый в право IIFE? Вам следует. Во-вторых, проверьте, что у вас есть личная копия Array.prototype.splice, Я объявляю эти вещи в начале моего IIFE.

// Call it whatever you want.
var protoSplice = Array.prototype.splice;

Затем в каком-либо методе инициализации (или в любое удобное для вас время, убедитесь, что он есть до его использования), вызовите метод polyfill.

polyfill();

Который вызывает тесты и исправления.

function polyfill() {
    // ...
    genericSpliceWorks() || fixGenericSplice();
    // ...
}

Вот методы, которые он использует:

function genericSpliceWorks() {

    // Create an array-like object.
    var arrayLike = {
    length: 1,
    '0': true
    };

    // Clean it with splice. This should work in ES5.
    protoSplice.call(arrayLike, 0, 2);

    // Note that IE does set the length to 0. We need to check an item.
    return !arrayLike[0];

}

function fixGenericSplice() {

    // Re-set our local protoSplice variable to something that works.
    // Note: this implementation only works with the first two arguments
    // of splice. This means that it does not add extra elements to the
    // array. It's as much as I need.
    protoSplice = function(index, count) {

        // If count is more than zero, run until it's zero, decrementing
        // each time.
        while (count--) {

            // Remove an array item from index, while incrementing index
            // for the next time.
            delete this[index++];

            // Decrement the length.
            this.length--;

        }

    };

}

Та-да! Теперь работает:)

Другие вопросы по тегам