Normalizza i metodi Array e restituisce i valori

Esiste una libreria di array JavaScript che normalizzi i valori e le mutazioni di ritorno dell’array? Penso che l’API dell’array JavaScript sia molto incoerente.

Alcuni metodi mutano l’array:

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

Alcuni non:

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

Alcuni restituiscono un riferimento all’array mutato:

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

Alcuni restituiscono undefined:

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

    Quello che mi piacerebbe è di mutare sempre l’array e restituire sempre lo stesso array, così posso avere una certa consistenza e anche essere in grado di fare catena. Per esempio:

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

    Ho provato alcuni frammenti semplici come:

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

    Questo funziona bene per la maggior parte dei metodi di mutazione, ma ci sono problemi. Ad esempio, ho bisogno di scrivere prototipi personalizzati per ogni metodo che non muta l’array originale.

    Quindi, come sempre mentre stavo facendo questo, stavo pensando che forse questo è stato fatto prima? Ci sono delle librerie di array leggere che lo fanno già? Sarebbe bello se la libreria aggiungesse anche shim per i nuovi metodi JavaScript 1.6 per i browser più vecchi.

    Non penso che sia davvero incoerente. Sì, potrebbero essere un po ‘di confusione poiché gli array JavaScript fanno tutte le cose per le quali gli altri linguaggi hanno strutture separate (elenco, coda, stack, …), ma la loro definizione è abbastanza coerente in tutte le lingue. Puoi raggrupparli facilmente in quelle categorie che hai già descritto:

    • elenca i metodi:
      • push / unshift restituisce la lunghezza dopo aver aggiunto gli elementi
      • pop / shift restituisce l’elemento richiesto
      • potresti definire metodi aggiuntivi per ottenere il primo e l’ultimo elemento, ma raramente sono necessari
    • splice è lo strumento multiuso per rimuovere / sostituire / inserire oggetti nel mezzo dell’elenco – restituisce un array degli elementi rimossi.
    • sort e reverse i due metodi standard di riordino sul posto.

    Tutti gli altri metodi non modificano la matrice originale:

    • slice per ottenere subarray per posizione, filter per ottenerli in base alle condizioni e concat per combinarli con gli altri per creare e restituire nuovi array
    • forEach semplicemente l’array e non restituisce nulla
    • every / some test gli articoli per una condizione, lastIndexOf e lastIndexOf cercano elementi (per uguaglianza) – entrambi restituiscono i loro risultati
    • reduce / reduceRight gli elementi dell’array ad un singolo valore e restituiscilo. Casi speciali sono:
      • map riduce a un nuovo array – è come forEach ma restituisce i risultati
      • join e toString riducono a una stringa

    Questi metodi sono sufficienti per la maggior parte dei nostri bisogni. Possiamo fare tutto con loro, e non conosco librerie che aggiungano metodi simili, ma internamente o di risultato per loro. La maggior parte delle librerie per la gestione dei dati (come Underscore ) le rende solo sicure da browser ( es5-shim ) e forniscono metodi di utilità aggiuntivi.

    Quello che mi piacerebbe è di mutare sempre l’array e restituire sempre lo stesso array, così posso avere una certa consistenza e anche essere in grado di fare catena.

    Direi che la coerenza di JavaScript è sempre di restituire un nuovo array quando gli elementi o la lunghezza vengono modificati. Immagino che questo sia dovuto al fatto che gli oggetti sono valori di riferimento, e cambiarli troppo spesso causerebbe effetti collaterali in altri ambiti che fanno riferimento allo stesso array.

    Il concatenamento è ancora ansible, è ansible utilizzare slice , concat , sort , reverse , filter e map insieme per creare un nuovo array in un solo passaggio. Se si desidera “modificare” solo la matrice, è sufficiente riassegnarla alla variabile dell’array:

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

    I metodi di mutazione hanno solo un vantaggio per me: sono più veloci perché potrebbero essere più efficienti in termini di memoria (dipende dall’implementazione e dalla sua garbage collection, ovviamente). Quindi, implementiamo alcuni metodi per quelli. Poiché la sottoclass di array non è né ansible né utile, le definirò sul prototipo nativo :

     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 

    Ora potresti farlo

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

    IMHO una delle migliori librerie è underscorejs http://underscorejs.org/

    Probabilmente non dovresti usare una libreria solo per questo (è una dipendenza non così utile da aggiungere al progetto).

    Il modo “standard” per fare è chiamare slice quando si vogliono eseguire operazioni mutate. Non c’è alcun problema a fare questo, dal momento che i motori JS sono abbastanza buoni con variabili temporanee (poiché questo è uno dei punti chiave del javascript).

    Ex. :

     function reverseStringify( array ) { return array.slice( ) .reverse( ) .join( ' ' ); } console.log( [ 'hello', 'world' ] );