// Soulignement.js 1.3.3
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore est librement distribuable sous licence MIT.
// Des portions de Underscore sont inspirées ou empruntées à Prototype,
// Functional d'Oliver Steele et Micro-Templating de John Resig.
// Pour tous les détails et la documentation :
// http://documentcloud.github.com/underscore
(fonction() {
// Crée un objet global, représenté comme un objet fenêtre dans le navigateur et un objet global dans Node.js
var racine = ceci ;
//Enregistre la valeur avant que "_" (variable de trait de soulignement) ne soit écrasé
// S'il y a un conflit de nom ou compte tenu de la spécification, la méthode _.noConflict() peut être utilisée pour restaurer la valeur de "_" avant qu'il ne soit occupé par Underscore, et renvoyer l'objet Underscore pour le renommer
var précédentUnderscore = root._;
//Créer une constante d'objet vide pour le partage et l'utilisation internes
var disjoncteur = {};
//Cache la chaîne de prototypes de l'objet intégré dans une variable locale pour faciliter les appels rapides
var ArrayProto = Array.prototype, //
ObjProto = Objet.prototype, //
FuncProto = Fonction.prototype ;
//Cache les méthodes courantes dans les prototypes d'objets intégrés dans les variables locales pour un appel rapide
var tranche = ArrayProto.slice, //
unshift = ArrayProto.unshift, //
toString = ObjProto.toString, //
hasOwnProperty = ObjProto.hasOwnProperty;
// Ceci définit quelques nouvelles méthodes fournies par JavaScript 1.6
// Si l'environnement hôte prend en charge ces méthodes, elles seront appelées en premier. Si l'environnement hôte ne les fournit pas, elles seront implémentées par Underscore.
var nativeForEach = ArrayProto.forEach, //
nativeMap = ArrayProto.map, //
nativeReduce = ArrayProto.reduce, //
nativeReduceRight = ArrayProto.reduceRight, //
nativeFilter = ArrayProto.filter, //
nativeEvery = ArrayProto.every, //
nativeSome = ArrayProto.some, //
nativeIndexOf = ArrayProto.indexOf, //
nativeLastIndexOf = ArrayProto.lastIndexOf, //
nativeIsArray = Array.isArray, //
nativeKeys = Objet.keys, //
nativeBind = FuncProto.bind;
// Crée une méthode d'appel de style objet, qui renverra un wrapper Underscore. Le prototype de l'objet wrapper contient toutes les méthodes de Underscore (similaire à l'encapsulation d'un objet DOM dans un objet jQuery).
var _ = fonction (obj) {
// Tous les objets Underscore sont construits en interne via des objets wrapper
renvoie un nouveau wrapper (obj);
} ;
// Pour différents environnements hôtes, stockez les variables nommées d'Undersocre dans différents objets.
if( typeof exports !== 'undefined') {// Environnement Node.js
if( type de module !== 'undéfini' && module.exports) {
exports = module.exports = _;
}
exportations._ = _;
} else {//La variable nommée de Underscore dans l'environnement du navigateur est accrochée dans l'objet window
racine['_'] = _;
}
// instruction de version
_.VERSION = '1.3.3';
// Méthodes liées aux collections (méthodes générales de traitement des données et des objets)
//--------------------
// Itère le processeur et exécute la méthode du processeur sur chaque élément de la collection
var each = _.each = _.forEach = function(obj, itérateur, contexte) {
//Ne gère pas les valeurs nulles
si(obj == nul)
retour;
if(nativeForEach && obj.forEach === nativeForEach) {
// Si l'environnement hôte le prend en charge, la méthode forEach fournie par JavaScript 1.6 sera appelée en premier.
obj.forEach(itérateur, contexte);
} sinon if(obj.length === obj.length) {
//Exécuter la méthode du processeur pour chaque élément du <array>
pour (var i = 0, l = obj.length; i < l; i ) {
if( i in obj && iterator.call(context, obj[i], i, obj) === disjoncteur)
retour;
}
} autre {
// Exécute la méthode du processeur pour chaque élément de <object>
pour (clé var dans obj) {
if(_.has(obj, clé)) {
if(iterator.call(context, obj[key], key, obj) === disjoncteur)
retour;
}
}
}
} ;
// Processeur d'itération, la différence avec chaque méthode est que map stockera la valeur de retour de chaque itération et la renverra sous forme d'un nouveau tableau
_.map = _.collect = function(obj, itérateur, contexte) {
//Tableau utilisé pour stocker les valeurs de retour
var résultats = [];
si(obj == nul)
renvoyer les résultats ;
// Priorité à l'appel de la méthode map fournie par l'environnement hôte
if(nativeMap && obj.map === nativeMap)
return obj.map (itérateur, contexte);
// Parcourir les éléments de la collection
chacun (obj, fonction (valeur, index, liste) {
// Stocke la valeur de retour de chaque itération dans le tableau de résultats
results[results.length] = iterator.call(contexte, valeur, index, liste);
});
//Renvoyer les résultats du traitement
si (obj.longueur === obj.longueur)
résultats.longueur = obj.longueur;
renvoyer les résultats ;
};
// Place chaque élément de la collection dans le processeur d'itération, et passe la valeur de retour de cette itération comme "mémo" à l'itération suivante, généralement utilisée pour accumuler des résultats ou connecter des données
_.reduce = _.foldl = _.inject = function(obj, itérateur, mémo, contexte) {
// Vérifiez s'il existe une valeur initiale par le nombre de paramètres
var initial = arguments.longueur >
si(obj == nul)
obj = [];
// Priorité à l'appel de la méthode de réduction fournie par l'environnement hôte
if(nativeReduce && obj.reduce === nativeReduce && false) {
si (contexte)
itérateur = _.bind(itérateur, contexte);
return initial ? obj.reduce(iterator, mémo) : obj.reduce(iterator);
}
// Parcourir les éléments de la collection
chacun (obj, fonction (valeur, index, liste) {
si(!initial) {
// S'il n'y a pas de valeur initiale, le premier élément est utilisé comme valeur initiale ; si la collection d'objets est traitée, la valeur par défaut est la valeur du premier attribut.
mémo = valeur ;
initiale = vrai ;
} autre {
// Enregistre les résultats du traitement et transmet les résultats à l'itération suivante
memo = iterator.call (contexte, mémo, valeur, index, liste);
}
});
si(!initial)
throw new TypeError('Réduction du tableau vide sans valeur initiale');
renvoyer le mémo ;
} ;
// Semblable à réduire, il itérera les éléments de la collection dans le sens inverse (c'est-à-dire en commençant du dernier élément au premier élément)
_.reduceRight = _.foldr = function (obj, itérateur, mémo, contexte) {
var initial = arguments.longueur >
si(obj == nul)
obj = [];
// Priorité à l'appel de la méthode réduireRight fournie par l'environnement hôte
if(nativeReduceRight && obj.reduceRight === nativeReduceRight) {
si (contexte)
itérateur = _.bind(itérateur, contexte);
return initial ? obj.reduceRight(iterator, mémo) : obj.reduceRight(iterator);
}
//Inverser l'ordre des éléments dans la collection
var inversé = _.toArray(obj).reverse();
si(contexte && !initial)
itérateur = _.bind(itérateur, contexte);
// Traiter les données via la méthode de réduction
return initial ? _.reduce(inversé, itérateur, mémo, contexte) : _.reduce(inversé, itérateur);
} ;
// Parcourt les éléments de la collection et renvoie le premier élément pouvant passer la vérification du processeur
_.find = _.detect = function (obj, itérateur, contexte) {
// le résultat stocke le premier élément pouvant réussir la vérification
résultat var ;
// Parcourez les données via la méthode any et enregistrez les éléments qui réussissent la vérification
// (Si vous vérifiez l'état de retour du processeur pendant l'itération, il serait plus approprié d'utiliser la méthode each ici)
any(obj, fonction(valeur, index, liste) {
// Si le résultat renvoyé par le processeur est converti en type booléen et que la valeur est vraie, alors l'élément courant est enregistré et renvoyé
if(iterator.call(contexte, valeur, index, liste)) {
résultat = valeur ;
renvoie vrai ;
}
});
renvoyer le résultat ;
} ;
// Similaire à la méthode find, mais la méthode filter enregistrera tous les éléments vérifiés dans la collection
_.filter = _.select = fonction (obj, itérateur, contexte) {
// Utilisé pour stocker un tableau d'éléments qui réussissent la validation
var résultats = [];
si(obj == nul)
renvoyer les résultats ;
// Donner la priorité à l'appel de la méthode de filtrage fournie par l'environnement hôte
if(nativeFilter && obj.filter === nativeFilter)
return obj.filter (itérateur, contexte);
// Parcourez les éléments de la collection et placez les éléments vérifiés par le processeur dans le tableau et retournez
chacun (obj, fonction (valeur, index, liste) {
if(iterator.call(contexte, valeur, index, liste))
résultats[results.length] = valeur ;
});
renvoyer les résultats ;
} ;
// L'effet inverse de la méthode filter, c'est-à-dire renvoyer une liste d'éléments qui n'ont pas réussi la vérification du processeur
_.reject = fonction (obj, itérateur, contexte) {
var résultats = [];
si(obj == nul)
renvoyer les résultats ;
chacun (obj, fonction (valeur, index, liste) {
if(!iterator.call(contexte, valeur, index, liste))
résultats[results.length] = valeur ;
});
renvoyer les résultats ;
} ;
//Si tous les éléments de la collection peuvent réussir la vérification du processeur, renvoie true
_.every = _.all = function(obj, itérateur, contexte) {
var résultat = vrai ;
si(obj == nul)
renvoyer le résultat ;
// Donner la priorité à l'appel de chaque méthode fournie par l'environnement hôte
if(nativeEvery && obj.every === nativeEvery)
return obj.every (itérateur, contexte);
//Parcourir les éléments de la collection
chacun (obj, fonction (valeur, index, liste) {
// Ceci est compris comme result = (result && iterator.call(context, value, index, list))
// Vérifiez si le résultat du processeur est une vraie valeur après avoir été converti en type booléen
if(!( result = result && iterator.call(context, value, index, list)))
disjoncteur de retour ;
});
retourner !!résultat ;
};
// Vérifier si un élément de la collection a une vraie valeur lorsqu'il est converti en type booléen ? Ou s'il a une vraie valeur après avoir été traité par le processeur ?
var any = _.some = _.any = function (obj, itérateur, contexte) {
// Si aucun paramètre de processeur n'est spécifié, la fonction de processeur par défaut renverra l'élément lui-même et déterminera s'il s'agit d'une vraie valeur en convertissant l'élément en type booléen lors de l'itération.
itérateur || ( itérateur = _.identity);
var résultat = faux ;
si(obj == nul)
renvoyer le résultat ;
// Donner la priorité à l'appel de certaines méthodes fournies par l'environnement hôte
if(nativeSome && obj.some === nativeSome)
return obj.some(itérateur, contexte);
//Parcourir les éléments de la collection
chacun (obj, fonction (valeur, index, liste) {
if(result || ( result = iterator.call(context, value, index, list)))
disjoncteur de retour ;
});
retourner !!résultat ;
} ;
// Vérifiez s'il existe une valeur dans la collection qui correspond exactement au paramètre cible (le type de données correspondra également)
_.include = _.contains = fonction (obj, cible) {
var trouvé = faux ;
si(obj == nul)
retour trouvé ;
// Priorité à l'appel de la méthode Array.prototype.indexOf fournie par l'environnement hôte
if(nativeIndexOf && obj.indexOf === nativeIndexOf)
return obj.indexOf(target) != -1;
// Parcourez les éléments de la collection via la méthode any et vérifiez si la valeur et le type de l'élément correspondent complètement à la cible.
trouvé = n'importe quel (obj, fonction (valeur) {
valeur de retour === cible ;
});
retour trouvé ;
} ;
// Appelez les méthodes du même nom de tous les éléments de la collection dans l'ordre, en commençant par le troisième paramètre, qui sera transmis à la méthode appelante de l'élément.
// Renvoie un tableau qui stocke les résultats du traitement de toutes les méthodes
_.invoke = fonction (obj, méthode) {
// Paramètres passés lors de l'appel de la méthode du même nom (à partir du 3ème paramètre)
var args = tranche.call(arguments, 2);
// Appelez la méthode de chaque élément tour à tour, mettez le résultat dans le tableau et renvoyez-le
return _.map(obj, fonction(valeur) {
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
});
} ;
// Parcourt un tableau composé d'une liste d'objets et renvoie une liste de valeurs pour l'attribut spécifié dans chaque objet
_.pluck = fonction (obj, clé) {
// Si la propriété n'existe pas dans un objet, renvoie undéfini
return _.map(obj, fonction(valeur) {
valeur de retour[clé] ;
});
} ;
//Renvoie la valeur maximale dans la collection, s'il n'y a pas de valeur comparable, renvoie undéfini
_.max = fonction (obj, itérateur, contexte) {
// Si la collection est un tableau et qu'aucun processeur n'est utilisé, utilisez Math.max pour obtenir la valeur maximale
// Généralement, une série de données de type Number est stockée dans un tableau.
if(!iterator && _.isArray(obj) && obj[0] === obj[0])
return Math.max.apply(Math, obj);
// Pour les valeurs nulles, renvoie directement l'infini négatif
if(!iterator && _.isEmpty(obj))
retourner -Infini ;
// Un objet temporaire, calculé est utilisé pour stocker la valeur maximale pendant le processus de comparaison (temporaire)
var résultat = {
calculé : -Infini
} ;
//Parcourir les éléments de la collection
chacun (obj, fonction (valeur, index, liste) {
// Si le paramètre processeur est spécifié, les données comparées sont la valeur renvoyée par le processeur, sinon la valeur par défaut lors de chaque parcours est utilisée directement.
var calculé = itérateur ? iterator.call(contexte, valeur, index, liste) : valeur;
// Si la valeur de comparaison est supérieure à la valeur précédente, mettez la valeur actuelle dans result.value
calculé >= résultat.calculé && ( résultat = {
valeur : valeur,
calculé : calculé
});
});
// Renvoie la valeur maximale
renvoyer résultat.valeur ;
} ;
//Renvoie la valeur minimale dans l'ensemble, le processus de traitement est cohérent avec la méthode max
_.min = fonction (obj, itérateur, contexte) {
if(!iterator && _.isArray(obj) && obj[0] === obj[0])
return Math.min.apply(Math, obj);
if(!iterator && _.isEmpty(obj))
retourner l'infini ;
var résultat = {
calculé : Infini
} ;
chacun (obj, fonction (valeur, index, liste) {
var calculé = itérateur ? iterator.call(contexte, valeur, index, liste) : valeur;
calculé < résultat.calculé && ( résultat = {
valeur : valeur,
calculé : calculé
});
});
renvoyer résultat.valeur ;
} ;
// Utilisez des nombres aléatoires pour que le tableau n'ait pas besoin d'être organisé
_.shuffle = fonction(obj) {
// les variables mélangées stockent le processus de traitement et les données du résultat final
var mélangé = [], rand;
//Parcourir les éléments de la collection
chacun (obj, fonction (valeur, index, liste) {
// Génère un nombre aléatoire, le nombre aléatoire est compris entre <0 et le nombre actuellement traité>
rand = Math.floor(Math.random() * (index 1));
// Place les éléments obtenus aléatoirement à la fin du tableau mélangé
mélangé[index] = mélangé[rand];
//Insérer la dernière valeur à la position du nombre aléatoire obtenu précédemment
mélangé[rand] = valeur ;
});
// Renvoie un tableau qui stocke les éléments de collection mélangés aléatoirement.
retour mélangé;
};
// Organise les éléments de la collection en fonction de champs ou de valeurs spécifiques
// Par rapport à la méthode Array.prototype.sort, la méthode sortBy prend en charge le tri des objets.
_.sortBy = fonction (obj, val, contexte) {
// val doit être une propriété de l'objet, ou une fonction de processeur, s'il s'agit d'un processeur, il doit renvoyer les données qui doivent être comparées.
var itérateur = _.isFunction(val) ? val : function(obj) {
retourner obj[val];
} ;
// Séquence d'appel : _.pluck(_.map().sort());
// Appelez la méthode _.map() pour parcourir la collection, placez les éléments de la collection dans le nœud de valeur et placez les données qui doivent être comparées dans les éléments dans l'attribut critères
//Appelez la méthode sort() pour trier les éléments de la collection en fonction des données de l'attribut critères.
// Appelez pluck pour obtenir la collection d'objets triés et la renvoyer
return _.pluck(_.map(obj, function(value, index, list) {
retour {
valeur : valeur,
critères : iterator.call (contexte, valeur, index, liste)
} ;
}).sort(fonction(gauche, droite) {
var a = gauche.critères, b = droite.critères;
si(a ===
vide 0)
renvoyer 1 ;
si(b ===
vide 0)
renvoie -1 ;
retourner un < b -1 : un >
}), 'valeur');
} ;
// Divise les éléments de la collection en plusieurs tableaux selon les clés renvoyées par le processeur
_.groupBy = fonction (obj, val) {
var résultat = {};
// val sera converti en fonction de processeur pour le regroupement. Si val n'est pas une donnée de type fonction, elle sera utilisée comme valeur clé lors du filtrage des éléments.
var itérateur = _.isFunction(val) ? val : function(obj) {
retourner obj[val];
} ;
//Parcourir les éléments de la collection
chacun (obj, fonction (valeur, index) {
// Utilise la valeur de retour du processeur comme clé et place les mêmes éléments clés dans un nouveau tableau
var key = itérateur (valeur, index);
(result[key] || (result[key] = [])).push(value);
});
// Renvoie les données groupées
renvoyer le résultat ;
} ;
_.sortedIndex = fonction (tableau, obj, itérateur) {
itérateur || ( itérateur = _.identity);
var faible = 0, élevé = tableau.longueur ;
tandis que (faible < élevé) {
var mid = (bas haut) >>
itérateur (tableau [milieu]) < itérateur (obj) ? bas = milieu 1 : haut = milieu ;
}
retour bas;
} ;
//Convertit une collection en tableau et renvoie
// Généralement utilisé pour convertir des arguments en tableaux, ou convertir des collections d'objets non ordonnées en collections ordonnées sous forme de données
_.toArray = fonction (obj) {
si(!obj)
retour [];
si(_.isArray(obj))
return slice.call(obj);
//Convertir les arguments en tableau
si(_.isArguments(obj))
return slice.call(obj);
if(obj.toArray && _.isFunction(obj.toArray))
return obj.toArray();
//Convertit l'objet en un tableau contenant la liste de valeurs de toutes les propriétés de l'objet (à l'exclusion des propriétés de la chaîne de prototypes de l'objet)
return _.values(obj);
} ;
// Compte le nombre d'éléments dans la collection
_.size = fonction(obj) {
// Si la collection est un tableau, comptez le nombre d'éléments du tableau
// Si la collection est un objet, comptez le nombre de propriétés dans l'objet (hors propriétés dans la chaîne de prototypes de l'objet)
return _.isArray(obj) ? obj.length : _.keys(obj).length;
} ;
// Méthodes liées aux tableaux
// ---------------
// Renvoie le premier ou les n éléments d'un tableau spécifié dans l'ordre
_.first = _.head = _.take = function(array, n, guard) {
// Si le paramètre n n'est pas spécifié, renvoie le premier élément
// Si n est spécifié, renvoie un nouveau tableau contenant le nombre spécifié de n éléments en séquence
//Le paramètre guard est utilisé pour déterminer que seul le premier élément est renvoyé. Lorsque guard est vrai, le nombre n spécifié n'est pas valide.
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
} ;
//Renvoie un nouveau tableau, contenant d'autres éléments à l'exception du premier élément, ou excluant n éléments spécifiés en commençant par le dernier élément
// La différence avec la première méthode est qu'elle détermine d'abord la position de l'élément requis avant le tableau et initial détermine la position de l'élément exclu à la fin du tableau.
_.initial = fonction (tableau, n, garde) {
// Si le paramètre n n'est pas passé, les autres éléments sauf le dernier élément seront renvoyés par défaut
// Si le paramètre n est passé, d'autres éléments, sauf n éléments en avant à partir du dernier élément, sont renvoyés.
// guard est utilisé pour garantir qu'un seul élément est renvoyé. Lorsque guard est vrai, le nombre n spécifié n'est pas valide.
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
} ;
//Renvoie les n derniers éléments du tableau ou les n éléments spécifiés dans l'ordre inverse
_.last = fonction (tableau, n, garde) {
if((n != null) && !guard) {
// Calcule et spécifie la position n de l'élément obtenu, jusqu'à la fin du tableau, et la renvoie sous la forme d'un nouveau tableau
return slice.call(array, Math.max(array.length - n, 0));
} autre {
// Si le nombre n'est pas spécifié ou si guard est vrai, seul le dernier élément est renvoyé
return array[array.length - 1];
}
};
// Récupère d'autres éléments à l'exception du premier ou des n premiers éléments spécifiés
_.rest = _.tail = fonction (tableau, index, garde) {
// Calcule le deuxième paramètre de position de slice jusqu'à la fin du tableau
// Si l'index n'est pas spécifié ou si la valeur de garde est vraie, renvoie les autres éléments à l'exception du premier élément.
// Lorsque la valeur (index == null) est vraie, le paramètre passé à la fonction slice sera automatiquement converti en 1
return slice.call(array, (index == null) || guard ? 1 : index);
} ;
// Renvoie tous les éléments du tableau dont les valeurs peuvent être converties en vrai, et renvoie un nouveau tableau
// Les valeurs qui ne peuvent pas être converties incluent false, 0, '', null, undefined, NaN, ces valeursseront converties en false
_.compact = fonction (tableau) {
return _.filter (tableau, fonction (valeur) {
retourner !!valeur ;
});
} ;
// Combine un nombre multidimensionnel dans un tableau unidimensionnel, prenant en charge la fusion profonde
// Le paramètre Shallow est utilisé pour contrôler la profondeur de la fusion. Lorsque Shallow est vrai, seul le premier calque est fusionné et la fusion profonde est effectuée par défaut.
_.flatten = fonction (tableau, peu profond) {
// Parcourez chaque élément du tableau et transmettez la valeur de retour en tant que démo à l'itération suivante
return _.reduce (tableau, fonction (mémo, valeur) {
// Si l'élément est toujours un tableau, faites le jugement suivant :
// - Si la fusion approfondie n'est pas effectuée, utilisez Array.prototype.concat pour connecter le tableau actuel et les données précédentes.
// - Si la fusion profonde est prise en charge, la méthode flatten est appelée de manière itérative jusqu'à ce que l'élément sous-jacent ne soit plus un type tableau
si (_.isArray (valeur))
return memo.concat( peu profond ? valeur : _.flatten(valeur));
// Les données (valeur) sont déjà en bas et ne sont plus de type tableau, puis fusionnent les données dans un mémo et renvoient
mémo[mémo.longueur] = valeur ;
renvoyer le mémo ;
}, []);
} ;
// Filtre et renvoie les données de différence dans le tableau actuel qui ne sont pas égales aux données spécifiées (veuillez vous référer au commentaire de la méthode de différence)
_.sans = fonction (tableau) {
return _.difference(array, slice.call(arguments, 1));
} ;
// Dédupliquez les données dans le tableau (utilisez === pour la comparaison)
// Lorsque le paramètre isSorted n'est pas faux, la méthode include sera appelée successivement sur les éléments du tableau pour vérifier si les mêmes éléments ont été ajoutés à la valeur de retour (tableau)
// Si vous vous assurez que les données du tableau sont organisées dans l'ordre avant d'appeler, vous pouvez définir isSorted sur true. Cela exclura la même valeur en la comparant avec le dernier élément. L'utilisation de isSorted sera plus efficace que la méthode d'inclusion par défaut. .
// La méthode uniq comparera les données du tableau par défaut. Si le processeur itérateur est déclaré, un tableau de comparaison sera créé en fonction du processeur. Les données du tableau prévaudront lors de la comparaison, mais les seules données renvoyées dans le. la fin est toujours le tableau d'origine
_.uniq = _.unique = fonction (tableau, isSorted, itérateur) {
// Si le processeur itérateur est utilisé, les données du tableau actuel seront d'abord traitées par l'itérateur et un nouveau tableau traité sera renvoyé.
// Le nouveau tableau est utilisé comme base de comparaison
var initial = itérateur ? _.map(array, itérateur) : tableau;
// Tableau temporaire utilisé pour enregistrer les résultats du traitement
var résultats = [];
// S'il n'y a que 2 valeurs dans le tableau, il n'est pas nécessaire d'utiliser la méthode include pour la comparaison. Définir isSorted sur true peut améliorer l'efficacité opérationnelle.
si (tableau.longueur < 3)
estSorted = vrai ;
// Utilisez la méthode réduire pour itérer et accumuler les résultats du traitement
//La variable initiale correspond aux données de base qui doivent être comparées. Il peut s'agir d'un tableau original ou d'un ensemble de résultats du processeur (si un itérateur est défini)
_.reduce(initial, function(mémo, valeur, index) {
// Si le paramètre isSorted est vrai, utilisez directement === pour comparer les dernières données de l'enregistrement
// Si le paramètre isSorted est faux, utilisez la méthode include pour comparer avec chaque donnée de la collection
if( isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
//le mémo enregistre les données non dupliquées qui ont été comparées
// Selon l'état du paramètre itérateur, les données enregistrées dans memo peuvent être des données originales ou des données traitées par le processeur
memo.push(valeur);
// Les données stockées dans le tableau des résultats du traitement sont toujours les données du tableau d'origine
résultats.push(array[index]);
}
renvoyer le mémo ;
}, []);
//Renvoie le résultat du traitement, qui contient uniquement des données non dupliquées dans le tableau
renvoyer les résultats ;
} ;
// La méthode union a le même effet que la méthode uniq. La différence est que l'union permet de transmettre plusieurs tableaux en tant que paramètres.
_.union = fonction() {
// l'union fusionne superficiellement plusieurs tableaux dans les paramètres dans un objet tableau et le transmet à la méthode uniq pour le traitement
return _.uniq(_.flatten(arguments, true));
};
// Récupère l'élément d'intersection du tableau actuel et d'un ou plusieurs autres tableaux
//À partir du deuxième paramètre, un ou plusieurs tableaux doivent être comparés
_.intersection = _.intersect = fonction (tableau) {
// La variable rest enregistre d'autres objets du tableau qui doivent être comparés.
var rest = tranche.call(arguments, 1);
// Utilisez la méthode uniq pour supprimer les données en double dans le tableau actuel afin d'éviter les calculs répétés
// Filtre les données du tableau actuel via le processeur et renvoie les données qui remplissent les conditions (en comparant les mêmes éléments)
return _.filter(_.uniq(array), function(item) {
// Utilisez la méthode Every pour vérifier que chaque tableau contient les données à comparer.
// Si tous les tableaux contiennent des données de comparaison, alors tous renvoient vrai, si un tableau ne contient pas l'élément, alors renvoie faux
return _.every (reste, fonction (autre) {
// L'autre paramètre stocke chaque tableau qui doit être comparé
// l'élément stocke les données qui doivent être comparées dans le tableau actuel
// Utilisez la méthode indexOf pour rechercher si l'élément existe dans le tableau (veuillez vous référer au commentaire de la méthode indexOf)
return _.indexOf(autre, élément) >= 0;
});
});
} ;
// Filtre et renvoie les données de différence dans le tableau actuel qui ne sont pas égales aux données spécifiées
// Cette fonction est généralement utilisée pour supprimer les données spécifiées dans le tableau et obtenir le nouveau tableau après suppression
// La fonction de cette méthode est équivalente à sans. Le paramètre de méthode sans ne permet pas d'inclure des données dans un tableau, tandis qu'il est recommandé que le paramètre de méthode de différence soit un tableau (vous pouvez également utiliser les mêmes paramètres que sans).
_.différence = fonction (tableau) {
// Fusionner tous les paramètres à partir du deuxième paramètre sous forme de tableau (fusionner uniquement le premier niveau, pas de fusion profonde)
// La variable rest stocke les données de vérification, qui sont utilisées dans cette méthode pour comparer avec les données d'origine.
var rest = _.flatten(slice.call(arguments, 1), true);
// Filtre les données du tableau fusionné. La condition de filtre est que le tableau actuel ne contient pas les données de vérification spécifiées par le paramètre.
// Combine les données qui répondent aux conditions de filtre dans un nouveau tableau et le renvoie
return _.filter (tableau, fonction (valeur) {
return !_.include(reste, valeur);
});
} ;
//Renvoie les données à la même position de chaque tableau en tant que nouveau tableau bidimensionnel. La longueur du tableau renvoyé est basée sur la longueur maximale du tableau dans les paramètres transmis. Les positions vides des autres tableaux sont remplies de valeurs indéfinies.
// La méthode zip doit contenir plusieurs paramètres, et chaque paramètre doit être un tableau
_.zip = fonction() {
//Convertit les paramètres en tableau, à ce stade args est un tableau à deux dimensions
var args = tranche.call(arguments);
// Calcule la longueur de chaque tableau et renvoie la valeur de longueur maximale
var longueur = _.max(_.pluck(args, 'longueur'));
//Créez un nouveau tableau vide en fonction de la valeur de longueur maximale, qui est utilisée pour stocker les résultats du traitement
var résultats = nouveau tableau (longueur);
//La longueur maximale de la boucle, dans chaque boucle la méthode pluck sera appelée pour obtenir les données à la même position dans chaque tableau (de 0 à la dernière position de la séquence)
// Stocke les données obtenues dans un nouveau tableau, les met dans les résultats et renvoie
pour (var i = 0; i < longueur; i )
résultats[i] = _.pluck(args, "" i);
//Le résultat renvoyé est un tableau à deux dimensions
renvoyer les résultats ;
} ;
// Recherche la première occurrence d'un élément dans le tableau, et renvoie -1 si l'élément n'existe pas
// Utilisez === pour faire correspondre les éléments lors de la recherche
_.indexOf = fonction (tableau, élément, isSorted) {
si (tableau == nul)
renvoie -1 ;
var je, l;
si (est trié) {
i = _.sortedIndex(tableau, élément);
return array[i] === élément i : -1;
}
// Priorité à l'appel de la méthode indexOf fournie par l'environnement hôte
si (nativeIndexOf && array.indexOf === nativeIndexOf)
return array.indexOf(élément);
// Boucle et renvoie la première occurrence de l'élément
pour( je = 0, l = tableau.longueur; je < l; je )
if( i dans le tableau && array[i] === élément)
je reviens;
// Aucun élément trouvé, renvoie -1
renvoie -1 ;
} ;
// Renvoie la position de la dernière occurrence d'un élément dans le tableau, ou -1 si l'élément n'existe pas
// Utilisez === pour faire correspondre les éléments lors de la recherche
_.lastIndexOf = fonction (tableau, élément) {
si (tableau == nul)
renvoie -1 ;
// Priorité à l'appel de la méthode lastIndexOf fournie par l'environnement hôte
if(nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf)
return array.lastIndexOf(item);
var je = tableau.longueur;
// Boucle et renvoie la dernière occurrence de l'élément
pendant que (je--)
if( i dans le tableau && array[i] === élément)
je reviens;
// Aucun élément trouvé, renvoie -1
renvoie -1 ;
} ;
// En fonction de l'intervalle et de la taille du pas, génère une série d'entiers et les renvoie sous forme de tableau
// Le paramètre start représente le nombre minimum
// Le paramètre stop indique le nombre maximum
//Le paramètre step représente la valeur de l'étape entre la génération de plusieurs valeurs.
_.range = fonction (démarrage, arrêt, étape) {
// Contrôle des paramètres
if(arguments.length <= 1) {
// S'il n'y a aucun paramètre, start = 0, stop = 0, aucune donnée ne sera générée dans la boucle et un tableau vide sera renvoyé.
// S'il y a 1 paramètre, le paramètre est affecté à stop, start = 0
arrêter = démarrer || 0;
début = 0 ;
}// Génère la valeur de pas de l'entier, la valeur par défaut est 1
étape = arguments[2] || 1;
// Calcule la valeur maximale qui sera générée en fonction de l'intervalle et de la taille du pas
var len = Math.max(Math.ceil((stop - start) / step), 0);
variablex = 0 ;
var range = nouveau tableau (len);
// Génère une liste d'entiers et la stocke dans le tableau range
tandis que (idx < len) {
plage[idx] = début ;
début = étape ;
}
//Renvoyer les résultats de la liste
plage de retour ;
} ;
//Méthodes liées aux fonctions
// ------------------
//Créer un objet fonction public pour définir le prototype
var cteur = fonction() {
} ;
// Lier le contexte d'exécution d'une fonction. Chaque fois que la fonction est appelée, celle-ci pointe vers l'objet de contexte.
// Lors de la liaison d'une fonction, vous pouvez transmettre les paramètres appelants à la fonction en même temps
_.bind = fonction bind(func, contexte) {
var lié, arguments;
// Priorité à l'appel de la méthode bind fournie par l'environnement hôte
si (func.bind === nativeBind && nativeBind)
return nativeBind.apply(func, slice.call(arguments, 1));
// Le paramètre func doit être un type de fonction
si(!_.isFunction(func))
lancez une nouvelle TypeError ;
// La variable args stocke la troisième liste de paramètres à partir de la méthode bind, qui sera transmise à la fonction func à chaque appel.
args = tranche.call(arguments, 2);
retour lié = fonction() {
if(!(cette instance de liaison))
return func.apply(context, sargs.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = nouveau cteur ;
var résultat = func.apply(self, args.concat(slice.call(arguments)));
si (Objet (résultat) === résultat)
renvoyer le résultat ;
revenir soi-même;
} ;
} ;
// Lie la fonction spécifiée, ou toutes les fonctions de l'objet lui-même, à l'objet lui-même. Lorsque la fonction liée est appelée, l'objet contextuel pointe toujours vers l'objet lui-même.
// Cette méthode est généralement utilisée lors de la gestion des événements d'objet, par exemple :
// _(obj).bindAll(); // ou _(obj).bindAll('handlerClick');
// document.addEventListener('click', obj.handlerClick);
// Dans la méthode handlerClick, le contexte est toujours l'objet obj
_.bindAll = fonction (obj) {
//Le deuxième paramètre commence par indiquer le nom de la fonction qui doit être liée
var funcs = tranche.call(arguments, 1);
// Si aucun nom de fonction spécifique n'est spécifié, toutes les propriétés de type Function sont liées à l'objet lui-même par défaut.
si (fonctions. longueur == 0)
funcs = _.functions(obj);
// Boucle et définit tous les contextes de fonction sur l'objet obj lui-même
// Chaque méthode elle-même ne traverse pas les méthodes de la chaîne de prototypes de l'objet, mais la liste des fonctions ici est obtenue via la méthode _.functions, qui contient déjà les méthodes de la chaîne de prototypes
chacun (fonctions, fonction (f) {
obj[f] = _.bind(obj[f], obj);
});
retourner obj ;
} ;
// La méthode memoize renverra une fonction, qui intègre la fonction de mise en cache, met en cache la valeur calculée dans une variable locale et la renvoie directement lors de son prochain appel.
// Si le résultat du calcul est un objet ou des données volumineux, l'utilisation de la mémoire doit être prise en compte lors de son utilisation.
_.memoize = fonction (func, hasher) {
//objet mémo utilisé pour stocker les résultats mis en cache
var mémo = {};
// Le paramètre hasher doit être une fonction utilisée pour renvoyer une clé utilisée comme identifiant pour lire le cache
// Si la clé n'est pas spécifiée, le premier paramètre de la fonction est utilisé comme clé par défaut. Si le premier paramètre de la fonction est un type de données composite, une clé similaire à [Object object] peut être renvoyée. peut entraîner des erreurs de calcul ultérieures. Les données sont incorrectes.
hachage || ( hachage = _.identité);
// Renvoie une fonction qui vérifie d'abord le cache puis appelle les données qui n'ont pas été mises en cache
fonction de retour() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
} ;
} ;
// Retarder l'exécution d'une fonction
// L'unité d'attente est ms, et le troisième paramètre sera transmis à la fonction d'exécution dans l'ordre.
_.delay = fonction (func, attendre) {
var args = tranche.call(arguments, 2);
retourner setTimeout(fonction() {
return func.apply(null, args);
}, attendez);
} ;
// Fonction de retard d'exécution
// setTimeout en JavaScript sera exécuté dans une pile de fonctions distincte. Le temps d'exécution correspond à l'exécution de toutes les fonctions appelées dans la pile actuelle.
// defer définit la fonction à exécuter après 1 ms. Le but est de placer la fonction func dans une pile séparée et d'attendre que la fonction actuelle soit terminée avant de l'exécuter.
// La méthode defer est généralement utilisée pour gérer la priorité des opérations DOM afin d'obtenir un flux logique correct et une expérience interactive plus fluide.
_.defer = fonction (func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Méthode de limitation des fonctions. La méthode throttle est principalement utilisée pour contrôler la fréquence d'exécution des fonctions. Dans l'intervalle de temps contrôlé, les fonctions fréquemment appelées ne seront pas exécutées plusieurs fois.
// Si la fonction est appelée plusieurs fois au cours de l'intervalle de temps, elle sera automatiquement appelée une fois à l'expiration de l'intervalle de temps. Il n'est pas nécessaire d'attendre l'expiration du délai avant de l'appeler manuellement (il n'y aura pas de valeur de retour lors de l'appel automatique. )
// La fonction throttle est généralement utilisée pour gérer des fonctions complexes et fréquemment appelées. Elle contrôle la fréquence d'appel des fonctions par limitation et économise les ressources de traitement.
// Par exemple, la fonction événementielle liée à window.onresize ou la fonction événementielle liée à element.onmousemove peut être empaquetée avec throttle.
//La méthode throttle renvoie une fonction qui appelle automatiquement func et effectue un contrôle de limitation
_.throttle = fonction (func, attendez) {
var contexte, arguments, délai d'attente, limitation, plus, résultat ;
// La variable whenDone appelle la méthode anti-rebond, donc lorsque la fonction est appelée plusieurs fois, le dernier appel écrasera le minuteur précédemment appelé et la fonction d'effacement de l'état ne sera exécutée qu'une seule fois
//La fonction whenDone est appelée lorsque l'intervalle de temps de la dernière exécution de la fonction expire, effaçant la limitation et certains états enregistrés pendant le processus d'appel
var whenDone = _.debounce(function() {
plus = limitation = faux ;
}, attendez);
// Renvoie une fonction et effectue un contrôle de limitation dans la fonction
fonction de retour() {
//Enregistre le contexte d'exécution et les paramètres de la fonction
contexte = ceci ;
arguments = arguments ;
//La dernière fonction est exécutée lorsque l'intervalle de temps du dernier appel de fonction expire
var plus tard = fonction() {
// Efface le handle de timeout pour faciliter le prochain appel de fonction
délai d'attente = nul ;
// more enregistre si la fonction a été appelée à plusieurs reprises entre le dernier appel et l'expiration de l'intervalle de temps
// Si la fonction est appelée à plusieurs reprises, la fonction sera automatiquement appelée à nouveau à l'expiration de l'intervalle de temps.
si (plus)
func.apply(contexte, arguments);
// Appelez whenDone, utilisé pour effacer l'état de limitation après l'intervalle de temps
quandTerminé();
} ;
// timeout enregistre le handle d'intervalle de temps de la dernière exécution de la fonction
// Appelez la fonction ultérieure lorsque le délai d'attente expire. Le délai d'attente sera effacé plus tard et vérifiera si la fonction doit être appelée à nouveau.
si(!timeout)
timeout = setTimeout (plus tard, attendez);
// La variable de limitation enregistre si l'intervalle de temps du dernier appel est terminé, c'est-à-dire s'il est en cours de limitation
// la limitation est définie sur true à chaque fois que la fonction est appelée, indiquant la nécessité d'une limitation, et est définie sur false à l'expiration de l'intervalle de temps (implémenté dans la fonction whenDone)
si (limitation) {
// Plusieurs appels sont effectués pendant le processus de limitation et un statut est enregistré dans more, indiquant que la fonction doit être automatiquement rappelée à l'expiration de l'intervalle de temps.
plus = vrai ;
} autre {
// Ce n'est pas dans le processus de limitation. C'est peut-être la première fois que vous appelez la fonction, ou l'intervalle du dernier appel est dépassé.
result = func.apply(contexte, args);
}
// Appelez whenDone, utilisé pour effacer l'état de limitation après l'intervalle de temps
quandTerminé();
// La variable de limitation enregistre l'état de limitation lorsque la fonction est appelée.
limitation = vrai ;
//Renvoie le résultat de l'appel
renvoyer le résultat ;
} ;
} ;
// La méthode anti-rebond est similaire à la méthode throttle, utilisée pour la limitation des fonctions. La différence entre elles est :
// -- throttle se concentre sur la fréquence d'exécution de la fonction. La fonction ne sera exécutée qu'une seule fois dans la fréquence spécifiée ;
// -- La fonction anti-rebond accorde plus d'attention à l'intervalle entre les exécutions de fonction, c'est-à-dire que le temps entre deux appels de fonction ne peut pas être inférieur au temps spécifié ;
// Si l'intervalle d'exécution entre deux fonctions est inférieur à attendre, le timer sera effacé et recréé, ce qui signifie que si la fonction est appelée de manière continue et fréquente, la fonction ne sera exécutée qu'entre un certain appel et l'appel précédent n'est pas inférieur à une milliseconde.
// La fonction anti-rebond est généralement utilisée pour contrôler les opérations qui prennent un certain temps à être exécutées. Par exemple, pour inviter l'utilisateur 200 ms après que l'utilisateur a terminé la saisie, vous pouvez utiliser l'anti-rebond pour envelopper une fonction et la lier à l'événement onkeyup. .
//------------------------------------------------ ----------------
// @param {Function} func représente la fonction exécutée
// @param {Number} wait représente l'intervalle de temps autorisé. Les appels répétés dans cette plage de temps seront reportés de quelques millisecondes d'attente.
// @param {Booléen}immédiat indique si la fonction sera exécutée immédiatement après son appel, vrai signifie qu'elle sera appelée immédiatement, faux signifie qu'elle sera appelée à l'expiration du délai.
// La méthode anti-rebond renvoie une fonction qui appelle automatiquement func et effectue un contrôle de limitation
_.debounce = fonction (func, attendre, immédiat) {
// le timeout est utilisé pour enregistrer l'état d'exécution du dernier appel de fonction (timer handle)
// Lorsque le timeout est nul, cela signifie que le dernier appel est terminé
délai d'expiration de la variable ;
// Renvoie une fonction et effectue un contrôle de limitation dans la fonction
fonction de retour() {
// Conserve l'objet contextuel et les paramètres de la fonction
var contexte = ceci, args = arguments ;
var plus tard = fonction() {
//Définit le délai d'attente sur null
// La fonction ultérieure sera appelée à l'expiration du délai autorisé
// Lors de l'appel de cette fonction, cela indique que le temps d'exécution de la dernière fonction a dépassé l'intervalle de temps convenu et que les appels ultérieurs après ce temps sont autorisés.
délai d'attente = nul ;
si(!immédiat)
func.apply(contexte, arguments);
} ;
// Si la fonction est configurée pour s'exécuter immédiatement et que l'intervalle de temps du dernier appel est écoulé, appelez la fonction immédiatement
if(immédiat && !timeout)
func.apply(contexte, arguments);
//Créer un timer pour vérifier et définir le statut d'appel de la fonction
// Avant de créer un timer, effacez le dernier handle setTimeout, que la dernière fonction liée ait été exécutée ou non.
// Si lorsque cette fonction est appelée, l'exécution de la fonction précédente n'a pas encore commencé (généralement lorsque immédiat est défini sur false), le temps d'exécution de la fonction sera retardé, donc le handle de timeout sera recréé
clearTimeout(délai d'attente);
// Appelez la fonction ultérieure lorsque le délai autorisé expire
timeout = setTimeout (plus tard, attendez);
} ;
} ;
// Crée une fonction qui ne sera exécutée qu'une seule fois. Si la fonction est appelée à plusieurs reprises, le résultat de la première exécution sera renvoyé.
// Cette fonction est utilisée pour obtenir et calculer la logique des données fixes, comme l'obtention du type de navigateur utilisé par l'utilisateur
_.once = fonction (func) {
// a exécuté un enregistrement si la fonction a été exécutée
//mémo enregistre le résultat de la dernière exécution de la fonction
var ran = false, mémo ;
fonction de retour() {
// Si la fonction a déjà été exécutée, renvoie directement le résultat de la première exécution
si (a couru)
renvoyer le mémo ;
couru = vrai ;
return memo = func.apply(this, arguments);
} ;
} ;
// Renvoie une fonction qui passe la fonction actuelle en tant que paramètre à une fonction wrapper
// Dans la fonction encapsulée, vous pouvez appeler la fonction actuelle via le premier paramètre et renvoyer le résultat
// Généralement utilisé pour les appels combinés à faible couplage de plusieurs fonctions de traitement de processus
_.wrap = fonction (func, wrapper) {
fonction de retour() {
// Passe la fonction actuelle comme premier paramètre à la fonction wrapper
var args = [func].concat(slice.call(arguments, 0));
// Renvoie le résultat du traitement de la fonction wrapper
return wrapper.apply(this, args);
} ;
} ;
// Combine plusieurs fonctions ensemble. Selon l'ordre de passage des paramètres, la valeur de retour de cette dernière fonction sera transmise en paramètre à la fonction précédente en tant que paramètre pour continuer le traitement.
// _.compose(A, B, C); est équivalent à A(B(C()));
// L'inconvénient de cette méthode est que le nombre de paramètres traités par la fonction associée ne peut être qu'un. Si plusieurs paramètres doivent être transmis, ils peuvent être assemblés via le type de données composite Array ou Object.
_.compose = fonction() {
// Récupère la liste des fonctions, tous les paramètres doivent être de type Fonction
var funcs = arguments ;
//Renvoie un handle de fonction pour l'appel
fonction de retour() {
// Exécute les fonctions dans l'ordre d'avant en arrière et transmet la valeur de retour enregistrée en tant que paramètre à la fonction précédente pour continuer le traitement.
var arguments = arguments ;
for(var i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
//Renvoie la valeur de retour du dernier appel de fonction
renvoyer arguments[0] ;
} ;
} ;
// Renvoie une fonction qui sert de compteur d'appels. Lorsque la fonction est appelée fois fois (ou dépasse fois fois), la fonction func sera exécutée.
// La méthode after est généralement utilisée comme compteur asynchrone. Par exemple, si vous devez exécuter une fonction après que plusieurs requêtes AJAX soient terminées, vous pouvez utiliser after pour l'appeler après que chaque requête AJAX soit terminée.
_.after = fonction (fois, fonction) {
// Si aucune heure ou des heures non valides sont spécifiées, func est appelée directement
si (fois <= 0)
return func();
// Renvoie une fonction compteur
fonction de retour() {
// Chaque fois que la fonction compteur times est appelée, décrémentez-la de 1. Après avoir appelé times times, exécutez la fonction func et renvoyez la valeur de retour de la fonction func.
si (--fois < 1) {
return func.apply(this, arguments);
}
} ;
} ;
// Méthodes liées aux objets
//----------------
// Récupère une liste des noms d'attributs d'un objet (hors attributs de la chaîne de prototypes)
_.keys = nativeKeys ||
fonction (obj) {
si(obj !== Objet(obj))
throw new TypeError('Objet invalide');
var clés = [];
//Enregistre et renvoie tous les noms de propriétés de l'objet
pour (clé var dans obj)
si (_.has (obj, clé))
clés[clés.longueur] = clé ;
retourner les clés ;
};
// Renvoie une liste de valeurs de toutes les propriétés d'un objet (à l'exclusion des propriétés de la chaîne de prototypes)
_.values = fonction(obj) {
return _.map(obj, _.identity);
} ;
// Obtenez une liste de clés de toutes les valeurs de propriété d'un objet qui sont de type Fonction et triez-les par nom de clé (y compris les propriétés de la chaîne de prototypes)
_.fonctions = _.méthodes = fonction(obj) {
noms de variables = [];
pour (clé var dans obj) {
if(_.isFunction(obj[clé]))
noms.push(clé);
}
return noms.sort();
} ;
// Copiez les propriétés d'un ou plusieurs objets (y compris les propriétés de la chaîne de prototypes) dans l'objet obj et écrasez-les s'il existe des propriétés portant le même nom.
_.extend = fonction (obj) {
// Un ou plusieurs objets dans chaque paramètre de boucle
chacun (slice.call (arguments, 1), fonction (source) {
// Copiez ou écrasez toutes les propriétés de l'objet vers l'objet obj
pour (var prop dans la source) {
obj[prop] = source[prop];
}
});
retourner obj ;
} ;
// Renvoie un nouvel objet et copie les propriétés spécifiées de obj vers le nouvel objet
//Le deuxième paramètre commence par le nom d'attribut spécifié qui doit être copié (prend en charge plusieurs paramètres et tableaux profonds)
_.pick = fonction(obj) {
//Créer un objet pour stocker les attributs spécifiés copiés
var résultat = {};
// À partir du deuxième paramètre, fusionnez-le dans un tableau pour stocker la liste des noms d'attributs.
chacun(_.flatten(slice.call(arguments, 1)), fonction(clé) {
// Parcourez la liste des noms d'attributs. Si l'attribut existe dans obj, copiez-le dans l'objet résultat.
si (clé dans obj)
résultat[clé] = obj[clé];
});
//Renvoie le résultat copié
renvoyer le résultat ;
} ;
// Copie les attributs qui n'existent pas dans obj ou qui ont une valeur false après conversion en type booléen d'un ou plusieurs objets spécifiés dans le paramètre vers obj
// Généralement utilisé pour attribuer des valeurs par défaut aux objets
_.defaults = fonction (obj) {
// Plusieurs objets peuvent être spécifiés à partir du deuxième paramètre, et les propriétés de ces objets seront à leur tour copiées dans l'objet obj (si la propriété n'existe pas dans l'objet obj)
chacun (slice.call (arguments, 1), fonction (source) {
// Parcourir toutes les propriétés de chaque objet
pour (var prop dans la source) {
// S'il n'existe pas dans obj ou si la valeur de l'attribut est fausse après avoir été convertie en type booléen, copiez l'attribut dans obj
si(obj[prop] == null)
obj[prop] = source[prop];
}
});
retourner obj ;
} ;
//Créez une copie d'obj et renvoyez un nouvel objet contenant l'état de toutes les propriétés et valeurs dans obj
// La fonction clone ne prend pas en charge la copie complète. Par exemple, si une propriété dans obj stocke un objet, l'objet ne sera pas copié.
// Si obj est un tableau, un objet tableau identique sera créé
_.clone = fonction(obj) {
//Ne prend pas en charge les données non-tableaux et de type objet
si(!_.isObject(obj))
retourner obj ;
// Copier et renvoyer un tableau ou un objet
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
} ;
//Exécute une fonction et passe obj en paramètre à la fonction. Une fois la fonction exécutée, l'objet obj est finalement renvoyé.
// Généralement, la méthode tap est utilisée lors de la création d'une chaîne de méthodes, par exemple :
// _(obj).chain().tap(click).tap(mouseover).tap(mouseout);
_.tap = fonction (obj, intercepteur) {
intercepteur (obj);
retourner obj ;
};
// La fonction eq est appelée uniquement dans la méthode isEqual et est utilisée pour comparer si les valeurs de deux données sont égales.
//La différence avec === est que eq accorde plus d'attention à la valeur des données
// Si la comparaison concerne deux types de données composites, elle comparera non seulement si elles proviennent de la même référence, mais effectuera également une comparaison approfondie (en comparant la structure et les données des deux objets)
fonction eq(a, b, pile) {
// Vérifiez si les valeurs de deux types de données simples sont égales
// Pour les types de données composites, elles sont considérées comme égales si elles proviennent de la même référence
// Si la valeur comparée contient 0, vérifiez si l'autre valeur est -0, car 0 === -0 est vrai
// Et 1 / 0 == 1 / -0 n'est pas vrai (la valeur 1 / 0 est l'infini, la valeur 1 / -0 est -Infini et l'infini n'est pas égal à -Infini)
si(a === b)
retourner une !== 0 || 1 / une == 1 / b;
// Après avoir converti les données en type booléen, si la valeur est fausse, il sera jugé si les types de données des deux valeurs sont égaux (car null est égal à non défini, faux, 0 et chaîne vide sous non -comparaison stricte)
si (a == nul || b == nul)
retourner un === b;
// Si les données comparées sont un objet encapsulé Underscore (les objets avec l'attribut _chain sont considérés comme des objets Underscore)
// Déballez ensuite l'objet et obtenez ses propres données (accessibles via _wrapped), puis comparez ses propres données.
// Leur relation est similaire à un objet DOM encapsulé par jQuery et à un objet DOM créé par le navigateur lui-même
si (a._chain)
a = a._wrapped;
si (b._chain)
b = b._wrapped;
// Si l'objet fournit une méthode isEqual personnalisée (la méthode isEqual ici n'est pas la méthode isEqual de l'objet Undersocre, car l'objet Undersocre a été débloqué à l'étape précédente)
// Utilisez ensuite la méthode isEqual personnalisée de l'objet pour comparer avec un autre objet
si(a.isEqual && _.isFunction(a.isEqual))
retourner a.isEqual(b);
si(b.isEqual && _.isFunction(b.isEqual))
return b.isEqual(a);
//Vérifier les types de données des deux données
// Récupère le type de données de l'objet a (via la méthode Object.prototype.toString)
var className = toString.call(a);
// Si le type de données de l'objet a ne correspond pas à l'objet b, les deux valeurs de données sont considérées comme ne correspondant pas non plus.
if(className != toString.call(b))
renvoie faux ;
// Après avoir exécuté ici, vous pouvez vous assurer que les deux données qui doivent être comparées sont des types de données composites et que les types de données sont égaux.
// Vérifiez le type de données des données via le commutateur et effectuez différentes comparaisons pour différents types de données.
// (Les tableaux et les types d'objets ne sont pas inclus ici, car ils peuvent contenir des données plus profondes, qui seront comparées plus tard)
commutateur (nom de classe) {
cas '[chaîne d'objet]' :
// Si ce qui est comparé est un type chaîne (où a est une chaîne créée via new String())
// Convertissez ensuite B en un objet String, puis faites la correspondance (la correspondance ici n'effectue pas de vérification stricte du type de données, car ce ne sont pas des références du même objet)
// Lors de l'appel de == pour comparaison, la méthode toString() de l'objet sera automatiquement appelée, renvoyant deux chaînes de types de données simples
return a == String(b);
cas '[numéro d'objet]' :
// Convertit a en Nombre via a. Si a n'est pas égal avant et après la conversion, on considère que a est de type NaN.
// Parce que NaN et NaN ne sont pas égaux, lorsque la valeur de a est NaN, vous ne pouvez pas simplement utiliser a == b pour correspondre, mais utilisez la même méthode pour vérifier si b est NaN (c'est-à-dire b != b)
// Lorsque la valeur de a est une donnée non NaN, vérifiez si a vaut 0, car lorsque b vaut -0, 0 === -0 est établi (en fait, ils appartiennent logiquement à deux données différentes)
return a != a ? b != b : (a == 0 ? 1 / a == 1 / b : a == b);
cas '[Date de l'objet]' :
// Aucun retour ni break n'est utilisé pour le type date, donc l'exécution continuera à l'étape suivante (que le type de données soit ou non un type booléen, car le type booléen sera vérifié à l'étape suivante)
cas '[objet booléen]' :
//Convertir la date ou le type booléen en nombre
// Le type de date sera converti en horodatage numérique (un format de date invalide sera converti en NaN)
// En type booléen, vrai est converti en 1, faux est converti en 0
// Comparez deux dates ou types booléens pour voir s'ils sont égaux après avoir été convertis en nombres.
retourner a == b;
cas '[objet RegExp]' :
// Type d'expression régulière, accédez à la forme chaîne de l'expression via la source
// Vérifiez si les formes de chaîne de deux expressions sont égales
// Vérifiez si les propriétés globales des deux expressions sont les mêmes (y compris g, i, m)
// Si elles sont complètement égales, les deux données sont considérées comme égales
return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase;
}// Lorsqu'elles sont exécutées à ce stade, les deux données ab doivent être du même type d'objet ou de type tableau
if( typeof a != 'objet' || typeof b != 'objet')
renvoie faux ;
// stack (heap) est le tableau vide transmis en interne lorsque isEqual appelle la fonction eq. Il sera également transmis lorsque la méthode eq sera appelée ultérieurement lors de l'itération interne de comparaison d'objets et de données.
//length enregistre la longueur du tas
var longueur = pile.longueur;
tandis que(longueur--) {
// Si un objet dans le tas correspond aux données a, il est considéré comme égal
si (pile [longueur] == a)
renvoie vrai ;
}
//Ajouter les données a au tas
stack.push(a);
//Définit quelques variables locales
var taille = 0, résultat = vrai ;
// Comparaison approfondie d'objets et de tableaux par récursion
if(className == '[object Array]') {
//Les données comparées sont de type tableau
// la taille enregistre la longueur du tableau
// Le résultat compare les longueurs des deux tableaux pour voir si elles sont cohérentes. Si les longueurs sont incohérentes, le résultat (c'est-à-dire faux) sera renvoyé à la fin de la méthode.
taille = a.longueur ;
résultat = taille == b.longueur ;
// Si les longueurs des deux tableaux sont identiques
si (résultat) {
//Appelez la méthode eq pour comparer de manière itérative les éléments du tableau (si le tableau contient un tableau ou un objet bidimensionnel, la méthode eq effectuera une comparaison approfondie)
while(taille--) {
// Lorsque vous vous assurez que les deux tableaux contiennent des éléments à l'index actuel, appelez la méthode eq pour une comparaison approfondie (transmettez les données du tas à la méthode eq)
// Stocke le résultat de la comparaison dans la variable de résultat. Si le résultat est faux (c'est-à-dire que les données d'un certain élément obtenues lors de la comparaison sont incohérentes), arrêtez l'itération.
if(!( résultat = taille dans a == taille dans b && eq(a[taille], b[taille], pile)))
casser;
}
}
} autre {
//Les données comparées sont de type objet
// Si les deux objets ne sont pas des instances de la même classe (par rapport aux propriétés du constructeur), les deux objets sont considérés comme non égaux.
if('constructeur' dans a != 'constructeur' dans b || a.constructor != b.constructor)
renvoie faux ;
// Comparez en profondeur les données de deux objets
pour (var clé dans a) {
if(_.has(a, clé)) {
// la taille est utilisée pour enregistrer le nombre d'attributs comparés, car ce qui est parcouru ici, ce sont les attributs de l'objet a, et compare les données de l'attribut dans l'objet b
// Lorsque le nombre d'attributs dans l'objet b dépasse l'objet a, la logique est ici valable, mais les deux objets ne sont pas égaux.
taille ;
// Appelez itérativement la méthode eq pour comparer en profondeur les valeurs d'attributdans les deux objets
// Enregistrez le résultat de la comparaison dans la variable de résultat et arrêtez l'itération lorsque des données inégales sont comparées.
if(!( result = _.has(b, key) && eq(a[key], b[key], stack)))
casser;
}
}
// La comparaison approfondie est terminée. On peut garantir que toutes les données de l'objet a et les mêmes données de l'objet b existent également.
// Vérifiez si le nombre d'attributs dans l'objet b est égal à celui de l'objet a en fonction de la taille (longueur de l'attribut de l'objet)
si (résultat) {
// Parcourt toutes les propriétés de l'objet b
pour (entrez b) {
// Lorsque la taille a atteint 0 (c'est-à-dire que le nombre d'attributs dans l'objet a a été parcouru) et qu'il y a encore des attributs dans l'objet b, alors l'objet b a plus d'attributs que l'objet a.
if(_.has(b, clé) && !(taille--))
casser;
}
// Lorsque l'objet b a plus d'attributs que l'objet a, les deux objets ne sont pas considérés comme égaux.
résultat = !size;
}
}
// Lorsque la fonction termine son exécution, supprimez les premières données du tas (lors de la comparaison d'objets ou de tableaux, la méthode eq sera itérée et il peut y avoir plusieurs données dans le tas)
stack.pop();
//Le résultat renvoyé enregistre le résultat final de la comparaison
renvoyer le résultat ;
}
// Comparez les valeurs de deux données (prend en charge les types de données composites), méthode externe de fonction interne eq
_.isEqual = fonction (a, b) {
return eq(a, b, []);
} ;
// Vérifiez si les données sont vides, y compris '', false, 0, null, non défini, NaN, un tableau vide (la longueur du tableau est 0) et un objet vide (l'objet lui-même n'a aucune propriété)
_.isEmpty = fonction (obj) {
// obj est converti en type booléen et la valeur est fausse
si(obj == nul)
renvoie vrai ;
// Vérifiez si la longueur de l'objet ou de la chaîne est 0
si(_.isArray(obj) || _.isString(obj))
return obj.length === 0;
// Vérifiez l'objet (lors de l'utilisation d'une boucle for in, les propriétés de l'objet lui-même seront bouclées en premier, suivies des propriétés de la chaîne de prototypes), donc si la première propriété appartient à l'objet lui-même, alors l'objet n'est pas un objet vide
pour (clé var dans obj)
si (_.has (obj, clé))
renvoie faux ;
// Tous les types de données n'ont pas réussi la vérification et sont des données vides
renvoie vrai ;
} ;
// Vérifiez si l'objet est un objet DOM
_.isElement = fonction (obj) {
return !!(obj && obj.nodeType == 1);
} ;
// Vérifiez si l'objet est de type tableau, appelez d'abord la méthode isArray fournie par l'environnement hôte
_.isArray = nativeIsArray ||
fonction (obj) {
return toString.call(obj) == '[object Array]';
} ;
// Vérifiez si l'objet est un objet de type de données composite (c'est-à-dire un type de données non basique String, Boolean, Number, null, non défini)
// Si le type de données de base est créé via new, il appartient également au type d'objet
_.isObject = fonction (obj) {
return obj === Objet(obj);
};
// 檢查一個資料是否為arguments參數對象
_.isArguments = function(obj) {
return toString.call(obj) == '[object Arguments]';
};
// 驗證isArguments函數, 如果運行環境無法正常驗證arguments類型的資料, 則重新定義isArguments方法
if(!_.isArguments(arguments)) {
// 對於環境無法透過toString驗證arguments類型的, 則透過呼叫arguments獨有的callee方法來進行驗證
_.isArguments = function(obj) {
// callee是arguments的一個屬性, 指向對arguments所屬函數本身的引用
return !!(obj && _.has(obj, 'callee'));
};
}
// 驗證物件是否是一個函數類型
_.isFunction = function(obj) {
return toString.call(obj) == '[object Function]';
};
// 驗證物件是否是一個字串類型
_.isString = function(obj) {
return toString.call(obj) == '[object String]';
};
// 驗證物件是否為數字類型
_.isNumber = function(obj) {
return toString.call(obj) == '[object Number]';
};
// 檢查一個數字是否為有效數字且有效範圍(Number類型, 值在負無窮大 - 正無窮大之間)
_.isFinite = function(obj) {
return _.isNumber(obj) && isFinite(obj);
};
// 檢查資料是否為NaN型別(所有資料中只有NaN與NaN不相等)
_.isNaN = function(obj) {
return obj !== obj;
};
// 檢查資料是否時Boolean類型
_.isBoolean = function(obj) {
// 支援字面量和物件形式的Boolean數據
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// 檢查資料是否為Date類型
_.isDate = function(obj) {
return toString.call(obj) == '[object Date]';
};
// 檢查資料是否為正規表示式類型
_.isRegExp = function(obj) {
return toString.call(obj) == '[object RegExp]';
};
// 檢查資料是否為Null值
_.isNull = function(obj) {
return obj === null;
};
// 檢查資料是否為Undefined(未定義的)值
_.isUndefined = function(obj) {
return obj ===
void 0;
};
// 檢查一個屬性是否屬於物件本身, 而非原型鏈中
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// 工具函數
// -----------------
// 放棄_(下劃線)命名的Underscore物件, 並傳回Underscore物件, 一般用於避免命名衝突或規範命名方式
// 例如:
// var us = _.noConflict(); // 取消_(下劃線)命名, 並將Underscore物件存放於us變數中
// console.log(_); // _(下劃線)已經無法再存取Underscore物件, 而恢復Underscore 定義前的值
_.noConflict = function() {
// previousUnderscore變數記錄了Underscore定義前_(下劃線)的值
root._ = previousUnderscore;
return this;
};
// 傳回與參數相同的值, 一般用於將一個資料的取得方式轉換為函數取得方式(內部用於建構方法時作為預設處理器函數)
_.identity = function(value) {
return value;
};
// 使指定的函數迭代執行n次(無參數)
_.times = function(n, iterator, context) {
for(var i = 0; i " '
_.escape = function(string) {
return ('' string).replace(/&/g, '&').replace(/, '/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(///g, '/');
};
// 指定一個物件的屬性, 傳回該屬性對應的值, 如果該屬性對應的是一個函數, 則會執行該函數並傳回結果
_.result = function(object, property) {
if(object == null)
return null;
// 取得物件的值
var value = object[property];
// 若值是函數, 則執行並傳回, 否則將直接傳回
return _.isFunction(value) ? value.call(object) : value;
};
// 新增一系列自訂方法到Underscore物件中, 用於擴充Underscore插件
_.mixin = function(obj) {
// obj是一個集合一系列自訂方法的物件, 此處透過each遍歷物件的方法
each(_.functions(obj), function(name) {
// 透過addToWrapper函數將自訂方法加入Underscore建構的物件, 用於支援物件式調用
// 同時將方法加入 _ 本身, 用於支援函數式調用
addToWrapper(name, _[name] = obj[name]);
});
};
// 取得一個全域唯一識別, 標識從0開始累加
var idCounter = 0;
// prefix表示標識的前綴, 如果沒有指定前綴則直接傳回標識, 一般用於給物件或DOM建立唯一ID
_.uniqueId = function(prefix) {
var id = idCounter ;
return prefix ? prefix id : id;
};
// 定義模板的界定符號, 在template方法中使用
_.templateSettings = {
// JavaScript可執行程式碼的界定符
evaluate : //g,
// 直接輸出變數的界定符
interpolate : //g,
// 需要將HTML輸出為字串(將特殊符號轉換為字串形式)的界定符
escape : //g
};
var noMatch = /.^/;
// escapes物件記錄了需要進行相互換轉的特殊符號與字串形式的對應關係, 在兩者進行相互轉換時作為索引使用
// 首先根據字串形式定義特殊字符
var escapes = {
'\' : '\',
"'" : "'",
'r' : 'r',
'n' : 'n',
't' : 't',
'u2028' : 'u2028',
'u2029' : 'u2029'
};
// 遍歷所有特殊字元字串, 並以特殊字元作為key記錄字串形式
for(var p in escapes)
escapes[escapes[p]] = p;
// 定義模板中需要替換的特殊符號, 包含反斜線, 單引號, 回車符, 換行符, 製表符, 行分隔符, 段落分隔符
// 將字串中的特殊符號轉換為字串形式時使用
var escaper = /\|'|r|n|t|u2028|u2029/g;
// 將字串形式的特殊符號反轉(替換)時使用
var unescaper = /\(\|'|r|n|t|u2028|u2029)/g;
// 反轉字串中的特殊符號
// 在範本中涉及到需要執行的JavaScript源碼, 需要進行特殊符號反轉, 否則如果以HTML實體或字串形式出現, 會拋出語法錯誤
var unescape = function(code) {
return code.replace(unescaper, function(match, escape) {
return escapes[escape];
});
};
// Underscore模板解析方法, 用於將資料填入一個模板字串中
// 模板解析流程:
// 1. 將範本中的特殊符號轉換為字串
// 2. 解析escape形式標籤, 將內容解析為HTML實體
// 3. 解析interpolate形式標籤, 輸出變數
// 4. 解析evaluate形式標籤, 建立可執行的JavaScript程式碼
// 5. 產生一個處理函數, 此函數在得到資料後可直接填入範本並傳回填滿後的字串
// 6. 根據參數傳回填滿後的字串或處理函數的句柄
// -------------------
// 在模板體內, 可透過argments取得2個參數, 分別為填滿資料(名稱為obj)和Underscore物件(名稱為_)
_.template = function(text, data, settings) {
// 範本配置, 如果沒有指定配置項, 則使用templateSettings中指定的配置項
settings = _.defaults(settings || {}, _.templateSettings);
// 開始將模板解析為可執行原始碼
var source = "__p ='" text.replace(escaper, function(match) {
// 將特殊符號轉移為字串形式
return '\' escapes[match];
}).replace(settings.escape || noMatch, function(match, code) {
// 解析escape形式標籤 , 將變數中包含的HTML透過_.escape函數轉換為HTML實體
return "' n_.escape(" unescape(code) ") n'";
}).replace(settings.interpolate || noMatch, function(match, code) {
// 解析interpolate形式標籤 , 將模板內容作為一個變數與其它字串連接起來, 則會作為一個變數輸出
return "' n(" unescape(code) ") n'";
}).replace(settings.evaluate || noMatch, function(match, code) {
// 解析evaluate形式標籤, evaluate標籤中存儲了需要執行的JavaScript代碼, 這裡結束當前的字符串拼接, 並在新的一行作為JavaScript語法執行, 並將後面的內容再次作為字符串的開始, 因此evaluate標籤內的JavaScript程式碼就能被正常執行
return "';n" unescape(code) "n;__p ='";
}) "';n";
if(!settings.variable)
source = 'with(obj||{}){n' source '}n';
source = "var __p='';" "var print=function(){__p =Array.prototype.join.call(arguments, '')};n" source "return __p;n";
// 建立一個函數, 將原始碼當作函數執行體, 將obj和Underscore當作參數傳遞給該函數
var render = new Function(settings.variable || 'obj', '_', source);
// 如果指定了模板的填充資料, 則替換模板內容, 並傳回替換後的結果
if(data)
return render(data, _);
// 如果沒有指定填充資料, 則傳回一個函數, 此函數用於將接收的資料替換到模板
// 如果程式中會多次填入相同範本, 那麼在第一次呼叫時建議不指定填充資料, 在獲得處理函數的參考後, 再直接呼叫會提高運行效率
var template = function(data) {
return render.call(this, data, _);
};
// 將創建的源碼字串添加到函數物件中, 一般用於調試和測試
template.source = 'function(' (settings.variable || 'obj') '){n' source '}';
// 沒有指定填滿資料的情況下, 傳回處理函數句柄
return template;
};
// 支援Underscore物件的方法鏈操作, 可參考 wrapper.prototype.chain
_.chain = function(obj) {
return _(obj).chain();
};
// Underscore物件封裝相關方法
// ---------------
// 建立一個包裝器, 將一些原始資料包裝
// 所有的undersocre物件, 內部均透過wrapper函數進行建構與封裝
// Underscore與wrapper的內在關係:
// -內部定義變數_, 將Underscore相關的方法加到_, 這樣就可以支援函數式的呼叫, 如_.bind()
// -內部定義wrapper類別, 將_的原型物件指向wrapper類別的原型
// -Underscore相關的方法加入wrapper原型, 所建立的_物件就具備了Underscore的方法
// -將Array.prototype相關方法加入wrapper原型, 建立的_物件就具備了Array.prototype中的方法
// -new _()時實際建立並傳回了一個wrapper()物件, 並將原始陣列儲存到_wrapped變數, 並將原始值作為第一個參數呼叫對應方法
var wrapper = function(obj) {
// 原始資料存放在包裝物件的_wrapped屬性中
this._wrapped = obj;
};
// 將Underscore的原型物件指向wrapper的原型, 因此透過像wrapper原型中加入方法, Underscore物件也會具備同樣的方法
_.prototype = wrapper.prototype;
// 傳回一個物件, 如果當前Underscore呼叫了chain()方法(即_chain屬性為true), 則傳回一個被包裝的Underscore物件, 否則回傳物件本身
// result函數
Copier après la connexion