2012-10-30 2 views
4

Есть ли библиотека JavaScript JavaScript, которая нормализует возвращаемые значения и мутации массива? Я думаю, что JavaScript Array API очень непоследователен.Нормализовать методы массива и возвращаемые значения

Некоторых методов мутировать массив:

var A = [0,1,2]; 
A.splice(0,1); // reduces A and returns a new array containing the deleted elements 

Некоторые из них не:

A.slice(0,1); // leaves A untouched and returns a new array 

Некоторые возвращают ссылку на видоизмененном массив:

A = A.reverse().reverse(); // reverses and then reverses back 

Некоторые просто вернуться неопределенным:

B = A.forEach(function(){}); 

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

A.slice(0,1).reverse().forEach(function(){}).concat(['a','b']); 

Я попробовал несколько простых фрагментов, как:

var superArray = function() { 
    this.length = 0; 
} 

superArray.prototype = { 
    constructor: superArray, 

    // custom mass-push method 
    add: function(arr) { 
     return this.push.apply(this, arr); 
    } 
} 

// native mutations 
'join pop push reverse shift sort splice unshift map forEach'.split(' ').forEach(function(name) { 
    superArray.prototype[name] = (function(name) { 
     return function() { 
      Array.prototype[name].apply(this, arguments); 
      // always return this for chaining 
      return this; 
     }; 
    }(name)); 
}); 

// try it 
var a = new superArray(); 
a.push(3).push(4).reverse(); 

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

Как всегда, когда я делал это, я думал, что, возможно, это было сделано раньше? Есть ли какие-либо облегченные библиотеки массивов, которые уже делают это? Было бы неплохо, если бы библиотека также добавила прокладки для новых JavaScript 1.6 для старых браузеров.

+0

Пример, который вы предоставили - 'A.slice (0,1) .reverse() Foreach (функция() {});' - работает, как ожидалось.. Я не уверен, какова ваша точка с этим примером. Вы заявляете, что хотели бы иметь возможность цепочки, а затем вы приводите пример с цепочкой с использованием существующих методов, который работает. Просьба привести примеры, которые * не * работают с существующими методами, чтобы я мог понять, что вы могли бы написать. –

+0

@ ŠimeVidas нет, потому что 'slice' не мутирует массив, как я хочу, - он возвращает новый массив. Я могу привести больше примеров, если это поможет – David

+0

Может быть, underscore.js? –

ответ

2

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

  • список методы:
    • push/unshift возвращают длину после добавления элементов
    • pop/shift возвращения запрашиваемого элемента
    • можно определить дополнительные методы получения первого и последнего элемента, но они редко необходимы
  • splice - универсальный инструмент для удаления/замены/вставки элементов в середине списка - он возвращает массив удаленных элементов.
  • sort и reverse - два стандартных метода переупорядочения на месте.

Все остальные методы не изменяют исходный массив:

  • slice получить подмассив положения, filter, чтобы получить их состояния и concat сочетать с другими создавать и возвращать новые массивы
  • forEach просто выполняет итерацию массива и ничего не возвращает
  • every/some проверить элементы состояния, indexOf и lastIndexOf se arch для элементов (по принципу равенства) - оба возвращают свои результаты
  • reduce/reduceRight уменьшают элементы массива до одного значения и возвращают их. Особые случаи:
    • map сводится к новый массив - это как forEach но возвращающие результаты
    • join и toString сводятся к строке

Эти методы являются достаточно для большинства наши потребности. Мы можем делать с ними все, и я не знаю никаких библиотек, которые бы добавляли к ним похожие, но внутренние или результирующие методы. Большинство библиотек обработки данных (например, Underscore) делают их только кросс-браузерными (es5-shim) и предоставляют дополнительные полезные методы.

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

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

Chaining еще можно с этим, вы можете использовать slice, concat, sort, reverse, filter и map вместе, чтобы создать новый массив, только один шаг. Если вы хотите, чтобы «изменить» только массив, вы можете просто передать его в переменную массива:

A = A.slice(0,1).reverse().concat(['a','b']); 

методы Мутации имеют только одно преимущество для меня: они быстрее, потому что они могут быть более эффективно использует память (зависит на реализацию и сборку мусора, конечно). Поэтому позволяет реализовать некоторые методы для них. Как Array subclassing является ни возможным, ни полезным, я определю их на native prototype:

var ap = Array.prototype; 
// the simple ones: 
ap.each = function(){ ap.forEach.apply(this, arguments); return this; }; 
ap.prepend = function() { ap.unshift.apply(this, arguments); return this; }; 
ap.append = function() { ap.push.apply(this, arguments; return this; }; 
ap.reversed = function() { return ap.reverse.call(ap.slice.call(this)); }; 
ap.sorted = function() { return ap.sort.apply(ap.slice.call(this), arguments); }; 
// more complex: 
ap.shorten = function(start, end) { // in-place slice 
    if (Object(this) !== this) throw new TypeError(); 
    var len = this.length >>> 0; 
    start = start >>> 0; // actually should do isFinite, then floor towards 0 
    end = typeof end === 'undefined' ? len : end >>> 0; // again 
    start = start < 0 ? Math.max(len + start, 0) : Math.min(start, len); 
    end = end < 0 ? Math.max(len + end, 0) : Math.min(end, len); 
    ap.splice.call(this, end, len); 
    ap.splice.call(this, 0, start); 
    return this; 
}; 
ap.restrict = function(fun) { // in-place filter 
    // while applying fun the array stays unmodified 
    var res = ap.filter.apply(this, arguments); 
    res.unshift(0, this.length >>> 0); 
    ap.splice.apply(this, res); 
    return this; 
}; 
ap.transform = function(fun) { // in-place map 
    if (Object(this) !== this || typeof fun !== 'function') throw new TypeError(); 
    var len = this.length >>> 0, 
     thisArg = arguments[1]; 
    for (var i=0; i<len; i++) 
     if (i in this) 
      this[i] = fun.call(thisArg, this[i], i, this) 
    return this; 
}; 
// possibly more 

Теперь вы можете сделать

A.shorten(0, 1).reverse().append('a', 'b'); 
0

Вы, вероятно, не следует использовать библиотеку только для этого (это not- так полезная зависимость для добавления в проект).

«Стандартным» способом является вызов slice, когда вы хотите выполнить операции мутанта.Это не проблема, потому что JS-двигатели довольно хороши с временными переменными (так как это один из ключевых моментов javascript).

Ex. :

function reverseStringify(array) { 
    return array.slice() 
     .reverse() 
     .join(' '); } 

console.log([ 'hello', 'world' ]); 
Смежные вопросы