Underscore source code analysis

Recommended for you: Get network issues from WhatsUp Gold. Not end users.
   1 (function() {
   2 
   3          // Create a global object, in the browser is expressed as window object, global object in Node.js
   4          var root = this;
   5 
   6          // "_ preservation" (underlined variables) before being overwritten values
   7          // If the naming conflicts or considering the specification, through the _.noConflict () method to restore the "_" is occupied by Underscore before the value, and returns a Underscore object to rename
   8          var previousUnderscore = root._;
   9 
  10          // Create an empty object constants for internal use, sharing
  11          var breaker = {};
  12 
  13          // The prototype chain built-in objects are cached local variables, convenient and fast call
  14          var ArrayProto = Array.prototype, //
  15          ObjProto = Object.prototype, //
  16          FuncProto = Function.prototype;
  17 
  18          // The common method of prototype cache built-in objects in a local variable, convenient and fast call
  19          var slice = ArrayProto.slice, //
  20          unshift = ArrayProto.unshift, //
  21          toString = ObjProto.toString, //
  22          hasOwnProperty = ObjProto.hasOwnProperty;
  23 
  24          // This defines 1.6 new methods to provide some JavaScript
  25          // If the support of these host environment is priority call, if you do not provide the host environment, will be implemented by Underscore
  26          var nativeForEach = ArrayProto.forEach, //
  27          nativeMap = ArrayProto.map, //
  28          nativeReduce = ArrayProto.reduce, //
  29          nativeReduceRight = ArrayProto.reduceRight, //
  30          nativeFilter = ArrayProto.filter, //
  31          nativeEvery = ArrayProto.every, //
  32          nativeSome = ArrayProto.some, //
  33          nativeIndexOf = ArrayProto.indexOf, //
  34          nativeLastIndexOf = ArrayProto.lastIndexOf, //
  35          nativeIsArray = Array.isArray, //
  36          nativeKeys = Object.keys, //
  37          nativeBind = FuncProto.bind;
  38 
  39          // Create the object type is called, will return a Underscore wrapper, all Underscore method contains wrapper object prototype (similar to the DOM object wrapper for a jQuery object)
  40          var _ = function(obj) {
  41              // All Underscore objects are constructed in the interior through the wrapper object
  42              return new wrapper(obj);
  43          };
  44          // In view of the different host environment, store named variable Undersocre to a different object
  45          if( typeof exports !== 'undefined') {// Node.js environment
  46              if( typeof module !== 'undefined' && module.exports) {
  47                  exports = module.exports = _;
  48              }
  49              exports._ = _;
  50          } else {// Named variable Underscore browser environment was hanging in the window object
  51              root['_'] = _;
  52          }
  53 
  54          // Version declaration
  55          _.VERSION = '1.3.3';
  56 
  57          // A collection of related methods (general data processing method and object)
  58          // --------------------
  59 
  60          // Iterative processor, processor executes the method on each element in the collection
  61          var each = _.each = _.forEach = function(obj, iterator, context) {
  62              // A null value is not
  63              if(obj == null)
  64                  return;
  65              if(nativeForEach && obj.forEach === nativeForEach) {
  66                  // If the host environment support, the priority calls the forEach method provided by JavaScript 1.6
  67                  obj.forEach(iterator, context);
  68              } else if(obj.length === +obj.length) {
  69                  // The <> array; each element in a processor executing method
  70                  for(var i = 0, l = obj.length; i <l; i++) {
  71                      if( i in obj && iterator.call(context, obj[i], i, obj) === breaker)
  72                          return;
  73                  }
  74              } else {
  75                  // The <> each element in a processor executing method
  76                  for(var key in obj) {
  77                      if(_.has(obj, key)) {
  78                          if(iterator.call(context, obj[key], key, obj) === breaker)
  79                              return;
  80                      }
  81                  }
  82              }
  83          };
  84          // Iterative processor, difference and each methods is the return value of map will store the iteration, and as a new array
  85          _.map = _.collect = function(obj, iterator, context) {
  86              // For the array to store the return value
  87              var results = [];
  88              if(obj == null)
  89                  return results;
  90              // Prior to calling the map method provided by the host environment
  91              if(nativeMap && obj.map === nativeMap)
  92                  return obj.map(iterator, context);
  93              // The elements in the collection of iterative processing
  94              each(obj, function(value, index, list) {
  95                  // The iteration process returns the value stored in results array
  96                  results[results.length] = iterator.call(context, value, index, list);
  97              });
  98              // Returns the processing result
  99              if(obj.length === +obj.length)
 100                  results.length = obj.length;
 101              return results;
 102          };
 103          // To each of the set of elements into the iterative processor, and will return to this iteration of the value as a "Memo" transfer to the next iteration, generally used for cumulative results or data connection
 104          _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
 105              // The number of parameters to check whether there is an initial value
 106              var initial = arguments.length > 2;
 107              if(obj == null)
 108                  obj = [];
 109              // Prior to calling the reduce method provided by the host environment
 110              if(nativeReduce && obj.reduce === nativeReduce && false) {
 111                  if(context)
 112                      iterator = _.bind(iterator, context);
 113                  return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
 114              }
 115              // The elements in the collection of iterative processing
 116              each(obj, function(value, index, list) {
 117                  if(!initial) {
 118                      // If there is no initial value, will be the first element as the initial value; if be treated is the set of objects, the default value is the first value of the property
 119                      memo = value;
 120                      initial = true;
 121                  } else {
 122                      // Record the results of handling, and sends the results to the next iteration
 123                      memo = iterator.call(context, memo, value, index, list);
 124                  }
 125              });
 126              if(!initial)
 127                  throw new TypeError('Reduce of empty array with no initial value');
 128              return memo;
 129          };
 130          // Similar to the reduce function, elements will reverse iteration in the collection (i.e. from the last element to the first element)
 131          _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
 132              var initial = arguments.length > 2;
 133              if(obj == null)
 134                  obj = [];
 135              // Prior to calling the reduceRight method provided by the host environment
 136              if(nativeReduceRight && obj.reduceRight === nativeReduceRight) {
 137                  if(context)
 138                      iterator = _.bind(iterator, context);
 139                  return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
 140              }
 141              // Reverse the order of the elements in the collection
 142              var reversed = _.toArray(obj).reverse();
 143              if(context && !initial)
 144                  iterator = _.bind(iterator, context);
 145              // Processing the data by the reduce method
 146              return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
 147          };
 148          // Traversal of the elements in the collection, return the first through the processor verification elements
 149          _.find = _.detect = function(obj, iterator, context) {
 150              // The first result store can be validated elements
 151              var result;
 152              // By the method of any traversal data, and record the elements
 153              // (if it is checked at the iteration processor return status, here the use of each method would be more appropriate)
 154              any(obj, function(value, index, list) {
 155                  // If the handler returns results are converted to Boolean type value is true, the current record and returns the current element
 156                  if(iterator.call(context, value, index, list)) {
 157                      result = value;
 158                      return true;
 159                  }
 160              });
 161              return result;
 162          };
 163          // Effect is similar to find, but the filter method will record all the elements in the collection
 164          _.filter = _.select = function(obj, iterator, context) {
 165              // Used to store the elements of an array
 166              var results = [];
 167              if(obj == null)
 168                  return results;
 169              // Prior to calling the filter method provided by the host environment
 170              if(nativeFilter && obj.filter === nativeFilter)
 171                  return obj.filter(iterator, context);
 172              // Iteration of the elements in the collection, and through the processor validation element in the array and returns
 173              each(obj, function(value, index, list) {
 174                  if(iterator.call(context, value, index, list))
 175                      results[results.length] = value;
 176              });
 177              return results;
 178          };
 179          // In contrast to the filter method, which returns the processor verification through without a list of elements
 180          _.reject = function(obj, iterator, context) {
 181              var results = [];
 182              if(obj == null)
 183                  return results;
 184              each(obj, function(value, index, list) {
 185                  if(!iterator.call(context, value, index, list))
 186                      results[results.length] = value;
 187              });
 188              return results;
 189          };
 190          // If all elements of sets can be through the processor verification, it returns true
 191          _.every = _.all = function(obj, iterator, context) {
 192              var result = true;
 193              if(obj == null)
 194                  return result;
 195              // Prior to calling the every method provided by the host environment
 196              if(nativeEvery && obj.every === nativeEvery)
 197                  return obj.every(iterator, context);
 198              // Iteration of the elements in the collection
 199              each(obj, function(value, index, list) {
 200                  // This understanding is result = (result && iterator.call(context, value, index, list))
 201                  // Verification results are converted to Boolean type is true
 202                  if(!( result = result && iterator.call(context, value, index, list)))
 203                      return breaker;
 204              });
 205              return !!result;
 206          };
 207          // Check every element in the set is converted to Boolean type, whether the true value? Or processed by the processor, if the value is true?
 208          var any = _.some = _.any = function(obj, iterator, context) {
 209              // If you do not specify a processor parameters, the default processor function returns the element itself, and when iterating through the elements are converted to Boolean types to determine whether the true value
 210              iterator || ( iterator = _.identity);
 211              var result = false;
 212              if(obj == null)
 213                  return result;
 214              // Prior to calling the some method provided by the host environment
 215              if(nativeSome && obj.some === nativeSome)
 216                  return obj.some(iterator, context);
 217              // Iteration of the elements in the collection
 218              each(obj, function(value, index, list) {
 219                  if(result || ( result = iterator.call(context, value, index, list)))
 220                      return breaker;
 221              });
 222              return !!result;
 223          };
 224          // Check whether the value set exactly match the target parameters (also will match the data type)
 225          _.include = _.contains = function(obj, target) {
 226              var found = false;
 227              if(obj == null)
 228                  return found;
 229              // Prior to calling the Array.prototype.indexOf method provided by the host environment
 230              if(nativeIndexOf && obj.indexOf === nativeIndexOf)
 231                  return obj.indexOf(target) != -1;
 232              // Through the collection of elements in the any iterative method, verify the element values and types and target match exactly
 233              found = any(obj, function(value) {
 234                  return value === target;
 235              });
 236              return found;
 237          };
 238          // In order to call a method of the same name all the elements in the set, from the beginning of the third parameters, will be to spread to the calling method elements.
 239          // Returns an array of all the methods, the results of storage
 240          _.invoke = function(obj, method) {
 241              // Parameter transfer calls a method of the same name (from the beginning of the third parameters)
 242              var args = slice.call(arguments, 2);
 243              // Method is called for each element, and put the result into in the returned array
 244              return _.map(obj, function(value) {
 245                  return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
 246              });
 247          };
 248          // Traverse a list of objects in the array, the value specified in the list of attributes for each object and returns the
 249          _.pluck = function(obj, key) {
 250              // If the property does not exist an object, it returns undefined
 251              return _.map(obj, function(value) {
 252                  return value[key];
 253              });
 254          };
 255          // Returns the maximum value in the set, if there is no comparable value, it returns undefined
 256          _.max = function(obj, iterator, context) {
 257              // If the collection is an array, and without the use of processor, Math.max is used to obtain the maximum value
 258              // The general will is stored in an array of a series of Number data type
 259              if(!iterator && _.isArray(obj) && obj[0] === +obj[0])
 260                  return Math.max.apply(Math, obj);
 261              // The null value, return directly to negative infinity
 262              if(!iterator && _.isEmpty(obj))
 263                  return -Infinity;
 264              // A temporary object, computed is used to store the maximum value in the process of comparison (temporary)
 265              var result = {
 266                  computed : -Infinity
 267              };
 268              // Iteration of the elements in the collection
 269              each(obj, function(value, index, list) {
 270                  // If the processor parameters are specified, compares the data processor for the return value, otherwise each traversal directly use the default values
 271                  var computed = iterator ? iterator.call(context, value, index, list) : value;
 272                  // If the comparison value compared to the last value is larger, the current value into result.value
 273                  computed >= result.computed && ( result = {
 274                      value : value,
 275                      computed : computed
 276                  });
 277              });
 278              // Returns the maximum value
 279              return result.value;
 280          };
 281          // Returns the minimum value in the collection process, consistent with the max method
 282          _.min = function(obj, iterator, context) {
 283              if(!iterator && _.isArray(obj) && obj[0] === +obj[0])
 284                  return Math.min.apply(Math, obj);
 285              if(!iterator && _.isEmpty(obj))
 286                  return Infinity;
 287              var result = {
 288                  computed : Infinity
 289              };
 290              each(obj, function(value, index, list) {
 291                  var computed = iterator ? iterator.call(context, value, index, list) : value;
 292                  computed <result.computed && ( result = {
 293                      value : value,
 294                      computed : computed
 295                  });
 296              });
 297              return result.value;
 298          };
 299          // Through the random number, let the array without arrangement
 300          _.shuffle = function(obj) {
 301              // The shuffled variable to store the processing process and the final result data
 302              var shuffled = [], rand;
 303              // Iteration of the elements in the collection
 304              each(obj, function(value, index, list) {
 305                  // Generate a random number, random number in the <> 0- number currently processing between;
 306                  rand = Math.floor(Math.random() * (index + 1));
 307                  // The random element in shuffled the end of the array
 308                  shuffled[index] = shuffled[rand];
 309                  // The random number in the position to insert the new value
 310                  shuffled[rand] = value;
 311              });
 312              // Returns an array, the array is stored in the collection elements through random mixing
 313              return shuffled;
 314          };
 315          // The elements in the collection, are arranged according to specific fields or value
 316          // Compared with Array.prototype.sort method, sortBy method support for the object sorting
 317          _.sortBy = function(obj, val, context) {
 318              // Val should be a property of an object, or a processor function, if a processor, you should return to the comparison of data
 319              var iterator = _.isFunction(val) ? val : function(obj) {
 320                  return obj[val];
 321              };
 322              // Call order: _.pluck(_.map().sort());
 323              // Call _.map () method iterates over the collection, and the collection of elements in the value node, the elements need to be compared with data in the criteria attribute
 324              // Call sort () method to the elements in the collection in accordance with the criteria attribute data in the order
 325              // Call pluck to obtain the object and returns the sorted collection
 326              return _.pluck(_.map(obj, function(value, index, list) {
 327                  return {
 328                      value : value,
 329                      criteria : iterator.call(context, value, index, list)
 330                  };
 331              }).sort(function(left, right) {
 332                  var a = left.criteria, b = right.criteria;
 333                  if(a ===
 334                      void 0)
 335                      return 1;
 336                  if(b ===
 337                      void 0)
 338                      return -1;
 339                  return a <b ? -1 : a > b ? 1 : 0;
 340              }), 'value');
 341          };
 342          // The elements in the collection, according to the handler returns key is divided into a plurality of array
 343          _.groupBy = function(obj, val) {
 344              var result = {};
 345              // The Val will be converted to the packet processor function, if the Val is not a Function data type, it will be used as a screening element when the key value
 346              var iterator = _.isFunction(val) ? val : function(obj) {
 347                  return obj[val];
 348              };
 349              // Iteration of the elements in the collection
 350              each(obj, function(value, index) {
 351                  // The processor of the return value as key, and will the same key element into a new array
 352                  var key = iterator(value, index);
 353                  (result[key] || (result[key] = [])).push(value);
 354              });
 355              // Returns the packet data
 356              return result;
 357          };
 358          _.sortedIndex = function(array, obj, iterator) {
 359              iterator || ( iterator = _.identity);
 360              var low = 0, high = array.length;
 361              while(low < high) {
 362                  var mid = (low + high) >> 1;
 363                  iterator(array[mid]) <iterator(obj) ? low = mid + 1 : high = mid;
 364              }
 365              return low;
 366          };
 367          // A collection into an array and returns
 368          // Generally used to convert the arguments to an array, or object unordered collection into an ordered set of data form
 369          _.toArray = function(obj) {
 370              if(!obj)
 371                  return [];
 372              if(_.isArray(obj))
 373                  return slice.call(obj);
 374              // Converts a arguments to an array
 375              if(_.isArguments(obj))
 376                  return slice.call(obj);
 377              if(obj.toArray && _.isFunction(obj.toArray))
 378                  return obj.toArray();
 379              // The object is converted to an array, list all attribute values in the object array containing (does not contain the attribute object prototype in the chain)
 380              return _.values(obj);
 381          };
 382          // Calculate the number of elements in the collection
 383          _.size = function(obj) {
 384              // If the collection is an array, then calculate the number of array elements
 385              // If the collection is an object, then calculate the number of the object of property (does not contain the attribute object prototype in the chain)
 386              return _.isArray(obj) ? obj.length : _.keys(obj).length;
 387          };
 388          // Method of array
 389          // ---------------
 390 
 391          // Returns an array of first or specified in the order of n elements
 392          _.first = _.head = _.take = function(array, n, guard) {
 393              // If you do not specify a parameter n, it returns the first element
 394              // If n is specified, it returns a new array, contains sequence specifies the number of n elements
 395              // The guard parameter is used to determine the returns only the first element, when guard is true, the number of N specified is invalid
 396              return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
 397          };
 398          // Return a new array, contain other elements except the first elements, or excluded from the beginning of last element forward specify n elements
 399          // Unlike the first method lies in the first position, to elements in the array, initial determines the excluded elements in the array position
 400          _.initial = function(array, n, guard) {
 401              // If there is no transmission parameters of N, the default return other elements except the last element of
 402              // If the parameter n, the other elements returned starting forward from the last element of the N element outside
 403              // Guard is used to determine the return only one element, when guard is true, the number of N specified is invalid
 404              return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
 405          };
 406          // Returns the last or reverse the specified n elements
 407          _.last = function(array, n, guard) {
 408              if((n != null) && !guard) {
 409                  // Element position n calculation and specify access, until the end of the array, return a new array
 410                  return slice.call(array, Math.max(array.length - n, 0));
 411              } else {
 412                  // If you do not specify a number, or guard true, only to return the last element
 413                  return array[array.length - 1];
 414              }
 415          };
 416          // In addition to the first or the specified access to other elements of n elements outside the
 417          _.rest = _.tail = function(array, index, guard) {
 418              // Second position parameters are calculated by slice, until the end of the array
 419              // If index is not specified, or guard value is true, it returns the other elements except the first elements of the
 420              // (index == null)Values for true, passed as parameters to the slice function will be automatically converted to 1
 421              return slice.call(array, (index == null) || guard ? 1 : index);
 422          };
 423          // Returns an array of all values can be converted to true elements, and returns a new array
 424          // Can not be converted values include false, 0, '', null, undefined, NaN, these values will be converted to false
 425          _.compact = function(array) {
 426              return _.filter(array, function(value) {
 427                  return !!value;
 428              });
 429          };
 430          // A multidimensional number combined into a one-dimensional array, deep with support
 431          // The shallow parameter is used to control and depth, when shallow is true, only with the first layer, deep with default
 432          _.flatten = function(array, shallow) {
 433              // Each element in the array and iteration, the return value as the demo passed to the next iteration
 434              return _.reduce(array, function(memo, value) {
 435                  // If the element is an array, the following judgment:
 436                  // - if not deep merge, the data will be the current array and prior to connect using Array.prototype.concat
 437                  // If support for deep with iteration, call the flatten method, until the bottom element is no longer an array type
 438                  if(_.isArray(value))
 439                      return memo.concat( shallow ? value : _.flatten(value));
 440                  // Data (value) has been at the bottom, is no longer an array type, the data is merged into the memo and returns
 441                  memo[memo.length] = value;
 442                  return memo;
 443              }, []);
 444          };
 445          // Screening and return difference in the array with the specified data not equal data (notes can be difference reference method)
 446          _.without = function(array) {
 447              return _.difference(array, slice.call(arguments, 1));
 448          };
 449          // The array of data (compared to using the = = =)
 450          // When the isSorted parameter is not false, will in turn for the elements in the array to call the include method, to check whether the same elements have been added to the return value (array).
 451          // If the call is sure before array data sequence, isSorted can be set to true, it will be compared with the last element to exclude the same value, use the isSorted efficiency will be higher than the include default mode
 452          // The default uniq method compared to the array of data, if the declaration of iterator processor, it will create an array processor according to the comparison, compared to the array of data as the standard, but the only data ultimately back is still the original array
 453          _.uniq = _.unique = function(array, isSorted, iterator) {
 454              // If you use the iterator processor, the first in the array data will go through to the iterator, and returns a new array a after treatment
 455              // The new array is used as a baseline for comparison
 456              var initial = iterator ? _.map(array, iterator) : array;
 457              // A temporary array is used to record the results of handling.
 458              var results = [];
 459              // If only 2 values in the array, you do not need to use the include method to compare, set isSorted to true to improve operation efficiency
 460              if(array.length <3)
 461                  isSorted = true;
 462              // Use the reduce method of iteration and accumulation processing results
 463              // The initial variable is the need for reference data comparison, it may be the original array processor, may also be the result set (if set iterator)
 464              _.reduce(initial, function(memo, value, index) {
 465                  // If the isSorted parameter is true, then directly use = = = is recorded in the last data
 466                  // If the isSorted parameter is false, using each data set in the include method are compared
 467                  if( isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
 468                      // No duplicate data records have been compared by memo
 469                      // According to the state of the iterator parameters, record the data in memo may be the original data, can also be processed data
 470                      memo.push(value);
 471                      // The processing results stored in the array is always in the original array data
 472                      results.push(array[index]);
 473                  }
 474                  return memo;
 475              }, []);
 476              // Returns the processing result, it only contains no duplicate data array
 477              return results;
 478          };
 479          // Union method and uniq method and the same, difference is that union allows the parameters into the plurality of array
 480          _.union = function() {
 481              // Union of the plurality of array parameters of shallow into passing an array object to the uniq method
 482              return _.uniq(_.flatten(arguments, true));
 483          };
 484          // Gets the current array element intersection with one or more arrays
 485          // Start one or more arrays are compared for the need from the second parameters
 486          _.intersection = _.intersect = function(array) {
 487              // Rest variable records need to compare the other array object
 488              var rest = slice.call(arguments, 1);
 489              // Use the uniq method to remove the redundant data in the array, to avoid repeated calculation
 490              // The array data is filtered by the processor, and returns the qualified (compared to the same data elements)
 491              return _.filter(_.uniq(array), function(item) {
 492                  // Verify that each element in the array contains the need to compare data using the every method
 493                  // If you include the comparative data are all in an array, all returns true, if any one does not contain the elements of the array, false is returned
 494                  return _.every(rest, function(other) {
 495                      // The other parameter storage every need comparative array
 496                      // Item stores need comparative data in the array
 497                      // Use the indexOf method to search for the existence of the element in the array (notes can be indexOf reference method)
 498                      return _.indexOf(other, item) >= 0;
 499                  });
 500              });
 501          };
 502          // Screening and return difference in the array with the specified data not equal data
 503          // This function is generally used to delete the specified array of data, and get the new array removed
 504          // Interaction with without equal to the method, the data is contained in the array is not allowed in the without method parameter form, while the difference method parameter form recommended is an array (you can also use the same parameters and without form)
 505          _.difference = function(array) {
 506              // All parameters of second parameters, as an array of merger (only with the first layer, but not deep merger)
 507              // The rest variable to store the validation data, in the method used with original data comparison
 508              var rest = _.flatten(slice.call(arguments, 1), true);
 509              // Filtering the array data after the merger, filter condition is to validate the data parameter specified does not contain the current array of content
 510              // Will match the filter data are combined into a new array and returns
 511              return _.filter(array, function(value) {
 512                  return !_.include(rest, value);
 513              });
 514          };
 515          // Returns the same position of each array of data as a new two-dimensional array, the array length returned to afferent array length maximum prevail in other array parameters, blank position using the undefined filling
 516          // The zip method should contain multiple parameters, and each parameter should be both array
 517          _.zip = function() {
 518              // The parameter is converted to an array, the args is a two-dimensional array
 519              var args = slice.call(arguments);
 520              // Calculated for each array length, and returns the maximum length
 521              var length = _.max(_.pluck(args, 'length'));
 522              // In accordance with the maximum length values create a new empty array, the array is used to store the processing results
 523              var results = new Array(length);
 524              // Circulating the maximum length, in each cycle will call the pluck method to get the same position of each array of data (in order from the 0 to last position)
 525              // The acquired data is stored in a new array, into the results and back
 526              for(var i = 0; i <length; i++)
 527              results[i] = _.pluck(args, "" + i);
 528              // The result is a two-dimensional array
 529              return results;
 530          };
 531          // The search for an element first appeared in the array position, if the element does not exist it returns -1
 532          // You used to search for matching elements.
 533          _.indexOf = function(array, item, isSorted) {
 534              if(array == null)
 535                  return -1;
 536              var i, l;
 537              if(isSorted) {
 538                  i = _.sortedIndex(array, item);
 539                  return array[i] === item ? i : -1;
 540              }
 541              // Prior to calling the indexOf method provided by the host environment
 542              if(nativeIndexOf && array.indexOf === nativeIndexOf)
 543                  return array.indexOf(item);
 544              // Circulation and returns the position of the first occurrence of elements
 545              for( i = 0, l = array.length; i <l; i++)
 546              if( i in array && array[i] === item)
 547                  return i;
 548              // Element not found, return -1
 549              return -1;
 550          };
 551          // Returns the last appeared in the array of elements in a position, if the element does not exist it returns -1
 552          // You used to search for matching elements.
 553          _.lastIndexOf = function(array, item) {
 554              if(array == null)
 555                  return -1;
 556              // Prior to calling the lastIndexOf method provided by the host environment
 557              if(nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf)
 558                  return array.lastIndexOf(item);
 559              var i = array.length;
 560              // Circulation and returns the last occurrence of elements
 561              while(i--)
 562              if( i in array && array[i] === item)
 563                  return i;
 564              // Element not found, return -1
 565              return -1;
 566          };
 567          // According to the interval and step, produce a series of integers, and as returns an array
 568          // The start parameter indicates the minimum number
 569          // The stop parameter indicates the maximum number of
 570          // The step parameter indicates the generation between multiple numerical value of the step size
 571          _.range = function(start, stop, step) {
 572              // Parameter control
 573              if(arguments.length <= 1) {
 574                  // If no parameters, start = 0, stop = 0, any data will not be generated in the cycle, returns an empty array
 575                  // If there are 1 parameters, the parameters specified for the stop, start = 0
 576                  stop = start || 0;
 577                  start = 0;
 578              }
 579              // Generate integer step value, the default is 1
 580              step = arguments[2] || 1;
 581 
 582              // According to the calculation of the maximum interval and length will be generated
 583              var len = Math.max(Math.ceil((stop - start) / step), 0);
 584              var idx = 0;
 585              var range = new Array(len);
 586 
 587              // Generated list of integers, and stored in the range array
 588              while(idx < len) {
 589                  range[idx++] = start;
 590                  start += step;
 591              }
 592 
 593              // Returns a list of results
 594              return range;
 595          };
 596          // Correlation function method
 597          // ------------------
 598 
 599          // Create a setting for the prototype public function object
 600          var ctor = function() {
 601          };
 602          // The execution context for a function binding, under any circumstances, call the function, function of the this is a pointer to context
 603          // Binding function, at the same time to transfer function call parameters
 604          _.bind = function bind(func, context) {
 605              var bound, args;
 606              // Prior to calling the bind method provided by the host environment
 607              if(func.bind === nativeBind && nativeBind)
 608                  return nativeBind.apply(func, slice.call(arguments, 1));
 609              // The func parameter must be a function (Function) type
 610              if(!_.isFunction(func))
 611                  throw new TypeError;
 612              // Args variable to store the bind third start parameter list, every call will be passed to the func function
 613              args = slice.call(arguments, 2);
 614              return bound = function() {
 615                  if(!(this instanceof bound))
 616                      return func.apply(context, sargs.concat(slice.call(arguments)));
 617                  ctor.prototype = func.prototype;
 618                  var self = new ctor;
 619                  var result = func.apply(self, args.concat(slice.call(arguments)));
 620                  if(Object(result) === result)
 621                      return result;
 622                  return self;
 623              };
 624          };
 625          // The specified function, all the functions or the object itself on the bound to the object itself, the bound function is invoked, the context object always points to the object itself
 626          // The method generally used in processing object events, for example:
 627          // _(obj).bindAll(); // Or_(obj).bindAll('handlerClick');
 628          // document.addEventListener('click', obj.handlerClick);
 629          // In the handlerClick method, the context is still the obj object
 630          _.bindAll = function(obj) {
 631              // The second parameter to expressed the need for binding function name
 632              var funcs = slice.call(arguments, 1);
 633              // If you do not specify a specific function name, the default binding object itself all types of Function attributes
 634              if(funcs.length == 0)
 635                  funcs = _.functions(obj);
 636              // Circulation and all of the functions on the set for the obj object itself
 637              // Methods each method itself does not traverse the object prototype chain, but the FuncS list is obtained by the _.functions method, it has been included in the prototype chain method
 638              each(funcs, function(f) {
 639                  obj[f] = _.bind(obj[f], obj);
 640              });
 641              return obj;
 642          };
 643          // The memoize method returns a function, the function integration of the cache, the calculated value cache to the local variables and return in the next call
 644          // If the result is a large object or data, use should be considered when memory usage
 645          _.memoize = function(func, hasher) {
 646              // The memo object used to store the cache
 647              var memo = {};
 648              // The hasher parameter should be a function, it is used to return a key, the key as a read cache ID
 649              // If key is not specified, the first parameter is the default function as key, if the first parameter is the compound data types, may return similar to [Object object] key, the key may cause the subsequent calculation data is incorrect
 650              hasher || ( hasher = _.identity);
 651              // Returns a function, the function by first checking the cache, and then call to not cache the data
 652              return function() {
 653                  var key = hasher.apply(this, arguments);
 654                  return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
 655              };
 656          };
 657          // Delay the execution of a function
 658          // The wait unit is MS, third parameters are passed to the start will be executive function
 659          _.delay = function(func, wait) {
 660              var args = slice.call(arguments, 2);
 661              return setTimeout(function() {
 662                  return func.apply(null, args);
 663              }, wait);
 664          };
 665          // Delayed function
 666          // JavaScript setTimeout will be executed on a separate function stack, execution time is after the function call in the current stack are executed
 667          // Defer function is executed after 1ms, the purpose is to func function in a single stack, wait for the current function is executed after the completion of execution
 668          // The defer method is generally used to handle DOM operations priority implementation experience, logical flow of correct and fluent interaction
 669          _.defer = function(func) {
 670              return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
 671          };
 672          // Function of throttle method, throttle method is used for the implementation of frequency control function, in control time interval, frequent calls the function will not be executed multiple times
 673          // In a time interval if multiple calls to the function, interval cut-off will automatically call a, do not need to wait until after the deadline to manually call (not automatically invoked with the return value)
 674          // The throttle function is used to treat complex and is called frequently by calling the function, frequency throttle control function, save processing resources
 675          // For example, the window.onresize binding event function, or element.onmousemove binding event function, can be packaged with throttle
 676          // The throttle method returns a function, the function will automatically call func and throttle control
 677          _.throttle = function(func, wait) {
 678              var context, args, timeout, throttling, more, result;
 679              // WhenDone variable called debounce, so in many times to call a function, the last call will cover before calling the timer, clear the state function is only executed once
 680              // The whenDone function calls in the last function execution time interval cut-off, remove some state recording throttle and call.
 681              var whenDone = _.debounce(function() {
 682                  more = throttling = false;
 683              }, wait);
 684              // Returns a function, and throttle control within the function
 685              return function() {
 686                  // Save the function execution context and parameters
 687                  context = this;
 688                  args = arguments;
 689                  // The later function in a time interval as function calls executed
 690                  var later = function() {
 691                      // Remove timeout handle, convenient for the next function call
 692                      timeout = null;
 693                      // More recorded in the last call to the interval stop, whether the repeated calling the function
 694                      // If repeated calls to the function, in the interval stop automatically when the function is called again
 695                      if(more)
 696                          func.apply(context, args);
 697                      // Call whenDone, used to remove the throttle state in the time interval
 698                      whenDone();
 699                  };
 700                  // Timeout records the time interval handlers execute the function a
 701                  // Timeout time interval cut-off when calling the later function, later will remove timeout, and check whether the need to call the function
 702                  if(!timeout)
 703                      timeout = setTimeout(later, wait);
 704                  // Throttling variable records the last call time interval is over, whether in the throttling process
 705                  // Throttling in each invocation is set to true, expressed the need for throttling, set to false in the time interval (cut-off when implementing the whenDone function)
 706                  if(throttling) {
 707                      // Throttling process is called multiple times, the recording of a state in the more, expressed the need for automatic re call functions in the time interval when the cutoff
 708                      more = true;
 709                  } else {
 710                      // There is no throttling process, may be the first call to the function, or have more than one call interval, can directly call the function
 711                      result = func.apply(context, args);
 712                  }
 713                  // Call whenDone, used to remove the throttle state in the time interval
 714                  whenDone();
 715                  // Throttling variable record function call throttle state
 716                  throttling = true;
 717                  // Return the result of the call
 718                  return result;
 719              };
 720          };
 721          // Debounce is similar to throttle, a function for the throttle, they are different in that:
 722          // Implementation of frequency - throttle attention function, in the specified frequency function is executed only once;
 723          // The debounce function is more concerned about the - function execution interval, is called time cannot function two times less than the specified time;
 724          // If the two functions of the execution interval is less than wait, the timer will be cleaned and re create, which means continuous frequent calls to the function, function has not be executed, until a call and a call time is not less than wait MS
 725          // The debounce function is generally used to control needs to perform a period of time after the operation, such as user input is completed after 200ms prompts the user, you can use the debounce package to a function, bind to the onkeyup event
 726          // ----------------------------------------------------------------
 727          // @param {Function} Func represents a function to be executed
 728          // @param {Number} Wait representation allows the time interval, in the time range of repeated calls will be re delayed wait MS
 729          // @param {Boolean} Immediate said the function call is executed immediately, true immediately call, false calls in the time when the cutoff
 730          // The debounce method returns a function, the function will automatically call func and throttle control
 731          _.debounce = function(func, wait, immediate) {
 732              // Timeout is used for recording function on a call execution state (timer handler)
 733              // When timeout is null, said on a call has ended
 734              var timeout;
 735              // Returns a function, and throttle control within the function
 736              return function() {
 737                  // Keep the context object function and parameters
 738                  var context = this, args = arguments;
 739                  var later = function() {
 740                      // Set the timeout to null
 741                      // The later function will be invoked in the allowed time
 742                      // When you call this function, that function on an execution time has exceeded the agreed time interval, is allowed at this time after a call
 743                      timeout = null;
 744                      if(!immediate)
 745                          func.apply(context, args);
 746                  };
 747                  // If the function is set to be executed immediately, and the last call time interval has passed, immediately call the function
 748                  if(immediate && !timeout)
 749                      func.apply(context, args);
 750                  // Create a timer is used to check and set the function call state
 751                  // SetTimeout handle to empty the last time before creating a timer function, regardless of whether a binding has been executed
 752                  // If the function calls, execute the function last time has not yet begun (usually immediate is set to false), then the function execution time will be delayed, so the timeout handlers will be re created
 753                  clearTimeout(timeout);
 754                  // Call the later function in the allowed time
 755                  timeout = setTimeout(later, wait);
 756              };
 757          };
 758          // Create a will only be executed a function, if this function is invoked repeatedly, will return to the first implementation results
 759          // This function is used for obtaining and fixed logic data calculation, such as access to the user's browser type
 760          _.once = function(func) {
 761              // Ran record whether the function to be executed
 762              // Memo recording function last execution results
 763              var ran = false, memo;
 764              return function() {
 765                  // If the function has been executed, directly returns the result of the first implementation
 766                  if(ran)
 767                      return memo;
 768                  ran = true;
 769                  return memo = func.apply(this, arguments);
 770              };
 771          };
 772          // Returns a function, the function will use the current function as an argument to a wrapper function
 773          // In the wrapper function can be the first parameter in the call to the function, and returns the result
 774          // Commonly used for low coupling combination of call multiple processing function
 775          _.wrap = function(func, wrapper) {
 776              return function() {
 777                  // Will the current function as the first argument, passed to the wrapper function
 778                  var args = [func].concat(slice.call(arguments, 0));
 779                  // The processing results returned by the wrapper function
 780                  return wrapper.apply(this, args);
 781              };
 782          };
 783          // The multiple function together, in accordance with the order of parameter passing, and then returns a function value is passed as a parameter to a time before a function as a parameter to continue processing
 784          // _.compose(A, B, C); Equivalent to A(B(C()));
 785          // The disadvantage is that by the parameter function treatment correlation number can only have one, if need to pass multiple parameters, can be assembled by Array or Object composite data type
 786          _.compose = function() {
 787              // Gets the function list, all parameters are required for the Function type
 788              var funcs = arguments;
 789              // Returns a handle for the function call
 790              return function() {
 791                  // From the forward followed executive function, and will record the return value is passed as a parameter to a function to continue processing
 792                  var args = arguments;
 793                  for(var i = funcs.length - 1; i >= 0; i--) {
 794                      args = [funcs[i].apply(this, args)];
 795                  }
 796                  // Returns the last value returned to the calling function
 797                  return args[0];
 798              };
 799          };
 800          // Returns a function, the function is called counter, when the function is called times (or more than times), the func function will be executed
 801          // The after method is commonly used for asynchronous counter, for example, after the completion of all required to perform a function in a AJAX request, you can use the after in each AJAX request is completed call
 802          _.after = function(times, func) {
 803              // If not specified or a specified number of invalid, then func is called directly
 804              if(times <= 0)
 805                  return func();
 806              // Returns a counter function
 807              return function() {
 808                  // Each call to counter function times minus 1, after a call to times executive func function return value and return to the func function
 809                  if(--times <1) {
 810                      return func.apply(this, arguments);
 811                  }
 812              };
 813          };
 814          // Object related method
 815          // ----------------
 816 
 817          // The attribute name list to retrieve an object (does not contain the attribute prototype in the chain)
 818          _.keys = nativeKeys ||
 819          function(obj) {
 820              if(obj !== Object(obj))
 821                  throw new TypeError('Invalid object');
 822              var keys = [];
 823              // Record and returns an object of all the attribute name
 824              for(var key in obj)
 825              if(_.has(obj, key))
 826                  keys[keys.length] = key;
 827              return keys;
 828          };
 829 
 830          // A list of all the value of the property returns an object (does not contain the attribute prototype in the chain)
 831          _.values = function(obj) {
 832              return _.map(obj, _.identity);
 833          };
 834          // All property gets a value of type Function objects in the key list, and sorted by key (contain attributes in the prototype chain)
 835          _.functions = _.methods = function(obj) {
 836              var names = [];
 837              for(var key in obj) {
 838                  if(_.isFunction(obj[key]))
 839                      names.push(key);
 840              }
 841              return names.sort();
 842          };
 843          // Property to one or more objects (including attribute prototype in the chain), copied to the obj object, if the property is covered with the same name already exists
 844          _.extend = function(obj) {
 845              // One or more objects in the each cycle parameters
 846              each(slice.call(arguments, 1), function(source) {
 847                  // The object of all the attributes of the copy or covered by the obj object
 848                  for(var prop in source) {
 849                      obj[prop] = source[prop];
 850                  }
 851              });
 852              return obj;
 853          };
 854          // Returns a new object, and copied from the obj to the new object in the specified property
 855          // The second parameter to specify need to copy the attribute names (support for multiple parameters and deep array)
 856          _.pick = function(obj) {
 857              // Creates an object for the specified attribute, copy
 858              var result = {};
 859              // From the second parameter into a stored attribute names list array
 860              each(_.flatten(slice.call(arguments, 1)), function(key) {
 861                  // A list of the names of cyclic property, if the property in obj, then copy it to the result object
 862                  if( key in obj)
 863                      result[key] = obj[key];
 864              });
 865              // Returns the copy results
 866              return result;
 867          };
 868          // The obj does not exist or is converted to Boolean type value for the false attribute, copy from obj to one or more objects in the specified in the argument
 869          // Generally used to the default value of the specified object
 870          _.defaults = function(obj) {
 871              // From the second parameter can specify multiple objects, attributes of these objects will be successively copied to the obj object (if the property does not exist in the obj object)
 872              each(slice.call(arguments, 1), function(source) {
 873                  // All the attributes of each object in the traversal
 874                  for(var prop in source) {
 875                      // If there is no value is converted to Boolean type value is false or obj attribute, the attribute is copied to the obj
 876                      if(obj[prop] == null)
 877                          obj[prop] = source[prop];
 878                  }
 879              });
 880              return obj;
 881          };
 882          // Creates a copy of an obj, returns a new object, the object contains all the attributes and values of the state in obj
 883          // The clone function does not support deep copy, such as an attribute in obj for an object, then the object is not copied
 884          // If obj is an array, it will create the same array object
 885          _.clone = function(obj) {
 886              // Does not support non array and object type data
 887              if(!_.isObject(obj))
 888                  return obj;
 889              // Copy and return an array or object
 890              return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
 891          };
 892          // The implementation of a function, and obj will be passed to the function as a parameter, the function returns a obj object after the completion of the execution of the final
 893          // Generally when creating a chain method will use the tap method, for example:
 894          // _(obj).chain().tap(click).tap(mouseover).tap(mouseout);
 895          _.tap = function(obj, interceptor) {
 896              interceptor(obj);
 897              return obj;
 898          };
 899          // The EQ function calls in the isEqual method, value equality is used to compare two data
 900          // With = = = is different, EQ is more concerned about the value of data
 901          // If the comparison is the two composite data type, not just whether the comparison of quotes from the same, and deep comparison (compare structure and data on two objects)
 902          function eq(a, b, stack) {
 903              // Check the two simple data type values are equal
 904              // For complex data types, if they come from the same reference, is considered equal
 905              // If the compared values which contains 0, check another if the value is -0, because 0 = = = -0 is established
 906              // 1 / 0 = 1 / -0 is not valid (1 / 0 = Infinity, 1 / -0 value is -Infinity, but Infinity is not equal to -Infinity)
 907              if(a === b)
 908                  return a !== 0 || 1 / a == 1 / b;
 909              // To convert data into Boolean type if the value is false, the two values of the data type are equal (as null and undefined, false, 0, an empty string, is equal in the non strict comparison value)
 910              if(a == null || b == null)
 911                  return a === b;
 912              // If the data is to compare a Underscore package (object has a _chain property of the Underscore object is considered as)
 913              // The object is to obtain the data itself. (accessed via _wrapped), and then to compare data
 914              // Their relationship is similar to a jQuery package DOM object, DOM object creation and the browser itself
 915              if(a._chain)
 916                  a = a._wrapped;
 917              if(b._chain)
 918                  b = b._wrapped;
 919              // If the object provides custom isEqual method (isEqual method, isEqual method of the Undersocre object is not here because in the last step is to understand the letter Undersocre object)
 920              // The isEqual method is used to customize the object with another object comparison
 921              if(a.isEqual && _.isFunction(a.isEqual))
 922                  return a.isEqual(b);
 923              if(b.isEqual && _.isFunction(b.isEqual))
 924                  return b.isEqual(a);
 925              // The two data types to be verified
 926              // Gets the object data type of a (by Object.prototype.toString method)
 927              var className = toString.call(a);
 928              // If the data type and the object a does not match the B, that value does not match the two data
 929              if(className != toString.call(b))
 930                  return false;
 931              // Perform here, can ensure that the two data to be compared are composite data type, and the data types are equal
 932              // By the examination of switch data types are different, the comparison for different data types
 933              // (this does not include types of arrays and objects, because they may contain deeper data, will make deep comparison at the back)
 934              switch (className) {
 935                  case '[object String]':
 936                      // If comparison is a string type (where a is through the new (String) to create a string)
 937                      // It converts B to String object matching (matching here is not data type checking, strict because of references to them are not from the same object)
 938                      // In comparison to call = =, will automatically call the object's toString () method returns a string, two simple data types
 939                      return a == String(b);
 940                  case '[object Number]':
 941                      // The +a will turn a into a Number, if a is converted and before conversion is not equal, is that a is a NaN type
 942                      // Because NaN and NaN are not equal, so when the a value is NaN, not easy to use a = B to match, but with the same method to check whether B is NaN (that is b != +b)
 943                      // When the a value is a non NaN data, check whether a is 0, when B is -0, 0 = -0 is established (in fact they belong to two different data in logic)
 944                      return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
 945                  case '[object Date]':
 946                  // The date type do not use return or break, it will continue to the next step (whether a data type is Boolean type, because the next step will be the Boolean type examination)
 947                  case '[object Boolean]':
 948                      // Add the date or the boolean type is converted to digital
 949                      // The type of date will be converted to the time stamp of a value type (invalid date format will be converted to NaN)
 950                      // Boolean types, the true is converted to 1, the false is converted to 0
 951                      // Comparison of two dates or boolean type is converted to digital is equal
 952                      return +a == +b;
 953                  case '[object RegExp]':
 954                      // Regular expression types, string access expression by source
 955                      // String check two expressions are equal
 956                      // The global property inspection two expressions are the same (including G, i, m)
 957                      // If equal, is that the two data.
 958                      return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase;
 959              }
 960              // When the execution time, the AB two data should be the objects of the same type or an array type
 961              if( typeof a != 'object' || typeof b != 'object')
 962                  return false;
 963              // Stack (heap) is in the isEqual calls a EQ function inside an empty array passed behind the call to the EQ method, comparison of the inner iteration objects and data will also transfer
 964              // Length recording stack length
 965              var length = stack.length;
 966              while(length--) {
 967                  // If an object and data heap a matching, is considered equal
 968                  if(stack[length] == a)
 969                      return true;
 970              }
 971              // The data of a added to the heap
 972              stack.push(a);
 973              // Definitions of some local variables
 974              var size = 0, result = true;
 975              // Through the recursive deep comparison objects and arrays
 976              if(className == '[object Array]') {
 977                  // By comparing data for array types
 978                  // Size records the length of the array
 979                  // Result two the length of the array is consistent, if the length is not consistent, the method will return result (false)
 980                  size = a.length;
 981                  result = size == b.length;
 982                  // If the two array of the same length
 983                  if(result) {
 984                      // Compared to call the EQ method iterating over the elements in the array (if a two-dimensional array or object, an array containing the EQ method will be deep comparison)
 985                      while(size--) {
 986                          // There are the current index in ensuring that the two elements of the array, is a call to the EQ method of deep (passed to the EQ method will heap)
 987                          // The result of the comparison is stored in the result variable, if result is false (that is, an element of data in comparison to inconsistent), stop the iteration
 988                          if(!( result = size in a == size in b && eq(a[size], b[size], stack)))
 989                              break;
 990                      }
 991                  }
 992              } else {
 993                  // By comparing data for object type
 994                  // If two objects are not in the same class (via the constructor attribute comparison), think that two objects are not equal
 995                  if('constructor' in a != 'constructor' in b || a.constructor != b.constructor)
 996                      return false;
 997                  // The deep comparison of the two data in the object
 998                  for(var key in a) {
 999                      if(_.has(a, key)) {
1000                          // Size is used for quantitative attributes recorded before, because this traversal is a property of the a object, the attribute data and compare the B object
1001                          // When the number of attributes in the B object when the object of excess a, the logical, but the two objects are not equal
1002                          size++;
1003                          // The iterative EQ method is called, the deep comparison of the two object attribute values
1004                          // The result of the comparison is logged to the result variable, when compared to not equal to stop iterative data
1005                          if(!( result = _.has(b, key) && eq(a[key], b[key], stack)))
1006                              break;
1007                      }
1008                  }
1009                  // Deep comparison is completed, it can ensure that all the data in the object in a, the same data also exists in the B object
1010                  // According to the size (object attribute length) number check object properties in B and a objects are equal
1011                  if(result) {
1012                      // Traversal of all properties of the object in B
1013                      for(key in b) {
1014                          // When the size has been to 0 (i.e., object property in a populations have been completed, and the B traversal) existing in the attribute, the object properties in B more than a objects
1015                          if(_.has(b, key) && !(size--))
1016                              break;
1017                      }
1018                      // When the objects in the B object properties than a, that the two objects are not equal
1019                      result = !size;
1020                  }
1021              }
1022              // Function is executed, the reactor removes the first data (in comparison to the object or array, iterative EQ method, there may be multiple sets of data)
1023              stack.pop();
1024              // Returns the result record compared the final results
1025              return result;
1026          }
1027 
1028          // According to the comparison of the two data values (support complex data type), external method of internal function EQ
1029          _.isEqual = function(a, b) {
1030              return eq(a, b, []);
1031          };
1032          // Check whether the data is null, contain a '', false, 0, null, undefined, NaN, an empty array (the length of the array is 0) and the object (the object itself has no attributes)
1033          _.isEmpty = function(obj) {
1034              // The obj is converted to Boolean type value is false
1035              if(obj == null)
1036                  return true;
1037              // Check the object or the length of the string is 0
1038              if(_.isArray(obj) || _.isString(obj))
1039                  return obj.length === 0;
1040              // Check the object (using for in cycle will be the first circular attribute, the object itself is the second attribute prototype in the chain), so if the first property belongs to the object, then the object is not a null object
1041              for(var key in obj)
1042              if(_.has(obj, key))
1043                  return false;
1044              // All data types are not verified, is an empty data
1045              return true;
1046          };
1047          // To verify whether the object is a DOM object
1048          _.isElement = function(obj) {
1049              return !!(obj && obj.nodeType == 1);
1050          };
1051          // To verify whether the object is an array type, preferred to call the isArray method provided by the host environment
1052          _.isArray = nativeIsArray ||
1053          function(obj) {
1054              return toString.call(obj) == '[object Array]';
1055          };
1056 
1057          // To verify whether the object is an object of a composite data type (i.e. non basic data type String, Boolean, Number, null, undefined)
1058          // If the basic data types are created by new, is also belong to the object type
1059          _.isObject = function(obj) {
1060              return obj === Object(obj);
1061          };
1062          // Check whether a data is a arguments parameter object
1063          _.isArguments = function(obj) {
1064              return toString.call(obj) == '[object Arguments]';
1065          };
1066          // Validation of the isArguments function, if the operating environment can not be normal to validate arguments data types, is redefining the isArguments method
1067          if(!_.isArguments(arguments)) {
1068              // For the environment could not be verified by toString arguments type, the callee method by calling the arguments unique to verify
1069              _.isArguments = function(obj) {
1070                  // Callee is an attribute of arguments, pointing to arguments the reference function itself
1071                  return !!(obj && _.has(obj, 'callee'));
1072              };
1073          }
1074 
1075          // To verify whether the object is a type of function
1076          _.isFunction = function(obj) {
1077              return toString.call(obj) == '[object Function]';
1078          };
1079          // To verify whether the object is a string type
1080          _.isString = function(obj) {
1081              return toString.call(obj) == '[object String]';
1082          };
1083          // To verify whether the object is a numeric type
1084          _.isNumber = function(obj) {
1085              return toString.call(obj) == '[object Number]';
1086          };
1087          // Check a number is a valid number and effective range (Number type, value in the negative - positive infinity)
1088          _.isFinite = function(obj) {
1089              return _.isNumber(obj) && isFinite(obj);
1090          };
1091          // Check whether the data of type NaN (all data only NaN and NaN are not equal)
1092          _.isNaN = function(obj) {
1093              return obj !== obj;
1094          };
1095          // Check whether the data of Boolean type
1096          _.isBoolean = function(obj) {
1097              // Support the literal and object form of Boolean data
1098              return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
1099          };
1100          // Check whether the data is a Date type
1101          _.isDate = function(obj) {
1102              return toString.call(obj) == '[object Date]';
1103          };
1104          // Check whether the data is a regular expression types
1105          _.isRegExp = function(obj) {
1106              return toString.call(obj) == '[object RegExp]';
1107          };
1108          // Check whether the data is Null
1109          _.isNull = function(obj) {
1110              return obj === null;
1111          };
1112          // Check whether the data is Undefined (undefined) value
1113          _.isUndefined = function(obj) {
1114              return obj ===
1115              void 0;
1116          };
1117          // Check whether a property belonging to the object itself, rather than the prototype chain
1118          _.has = function(obj, key) {
1119              return hasOwnProperty.call(obj, key);
1120          };
1121          // Tool function
1122          // -----------------
1123 
1124          // Give up _ (underlined) named Underscore object, and returns a Underscore object, generally used to avoid naming conflicts or standard way of naming
1125          // For example:
1126          // var us = _.noConflict(); // Cancel _ (underlined) name, and the Underscore object stored in the US variable
1127          // console.log(_); // _(underlined) have been unable to access the Underscore object, and to restore the value before the definition of Underscore
1128          _.noConflict = function() {
1129              // The previousUnderscore variable to record the Underscore definition before _ (underlined) value
1130              root._ = previousUnderscore;
1131              return this;
1132          };
1133          // Returns the same values and parameters, are commonly used to access a data conversion as a function of acquisition mode (internal method for building as the default handler function)
1134          _.identity = function(value) {
1135              return value;
1136          };
1137          // The iterated function specified executed n times (non parametric)
1138          _.times = function(n, iterator, context) {
1139              for(var i = 0; i <n; i++)
1140              iterator.call(context, i);
1141          };
1142          // The special characters in the string is converted to HTML HTML entity, including & <> " ' \
1143          _.escape = function(string) {
1144              return ('' + string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/');
1145          };
1146          // The specified attribute of an object, returns the attribute values, if the property is a function, it will execute the function and returns the result
1147          _.result = function(object, property) {
1148              if(object == null)
1149                  return null;
1150              // Gets the value of the object
1151              var value = object[property];
1152              // If the value is a function, executing and return, otherwise it will return
1153              return _.isFunction(value) ? value.call(object) : value;
1154          };
1155          // Add a series of custom methods to the Underscore object, to expand the Underscore plugin
1156          _.mixin = function(obj) {
1157              // Obj is a collection of a series of custom methods on objects, here by the method of each traversal objects
1158              each(_.functions(obj), function(name) {
1159                  // Add a custom method to construct the Underscore object through the addToWrapper function, used to support the object.
1160                  // At the same time will add the method to _ itself, to support the call function
1161                  addToWrapper(name, _[name] = obj[name]);
1162              });
1163          };
1164          // To obtain a globally unique identifier, identifying began to accumulate from 0
1165          var idCounter = 0;
1166          // Prefix said the ID prefix, if you do not specify a prefix is returned directly identification, generally used to create a unique ID for the object or DOM
1167          _.uniqueId = function(prefix) {
1168              var id = idCounter++;
1169              return prefix ? prefix + id : id;
1170          };
1171          // Define a template definition symbols, used in the template method
1172          _.templateSettings = {
1173              // JavaScript executable code delimiter
1174              evaluate : /<%([\s\S]+?)%>/g,
1175              // The direct output of the definition of variables.
1176              interpolate : /<%=([\s\S]+?)%>/g,
1177              // HTML needs to be output as a string (the special symbols into a string representation of the delimiter)
1178              escape : /<%-([\s\S]+?)%>/g
1179          };
1180 
1181          var noMatch = /.^/;
1182 
1183          // The escapes object records the corresponding relationship between the need for special symbol of mutual conversion and string, as the index using the conversion between the two
1184          // According to the definition of special character string
1185          var escapes = {
1186              '\\' : '\\',
1187              "'" : "'",
1188              'r' : '\r',
1189              'n' : '\n',
1190              't' : '\t',
1191              'u2028' : '\u2028',
1192              'u2029' : '\u2029'
1193          };
1194          // The traversal of all special characters in the string, and the special character string as key records
1195          for(var p in escapes)
1196          escapes[escapes[p]] = p;
1197          // Special symbol substitution needs to define a template, contain a backslash, single quotes, carriage returns, newline, tab, row delimiters, paragraph separator
1198          // In the special symbols in the string is converted to a string using the form
1199          var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
1200          // In the inversion of special symbol string (replacement) are used
1201          var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
1202 
1203          // Special sign reversal in the string
1204          // Involve in the template need to perform the JavaScript source code, the need for special sign reversal, if appear to HTML entity or a string, throws a syntax error
1205          var unescape = function(code) {
1206              return code.replace(unescaper, function(match, escape) {
1207                  return escapes[escape];
1208              });
1209          };
1210          // Underscore template parsing method, used to fill data into a template string
1211          // Template analysis process:
1212          // 1 special symbols in the template is converted to a string
1213          // 2 Analytical escape form tag, the content analysis for a HTML entity
1214          // 3 Analytical interpolate form tag, the output variable
1215          // 4 Analytical evaluate form tag, create executable JavaScript code
1216          // The 5 generation of a function, the function can be directly filled into the templates and the string is returned after filling in the data
1217          // 6 according to handle parameter returns a string or processing function after filling.
1218          // -------------------
1219          // In the template body, can be accessed through argments 2 parameters, respectively, to fill the data (called obj) and Underscore (object name for the _)
1220          _.template = function(text, data, settings) {
1221              // The template configuration, if you do not specify a configuration item, use the templateSettings specified in the configuration items
1222              settings = _.defaults(settings || {}, _.templateSettings);
1223 
1224              // Begin executable source code template analysis
1225              var source = "__p+='" + text.replace(escaper, function(match) {
1226                  // The special symbols transfer as a string
1227                  return '\\' + escapes[match];
1228              }).replace(settings.escape || noMatch, function(match, code) {
1229                  // Analysis of escape form tag <%;%>, the variable contains the HTML into a HTML entity by _.escape function
1230                  return "'+\n_.escape(" + unescape(code) + ")+\n'";
1231              }).replace(settings.interpolate || noMatch, function(match, code) {
1232                  // Analysis of interpolate form tag <%;%>, the template content as a variable in connection with other string together, can serve as a variable output
1233                  return "'+\n(" + unescape(code) + ")+\n'";
1234              }).replace(settings.evaluate || noMatch, function(match, code) {
1235                  // Analysis of evaluate form tag <%;%>, evaluate tags in the storage needed for the implementation of the JavaScript code, the end of the current string together here, and in a new line as the JavaScript syntax, and the content behind again as the beginning of the string, the evaluate tags within the JavaScript code can be properly
1236                  return "';\n" + unescape(code) + "\n;__p+='";
1237              }) + "';\n";
1238              if(!settings.variable)
1239                  source = 'with(obj||{}){\n' + source + '}\n';
1240              source = "var __p='';" + "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" + source + "return __p;\n";
1241 
1242              // Create a function, the source as a function of the execution, the obj and Underscore is passed as a parameter to the function
1243              var render = new Function(settings.variable || 'obj', '_', source);
1244              // If the filled data template is specified, replace the template content, and return to replace the results
1245              if(data)
1246                  return render(data, _);
1247              // If you fill the data is not specified, it returns a function, the function is used to replace the received data to the template
1248              // If the program will have to fill the same template, on the first call is not specified to fill the data then suggest, in reference to obtain processing function, then directly call will improve operation efficiency
1249              var template = function(data) {
1250                  return render.call(this, data, _);
1251              };
1252              // Add the source string is created to the function object, usually used for debugging and testing
1253              template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
1254              // No designated filling data, returns processing function handle
1255              return template;
1256          };
1257          // Method of chain operation support for Underscore objects, see wrapper.prototype.chain
1258          _.chain = function(obj) {
1259              return _(obj).chain();
1260          };
1261          // The Underscore object encapsulates correlation method
1262          // ---------------
1263 
1264          // Create a wrapper, some original data package
1265          // All of the undersocre object through the wrapper function, internal structure and package
1266          // The internal relation between Underscore and wrapper:
1267          // Internal variable _, add the Underscore method related to _, so it can support function calls, such as _.bind()
1268          // - defined inside the wrapper class, the prototype object _ pointing wrapper prototype
1269          // - add method of Underscore related to wrapper prototype, created _ object has a Underscore method
1270          // - add the Array.prototype correlation method to the wrapper prototype, created _ object has a method in Array.prototype
1271          // -new _()It creates and returns a wrapper object (), and the original array is stored in the _wrapped variable, and the original value as the first parameter in the call to the corresponding method
1272          var wrapper = function(obj) {
1273              // The original data stored in the _wrapped property of packaging in the object
1274              this._wrapped = obj;
1275          };
1276          // The prototype object Underscore to wrapper prototype, so by adding wrapper as prototype, the Underscore object will also have the same method
1277          _.prototype = wrapper.prototype;
1278 
1279          // Returns an object, if the current Underscore is called the chain () method (i.e. the _chain attribute is true), it returns a packed Underscore objects, otherwise it returns the object itself
1280          // The result function is used to package object returns Underscore in the construction method of the chain
1281          var result = function(obj, chain) {
1282              return chain ? _(obj).chain() : obj;
1283          };
1284          // To add a custom methods to the Underscore object (the actual is added to the wrapper prototype, and the Underscore object's prototype to wrapper prototype)
1285          var addToWrapper = function(name, func) {
1286              // A name function is added to the wrapper prototype, the function call func function, treatment and support method of chain
1287              wrapper.prototype[name] = function() {
1288                  // To obtain the parameters of the func function, and the original data current is added to the first parameter
1289                  var args = slice.call(arguments);
1290                  unshift.call(args, this._wrapped);
1291                  // Executive function and returns the result, and is packaged by result function each other chain method, if the current call chain () method, it returns the package after the Underscore object, otherwise it returns the object itself
1292                  return result(func.apply(_, args), this._chain);
1293              };
1294          };
1295          // The internal definition of the _ (underlined, namely Underscore method set object) replication method into the prototype chain of wrapper (a prototype chain of Underscore)
1296          // This is in order to construct object type called Underscore objects, these objects will have Underscore methods defined inside
1297          _.mixin(_);
1298 
1299          // Add the related method in Array.prototype to the Underscore object, and call the Array.prototype method directly in the can in the package after the Underscore object
1300          // Such as: _([]).push()
1301          each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
1302              // Gets a reference corresponding methods in Array.prototype
1303              var method = ArrayProto[name];
1304              // Add the method to the Underscore object (actually added to the prototype object, wrapper in a Underscore object is created with this method)
1305              wrapper.prototype[name] = function() {
1306                  // _The original value Underscore object is stored in an wrapped variable
1307                  var wrapped = this._wrapped;
1308                  // Method invocation corresponds to Array and returns the result
1309                  method.apply(wrapped, arguments);
1310                  var length = wrapped.length;
1311                  if((name == 'shift' || name == 'splice') && length === 0)
1312                      delete wrapped[0];
1313                  // Even with the methods of Array, Underscore also supports a method of chain operation
1314                  return result(wrapped, this._chain);
1315              };
1316          });
1317          // On the role as a section of code, we will add some method in the array to the Underscore object, and the method of chain operation
1318          // The difference is that the function of adding a section of code, return the Array object itself (may also be encapsulated Array), concat, join, the slice method returns a new Array object (probably after the Array package)
1319          each(['concat', 'join', 'slice'], function(name) {
1320              var method = ArrayProto[name];
1321              wrapper.prototype[name] = function() {
1322                  return result(method.apply(this._wrapped, arguments), this._chain);
1323              };
1324          });
1325          // The statement of chain operation on the Underscore object
1326          wrapper.prototype.chain = function() {
1327              // This._chain used to indicate whether the current object using chain operation
1328              // To support the method of chain operation data, generally in the specific method returns a Underscore object, the original value stored in the _wrapped property, also can through the value () method to get the original value
1329              this._chain = true;
1330              return this;
1331          };
1332          // Returns the original value package Underscore objects (stored in the _wrapped property)
1333          wrapper.prototype.value = function() {
1334              return this._wrapped;
1335          };
1336      }).call(this);

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Posted by Liz at November 14, 2013 - 2:56 PM