Javascript-форум (https://javascript.ru/forum/)
-   Оффтопик (https://javascript.ru/forum/offtopic/)
-   -   Обсуждений тред (https://javascript.ru/forum/offtopic/47364-obsuzhdenijj-tred.html)

nerv_ 31.03.2015 18:39

Array Like Objects
 
Как правильно и дешево идентифицировать массиво-подообные объекты?
Очевидно, что следующей проверки мало:
var array = [1];
var arrayLike = {0:1, length:1};
var fakeArrayLike = {length:1, height:2};
var window = window;
var fn = function() {};

alert(isArrayLike(array)); // true
alert(isArrayLike(arrayLike)); // true
alert(isArrayLike(fakeArrayLike)); // true | must be false
alert(isArrayLike(window)); // true | must be false
alert(isArrayLike(fn)); // false | must be false
// -------------------

/**
 * @param {*} any
 * @returns {Boolean}
 */
function isArrayLike(any) {
    return isObject(any) && any.hasOwnProperty('length');
}

/**
 * @param {*} any
 * @returns {Boolean}
 */
function isObject(any) {
    return any !== null && typeof any === 'object';
}

Тут написано, что эти объекты должны иметь метод splice, но по факту они его не имеют:
var collection = document.getElementsByTagName('body');
alert(collection.splice);
alert(collection.length);


ангуляр
/**
 * @private
 * @param {*} obj
 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
 *                   String ...)
 */
function isArrayLike(obj) {
  if (obj == null || isWindow(obj)) {
    return false;
  }

  var length = obj.length;

  if (obj.nodeType === 1 && length) {
    return true;
  }

  return isString(obj) || isArray(obj) || length === 0 ||
         typeof length === 'number' && length > 0 && (length - 1) in obj;
}

жуквери
function isArraylike( obj ) {
	var length = obj.length,
		type = jQuery.type( obj );

	if ( type === "function" || jQuery.isWindow( obj ) ) {
		return false;
	}

	if ( obj.nodeType === 1 && length ) {
		return true;
	}

	return type === "array" || length === 0 ||
		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}


Глядя на все это безобразие, кажется, что нет способа точно определить массиво-подобный объект. Так ли это? :)

МаксимкаНевозбанный 31.03.2015 18:54

function likeArray(item) {

  if( !('length' in item) ) return false;

  for(var index = 0; index < item.length; index++){
    if( !(index in item) ) return false;
  }

  return true;
}



:victory: илита

Octane 31.03.2015 19:12

Цитата:

Сообщение от МаксимкаНевозбанный
function likeArray(item) {

  if( !('length' in item) ) return false;

  for(var index = 0; index < item.length; index++){
    if( !(index in item) ) return false;
  }

  return true;
}

илита

значит [1,,2] нифига не похоже на массив, да?

МаксимкаНевозбанный 31.03.2015 19:13

Цитата:

Сообщение от Octane
да?

да, в массиве не должно быть прерываний иначе итерации провалятся, в этом и суть проверки на массивоподобность. ваш кэп

к примеру

arr = [];
arr.length = 100;


тоже не считается массивоподобным я осознанно это сделал хотя мог бы проверять чисто (item.length-1 in item)

Erolast 31.03.2015 19:13

Цитата:

Глядя на все это безобразие, кажется, что нет способа точно определить массиво-подобный объект. Так ли это?
Symbol.iterator in object

?

nerv_ 31.03.2015 19:17

МаксимкаНевозбанный,
Цитата:

Сообщение от nerv_
Как правильно и дешево идентифицировать массиво-подообные объекты?

forEach я и сам могу, но я это делаю уже после идентификации, что более оправданно, нежели для нее
Цитата:

Сообщение от МаксимкаНевозбанный
илита

говоришь :)
var object = {'12.24': 1, foo: NaN, length: 1};
      
alert(likeArray(object));
      
function likeArray(item) {

  if( !('length' in item) ) return false;

  var keys = Object.keys(item);
  var indexCnt = 0;

  for(var i = 0; i < keys.length; i++){
    var key = keys[i]
    if(+key != key) continue;
    indexCnt++
  }

  return indexCnt === item.length;
}

nerv_ 31.03.2015 19:24

Цитата:

Сообщение от Erolast
Symbol.iterator in object

alert(Symbol.iterator in document.getElementsByTagName('body'));
alert(Symbol.iterator in document.querySelectorAll('body'));

Erolast 31.03.2015 19:30

NodeList.prototype[Symbol.iterator] = function*() {
    for (let i = 0; i < this.length; i++) {
        yield this[i];
    }
}

МаксимкаНевозбанный 31.03.2015 19:31

nerv_, я уже изменил свой камент) удаляй свой

МаксимкаНевозбанный 31.03.2015 19:33

function likeArray(item) {
  if( item == null ) return false;
  if( item === window ) return false;
  if( !('length' in item) ) return false;
  if( typeof item === 'function' ) return false;
  if( item.length === 0 ) return true;
  return ( item.length - 1 ) in item
}



вот самый крутой способ и самый быстрый


Часовой пояс GMT +3, время: 08:11.