Translation instructions for Chinese annotations

  • 2020-06-22 23:44:28
  • OfStack


// Underscore.js 1.3.3

// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.

// Underscore is freely distributable under the MIT license.

// Portions of Underscore are inspired or borrowed from Prototype,

// Oliver Steele's Functional, and John Resig's Micro-Templating.

// For all details and documentation:

// http://documentcloud.github.com/underscore

(function() {

 

  //  create 1 Global object ,  Is represented in the browser as window object ,  in Node.js In the said global object 

  var root = this;

 

  //  save "_"( Underscore variable ) The value before being overridden 

  //  If a naming conflict occurs or specification is considered ,  through _.noConflict() Methods to recover "_" be Underscore Take the previous value ,  And return Underscore Object to rename 

  var previousUnderscore = root._;

 

  //  create 1 Empty object constants ,  Easy to share internally 

  var breaker = {};

 

  //  Cache the prototype chain of the built-in object in a local variable ,  Easy to call quickly 

  var ArrayProto = Array.prototype, //

  ObjProto = Object.prototype, //

  FuncProto = Function.prototype;

 

  //  Cache common methods in the built-in object prototype in local variables ,  Easy to call quickly 

  var slice = ArrayProto.slice, //

  unshift = ArrayProto.unshift, //

  toString = ObjProto.toString, //

  hasOwnProperty = ObjProto.hasOwnProperty;

 

  //  It's defined here 1 some JavaScript 1.6 Provides a new approach 

  //  If these methods are supported in the host environment, they are called first ,  If not provided in the host environment ,  Will be by Underscore implementation 

  var nativeForEach = ArrayProto.forEach, //

  nativeMap = ArrayProto.map, //

  nativeReduce = ArrayProto.reduce, //

  nativeReduceRight = ArrayProto.reduceRight, //

  nativeFilter = ArrayProto.filter, //

  nativeEvery = ArrayProto.every, //

  nativeSome = ArrayProto.some, //

  nativeIndexOf = ArrayProto.indexOf, //

  nativeLastIndexOf = ArrayProto.lastIndexOf, //

  nativeIsArray = Array.isArray, //

  nativeKeys = Object.keys, //

  nativeBind = FuncProto.bind;

 

  //  Create an object-like invocation ,  Will return 1 a Underscore The wrapper ,  Included in the prototype of the wrapper object Underscore All the methods ( Similar to the DOM The object is wrapped as 1 a jQuery object )

  var _ = function(obj) {

    //  all Underscore Objects pass internally wrapper Object construction 

    return new wrapper(obj);

  };

  //  For different host environments ,  will Undersocre A named variable is stored in a different object 

  if( typeof exports !== 'undefined') {// Node.js The environment 

    if( typeof module !== 'undefined' && module.exports) {

      exports = module.exports = _;

    }

    exports._ = _;

  } else {//  In the browser environment Underscore The named variable is hung at window In the object 

    root['_'] = _;

  }

 

  //  Versions. 

  _.VERSION = '1.3.3';

 

  //  Collection related methods ( General handling of data and objects )

  // --------------------

 

  //  Iterative processor ,  For each of the sets 1 The element executes the processor method 

  var each = _.each = _.forEach = function(obj, iterator, context) {

    //  Null values are not processed 

    if(obj == null)

      return;

    if(nativeForEach && obj.forEach === nativeForEach) {

      //  If the host environment supports it ,  Priority call JavaScript 1.6 To provide the forEach methods 

      obj.forEach(iterator, context);

    } else if(obj.length === +obj.length) {

      //  right < An array of > Each of the 1 The element executes the processor method 

      for(var i = 0, l = obj.length; i < l; i++) {

        if( i in obj && iterator.call(context, obj[i], i, obj) === breaker)

          return;

      }

    } else {

      //  right < object > Each of the 1 The element executes the processor method 

      for(var key in obj) {

        if(_.has(obj, key)) {

          if(iterator.call(context, obj[key], key, obj) === breaker)

            return;

        }

      }

    }

  };

  //  Iterative processor ,  with each The difference in approach is map The return value for each iteration is stored ,  And as a 1 Returns a new array 

  _.map = _.collect = function(obj, iterator, context) {

    //  An array to hold the return value 

    var results = [];

    if(obj == null)

      return results;

    //  Take precedence over calls provided by the host environment map methods 

    if(nativeMap && obj.map === nativeMap)

      return obj.map(iterator, context);

    //  Iterating through the elements in the collection 

    each(obj, function(value, index, list) {

      //  Stores the return value processed by each iteration to results An array of 

      results[results.length] = iterator.call(context, value, index, list);

    });

    //  Return processing results 

    if(obj.length === +obj.length)

      results.length = obj.length;

    return results;

  };

  //  Put each element in the collection into the iteration handler ,  And take the return value of this iteration as "memo" Pass to the next 1 iteration , 1 Generally used to aggregate results or join data 

  _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {

    //  Check for an initial value by the number of arguments 

    var initial = arguments.length > 2;

    if(obj == null)

      obj = [];

    //  Take precedence over calls provided by the host environment reduce methods 

    if(nativeReduce && obj.reduce === nativeReduce && false) {

      if(context)

        iterator = _.bind(iterator, context);

      return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);

    }

    //  Iterating through the elements in the collection 

    each(obj, function(value, index, list) {

      if(!initial) {

        //  If there is no initial value ,  Will be the first 1 Three elements as the initial value ;  If you're dealing with a collection of objects ,  The default value is the value of 1 The value of an attribute 

        memo = value;

        initial = true;

      } else {

        //  Record processing results ,  And pass the result to the next one 1 iteration 

        memo = iterator.call(context, memo, value, index, list);

      }

    });

    if(!initial)

      throw new TypeError('Reduce of empty array with no initial value');

    return memo;

  };

  //  with reduce Role similar ,  The elements in the collection will be iterated backwards ( That is, from the last 1 The number of elements starts at number 1 1 An element )

  _.reduceRight = _.foldr = function(obj, iterator, memo, context) {

    var initial = arguments.length > 2;

    if(obj == null)

      obj = [];

    //  Take precedence over calls provided by the host environment reduceRight methods 

    if(nativeReduceRight && obj.reduceRight === nativeReduceRight) {

      if(context)

        iterator = _.bind(iterator, context);

      return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);

    }

    //  Reverses the order of elements in a collection 

    var reversed = _.toArray(obj).reverse();

    if(context && !initial)

      iterator = _.bind(iterator, context);

    //  through reduce Methods To process data 

    return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);

  };

  //  Iterate over the elements in the collection ,  Returns the first 1 Three elements that can be validated by the processor 

  _.find = _.detect = function(obj, iterator, context) {

    // result For the first 1 Three elements that can be validated 

    var result;

    //  through any Method iterates over the data ,  And record the elements that pass the validation 

    // ( If you are checking the processor return status in an iteration ,  Used here each The method will be more appropriate )

    any(obj, function(value, index, list) {

      //  If the result returned by the processor is converted to Boolean The post-type value is true,  The current record and returns the current element 

      if(iterator.call(context, value, index, list)) {

        result = value;

        return true;

      }

    });

    return result;

  };

  //  with find Method action is similar ,  but filter Method records all elements in the collection that pass validation 

  _.filter = _.select = function(obj, iterator, context) {

    //  Used to store an array of elements that have passed validation 

    var results = [];

    if(obj == null)

      return results;

    //  Take precedence over calls provided by the host environment filter methods 

    if(nativeFilter && obj.filter === nativeFilter)

      return obj.filter(iterator, context);

    //  Iterate over the elements in the collection ,  And put the processor-validated elements into an array and return them 

    each(obj, function(value, index, list) {

      if(iterator.call(context, value, index, list))

        results[results.length] = value;

    });

    return results;

  };

  //  with filter The method does the opposite ,  Returns a list of elements that have not been validated by the processor 

  _.reject = function(obj, iterator, context) {

    var results = [];

    if(obj == null)

      return results;

    each(obj, function(value, index, list) {

      if(!iterator.call(context, value, index, list))

        results[results.length] = value;

    });

    return results;

  };

  //  If all elements in the collection are processor-validated ,  It returns true

  _.every = _.all = function(obj, iterator, context) {

    var result = true;

    if(obj == null)

      return result;

    //  Take precedence over calls provided by the host environment every methods 

    if(nativeEvery && obj.every === nativeEvery)

      return obj.every(iterator, context);

    //  Iterate over the elements in the collection 

    each(obj, function(value, index, list) {

      //  This is understood as  result = (result && iterator.call(context, value, index, list))

      //  The result of the validation processor is converted to Boolean Is after type true value 

      if(!( result = result && iterator.call(context, value, index, list)))

        return breaker;

    });

    return !!result;

  };

  //  Check any in the collection 1 The elements are being converted to Boolean type ,  Whether it is true value ? Or through the processor processing ,  If a value of true?

  var any = _.some = _.any = function(obj, iterator, context) {

    //  If no processor parameters are specified ,  The default handler function returns the element itself ,  And at iteration time by converting the element to Boolean Type to determine if it is true value 

    iterator || ( iterator = _.identity);

    var result = false;

    if(obj == null)

      return result;

    //  Take precedence over calls provided by the host environment some methods 

    if(nativeSome && obj.some === nativeSome)

      return obj.some(iterator, context);

    //  Iterate over the elements in the collection 

    each(obj, function(value, index, list) {

      if(result || ( result = iterator.call(context, value, index, list)))

        return breaker;

    });

    return !!result;

  };

  //  Check to see if any of the values in the collection exactly match the target parameter ( The data type will also be matched )

  _.include = _.contains = function(obj, target) {

    var found = false;

    if(obj == null)

      return found;

    //  Take precedence over calls provided by the host environment Array.prototype.indexOf methods 

    if(nativeIndexOf && obj.indexOf === nativeIndexOf)

      return obj.indexOf(target) != -1;

    //  through any Method iterates through the elements in the collection ,  Verify that the value and type of the element exactly match the target 

    found = any(obj, function(value) {

      return value === target;

    });

    return found;

  };

  //  The method of the same name for all elements in the collection is called in turn ,  From the first 3 Parameter start ,  This will be passed into the element's calling method 

  //  return 1 An array ,  The results of all the methods are stored 

  _.invoke = function(obj, method) {

    //  Parameters passed when a method with the same name is called ( From the first 3 Parameter start )

    var args = slice.call(arguments, 2);

    //  Call the methods of each element in turn ,  And returns the result in an array 

    return _.map(obj, function(value) {

      return (_.isFunction(method) ? method || value : value[method]).apply(value, args);

    });

  };

  //  traverse 1 An array of objects ,  And returns a list of values for the specified property in each object 

  _.pluck = function(obj, key) {

    //  If a 1 The attribute does not exist in the object ,  It returns undefined

    return _.map(obj, function(value) {

      return value[key];

    });

  };

  //  Returns the maximum value in the collection ,  If there are no comparable values ,  It returns undefined

  _.max = function(obj, iterator, context) {

    //  If the set is 1 An array ,  And no processor is used ,  Use the Math.max Get the maximum 

    // 1 Will be in 1 An array is stored 1 A series of Number Type of data 

    if(!iterator && _.isArray(obj) && obj[0] === +obj[0])

      return Math.max.apply(Math, obj);

    //  For a null value ,  Just go back to negative infinity 

    if(!iterator && _.isEmpty(obj))

      return -Infinity;

    // 1 A temporary object , computed Used to store maximum values during comparison ( temporary )

    var result = {

      computed : -Infinity

    };

    //  Iterate over the elements in the collection 

    each(obj, function(value, index, list) {

      //  If processor parameters are specified ,  Is the value returned by the processor ,  Otherwise, just use it each Default value for traversal 

      var computed = iterator ? iterator.call(context, value, index, list) : value;

      //  If you compare the values 1 A value ,  Puts the current value in result.value

      computed >= result.computed && ( result = {

        value : value,

        computed : computed

      });

    });

    //  Return maximum value 

    return result.value;

  };

  //  Returns the minimum value in the collection ,  Treatment process and max methods 1 to 

  _.min = function(obj, iterator, context) {

    if(!iterator && _.isArray(obj) && obj[0] === +obj[0])

      return Math.min.apply(Math, obj);

    if(!iterator && _.isEmpty(obj))

      return Infinity;

    var result = {

      computed : Infinity

    };

    each(obj, function(value, index, list) {

      var computed = iterator ? iterator.call(context, value, index, list) : value;

      computed < result.computed && ( result = {

        value : value,

        computed : computed

      });

    });

    return result.value;

  };

  //  By random number ,  Make arrays non-permutation 

  _.shuffle = function(obj) {

    // shuffled Variables store processing and final result data 

    var shuffled = [], rand;

    //  Iterate over the elements in the collection 

    each(obj, function(value, index, list) {

      //  generate 1 A random number ,  The random number in <0- The amount currently processed > between 

      rand = Math.floor(Math.random() * (index + 1));

      //  I'm going to put the element that I got at random shuffled At the end of the array 

      shuffled[index] = shuffled[rand];

      //  Insert the latest value in the position of the previous random number 

      shuffled[rand] = value;

    });

    //  return 1 An array ,  The array stores random mix-and-match collection elements 

    return shuffled;

  };

  //  For the elements in the set ,  Sort by a specific field or value 

  //  Compared with the Array.prototype.sort methods , sortBy Method supports sorting objects 

  _.sortBy = function(obj, val, context) {

    // val It should be an object 1 A property ,  or 1 10 processor functions ,  If it is 1 A processor ,  The data that needs to be compared should be returned 

    var iterator = _.isFunction(val) ? val : function(obj) {

      return obj[val];

    };

    //  Call to order : _.pluck(_.map().sort());

    //  call _.map() Method traverses the collection ,  And put the elements in the set value node ,  Place the data in the element to be compared to criteria Properties of the 

    //  call sort() Method to represent the elements in a collection criteria The data in the property is sorted sequentially 

    //  call pluck Gets the sorted collection of objects and returns 

    return _.pluck(_.map(obj, function(value, index, list) {

      return {

        value : value,

        criteria : iterator.call(context, value, index, list)

      };

    }).sort(function(left, right) {

      var a = left.criteria, b = right.criteria;

      if(a ===

        void 0)

        return 1;

      if(b ===

        void 0)

        return -1;

      return a < b ? -1 : a > b ? 1 : 0;

    }), 'value');

  };

  //  Will be an element in the collection ,  Press the processor to return key Divided into multiple arrays 

  _.groupBy = function(obj, val) {

    var result = {};

    // val The processor functions that will be converted to be grouped ,  if val not 1 a Function Type of data ,  Will be used as a filter element key value 

    var iterator = _.isFunction(val) ? val : function(obj) {

      return obj[val];

    };

    //  Iterate over the elements in the collection 

    each(obj, function(value, index) {

      //  Takes the return value of the processor as key,  And will be the same key Elements in the 1 A new array 

      var key = iterator(value, index);

      (result[key] || (result[key] = [])).push(value);

    });

    //  Returns grouped data 

    return result;

  };

  _.sortedIndex = function(array, obj, iterator) {

    iterator || ( iterator = _.identity);

    var low = 0, high = array.length;

    while(low < high) {

      var mid = (low + high) >> 1;

      iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;

    }

    return low;

  };

  //  will 1 Individual set transformation 1 Array and returns 

  // 1 As used to arguments Convert to array ,  Or transform an unordered collection of objects into an ordered collection of data 

  _.toArray = function(obj) {

    if(!obj)

      return [];

    if(_.isArray(obj))

      return slice.call(obj);

    //  will arguments Convert to array 

    if(_.isArguments(obj))

      return slice.call(obj);

    if(obj.toArray && _.isFunction(obj.toArray))

      return obj.toArray();

    //  Converts an object to an array ,  The array contains a list of values for all properties in the object ( Contains no properties in the object prototype chain )

    return _.values(obj);

  };

  //  Calculate the number of elements in the collection 

  _.size = function(obj) {

    //  If the set is 1 An array ,  Counts the number of array elements 

    //  If the set is 1 An object ,  Counts the number of attributes in the object ( Contains no properties in the object prototype chain )

    return _.isArray(obj) ? obj.length : _.keys(obj).length;

  };

  //  Array dependent methods 

  // ---------------

 

  //  return 1 The first digit of an array 1 Or specified by order n An element 

  _.first = _.head = _.take = function(array, n, guard) {

    //  If no parameters are specified n,  Returns the first 1 An element 

    //  If you specify n,  It returns 1 A new array ,  Contains a specified number in order n An element 

    // guard The parameter is used to determine that only the values are returned 1 An element ,  when guard for true when ,  Specify the number of n invalid 

    return (n != null) && !guard ? slice.call(array, 0, n) : array[0];

  };

  //  return 1 A new array ,  Contains in addition to the first 1 The other elements ,  Or exclude from the end 1 The element is specified forward n An element 

  //  with first The method is different , first Determine the location of the desired element before the array , initial Determines the last position of the excluded elements in the array 

  _.initial = function(array, n, guard) {

    //  If no parameters are passed n,  The default is to return the last division 1 The other elements 

    //  If you pass a parameter n,  Returns from the last 1 The elements start moving forward n The other elements 

    // guard Used to determine return only 1 An element ,  when guard for true when ,  Specify the number of n invalid 

    return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));

  };

  //  Returns the end of the array 1 Specified in or reverse order n An element 

  _.last = function(array, n, guard) {

    if((n != null) && !guard) {

      //  Calculate and specify the location of the element to get n,  All the way to the end of the array ,  As a 1 Returns a new array 

      return slice.call(array, Math.max(array.length - n, 0));

    } else {

      //  If no quantity is specified ,  or guard for true when ,  Just return the last 1 An element 

      return array[array.length - 1];

    }

  };

  //  Get exception control 1 Or before specified n The other elements 

  _.rest = _.tail = function(array, index, guard) {

    //  To calculate slice The first 2 Position parameter ,  All the way to the end of the array 

    //  If not specified index,  or guard A value of true,  Returns a divisor 1 The other elements 

    // (index == null) A value of true when ,  Passed as a parameter to slice The function is automatically converted to 1

    return slice.call(array, (index == null) || guard ? 1 : index);

  };

  //  Returns all values in the array that can be converted to true The elements of the ,  return 1 A new array 

  //  Values that cannot be converted include  false, 0, '', null, undefined, NaN,  These values will be converted to false

  _.compact = function(array) {

    return _.filter(array, function(value) {

      return !!value;

    });

  };

  //  will 1 A combination of several dimensions 1 Dimensional array ,  Support for deep merging 

  // shallow Parameter is used to control the merge depth ,  when shallow for true when ,  Incorporate only the first 1 layer ,  Deep merge is performed by default 

  _.flatten = function(array, shallow) {

    //  Iterate through each of the arrays 1 An element ,  And return the value as demo Passed to the 1 iteration 

    return _.reduce(array, function(memo, value) {

      //  If the element is still 1 An array ,  Make the following judgment :

      // -  If you don't do a deep merge ,  Use the Array.prototype.concat Joins the current array with the previous data 

      // -  If deep merge is supported ,  Then iterative call flatten methods ,  Until the underlying element is no longer an array type 

      if(_.isArray(value))

        return memo.concat( shallow ? value : _.flatten(value));

      //  data (value) Already at the bottom ,  It's no longer an array type ,  The data is merged into memo And in return 

      memo[memo.length] = value;

      return memo;

    }, []);

  };

  //  Filters and returns the difference data in the current array that is not equal to the specified data ( May refer to difference Methods the annotation )

  _.without = function(array) {

    return _.difference(array, slice.call(arguments, 1));

  };

  //  De-weight the data in the array ( use === To compare the )

  //  when isSorted Parameters for false when ,  The elements in the array are called in turn include methods ,  Check if the same element has been added to the return value ( An array of ) In the 

  //  Make sure the data in the array is sorted before calling ,  You can isSorted Set to true,  It will pass with the last 1 The elements are compared to exclude the same value ,  use isSorted It will be more efficient than the default include way 

  // uniq Method defaults to comparing data in an array ,  If the statement iterator The processor ,  It is created based on the processor 1 A comparison array ,  The comparison is based on the data in the array ,  But in the end the only return 1 The data is still the original array 

  _.uniq = _.unique = function(array, isSorted, iterator) {

    //  If you use iterator The processor ,  The data in the current array is processed by iterator first ,  And return 1 A new array after processing 

    //  The new array is used as a benchmark for comparison 

    var initial = iterator ? _.map(array, iterator) : array;

    //  A temporary array used to record the results of processing 

    var results = [];

    //  If there's only one in the array 2 A value ,  You don't need to use it include Methods To compare ,  will isSorted Set to true Can improve the operation efficiency 

    if(array.length < 3)

      isSorted = true;

    //  use reduce Methods Iterate and accumulate the results 

    // initial Variables are the baseline data to be compared ,  It could be the original array ,  It could also be the result set of the processor ( If it's set iterator)

    _.reduce(initial, function(memo, value, index) {

      //  if isSorted Parameters for true,  Use it directly === Compare the last in the record 1 A data 

      //  if isSorted Parameters for false,  Use the include Method and each in the collection 1 Data for comparison 

      if( isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {

        // memo No duplicate data has been compared 

        //  According to the iterator State of parameter , memo The data recorded in May be raw data ,  It could also be data processed by the processor 

        memo.push(value);

        //  The processing result array always holds the data from the original array 

        results.push(array[index]);

      }

      return memo;

    }, []);

    //  Return processing results ,  It contains only data that has no duplicates in the array 

    return results;

  };

  // union The methods and uniq Methods effect 1 to ,  The difference is that union Allows multiple arrays to be passed in as parameters 

  _.union = function() {

    // union Multiple arrays in the parameter are shallowly merged as 1 An array object is passed to uniq Method processing 

    return _.uniq(_.flatten(arguments, true));

  };

  //  Gets the current array and others 1 The intersection elements of two or more arrays 

  //  From the first 2 The parameters start as those to be compared 1 One or more arrays 

  _.intersection = _.intersect = function(array) {

    // rest A variable records other array objects that need to be compared 

    var rest = slice.call(arguments, 1);

    //  use uniq Method to remove duplicate data from the current array ,  Avoid double counting 

    //  The data for the current array is filtered through the processor ,  And returns a condition that meets the criteria ( Compare the same elements ) The data of 

    return _.filter(_.uniq(array), function(item) {

      //  use every Method validation per 1 Each of the arrays contains the data that needs to be compared 

      //  If all arrays contain comparison data ,  Then return all true,  If any 1 An array does not contain this element ,  It returns false

      return _.every(rest, function(other) {

        // other Parameters are stored for each 1 An array that needs to be compared 

        // item Stores the data in the current array that needs to be compared 

        //  use indexOf Method searches for the presence of the element in the array ( May refer to indexOf Methods the annotation )

        return _.indexOf(other, item) >= 0;

      });

    });

  };

  //  Filters and returns the difference data in the current array that is not equal to the specified data 

  //  This function 1 Generally used to delete data specified in an array ,  And get the deleted array 

  //  The function of this method is without equal , without Method parameters do not formally allow data to be contained in an array ,  while difference The method parameter is formally recommended as an array ( Can also and without Use the same form of parameters )

  _.difference = function(array) {

    //  For the first 2 All of the parameters at the beginning of an argument ,  As a 1 The array is merged ( Only merge the first 1 layer ,  It's not a deep merger )

    // rest Variables store validation data ,  This method is used for comparison with the original data 

    var rest = _.flatten(slice.call(arguments, 1), true);

    //  Filter the merged array data ,  A filter condition is the contents of the current array that does not contain validation data specified by the parameter 

    //  Combine the data that meets the filtering criteria as 1 Returns a new array 

    return _.filter(array, function(value) {

      return !_.include(rest, value);

    });

  };

  //  Take the data at the same location in each array as 1 A new one 2 Dimension array return ,  The length of the array returned is the maximum length of the array passed in ,  Blank Spaces for other arrays are used undefined fill 

  // zip Methods should contain multiple parameters ,  And each parameter should be an array 

  _.zip = function() {

    //  Converts parameters to arrays ,  At this time args is 1 a 2 Dimensional array 

    var args = slice.call(arguments);

    //  Calculate each 1 The length of an array ,  And returns the maximum length value 

    var length = _.max(_.pluck(args, 'length'));

    //  Create according to the maximum length value 1 A new empty array ,  This array is used to store the results of the processing 

    var results = new Array(length);

    //  Maximum cycle length ,  Each loop will be called pluck Method gets data at the same location in each array ( In turn, from 0 To the last position )

    //  Store the obtained data in 1 A new array ,  In the results And return 

    for(var i = 0; i < length; i++)

    results[i] = _.pluck(args, "" + i);

    //  The result returned is 1 a 2 Dimensional array 

    return results;

  };

  //  search 1 The first occurrence of an element in an array ,  Returns if the element does not exist  -1

  //  Use when searching  ===  Matches elements 

  _.indexOf = function(array, item, isSorted) {

    if(array == null)

      return -1;

    var i, l;

    if(isSorted) {

      i = _.sortedIndex(array, item);

      return array[i] === item ? i : -1;

    }

    //  Take precedence over calls provided by the host environment indexOf methods 

    if(nativeIndexOf && array.indexOf === nativeIndexOf)

      return array.indexOf(item);

    //  Loop and return the first occurrence of the element 

    for( i = 0, l = array.length; i < l; i++)

    if( i in array && array[i] === item)

      return i;

    //  No element found ,  return -1

    return -1;

  };

  //  return 1 Five elements are at the end of the array 1 Secondary position ,  Returns if the element does not exist  -1

  //  Use when searching  ===  Matches elements 

  _.lastIndexOf = function(array, item) {

    if(array == null)

      return -1;

    //  Take precedence over calls provided by the host environment lastIndexOf methods 

    if(nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf)

      return array.lastIndexOf(item);

    var i = array.length;

    //  Loop and return the last occurrence of the element 

    while(i--)

    if( i in array && array[i] === item)

      return i;

    //  No element found ,  return -1

    return -1;

  };

  //  Depending on the interval and the step size ,  generate 1 Series of integer ,  And returned as an array 

  // start The parameter represents the minimum number 

  // stop The parameter represents the largest number 

  // step Parameter represents the step size value between generating multiple values 

  _.range = function(start, stop, step) {

    //  Parameter control 

    if(arguments.length <= 1) {

      //  If there are no parameters ,  the start = 0, stop = 0,  No data is generated in the loop ,  Will return 1 An empty array 

      //  If you have 1 A parameter ,  The parameter is specified to stop, start = 0

      stop = start || 0;

      start = 0;

    }

    //  The step value that generates an integer ,  The default is 1

    step = arguments[2] || 1;

 

    //  Calculate the maximum value that will be generated based on the interval and step size 

    var len = Math.max(Math.ceil((stop - start) / step), 0);

    var idx = 0;

    var range = new Array(len);

 

    //  Generate integer list ,  And stored in the range An array of 

    while(idx < len) {

      range[idx++] = start;

      start += step;

    }

 

    //  Return list result 

    return range;

  };

  //  Function correlation method 

  // ------------------

 

  //  create 1 Three for setting prototype Public function object 

  var ctor = function() {

  };

  //  for 1 A function binding execution context ,  Call this function in any case ,  In the function this All point to the context object 

  //  Binding function ,  You can also pass the call parameter to the function 

  _.bind = function bind(func, context) {

    var bound, args;

    //  Take precedence over calls provided by the host environment bind methods 

    if(func.bind === nativeBind && nativeBind)

      return nativeBind.apply(func, slice.call(arguments, 1));

    // func The parameter must be 1 A function (Function) type 

    if(!_.isFunction(func))

      throw new TypeError;

    // args Variables are stored bind Methods the first 3 A starting argument list ,  Will be passed to each invocation func function 

    args = slice.call(arguments, 2);

    return bound = function() {

      if(!(this instanceof bound))

        return func.apply(context, sargs.concat(slice.call(arguments)));

      ctor.prototype = func.prototype;

      var self = new ctor;

      var result = func.apply(self, args.concat(slice.call(arguments)));

      if(Object(result) === result)

        return result;

      return self;

    };

  };

  //  The function that will be specified ,  Or all functions of the object itself bind to the object itself ,  When the bound function is called ,  The context object always points to the object itself 

  //  The method 1 Generally used when dealing with object events ,  For example, :

  // _(obj).bindAll(); //  or _(obj).bindAll('handlerClick');

  // document.addEventListener('click', obj.handlerClick);

  //  in handlerClick In the method ,  The context is still obj object 

  _.bindAll = function(obj) {

    //  The first 2 The parameters begin to indicate the name of the function to bind to 

    var funcs = slice.call(arguments, 1);

    //  If a specific function name is not specified ,  Then the default binding object itself is of all types Function The properties of the 

    if(funcs.length == 0)

      funcs = _.functions(obj);

    //  Loop through and set all function Settings to obj The object itself 

    // each Methods themselves do not traverse methods in the object prototype chain ,  But here the funcs The list is through _.functions Method-Acquired ,  It already contains the methods in the prototype chain 

    each(funcs, function(f) {

      obj[f] = _.bind(obj[f], obj);

    });

    return obj;

  };

  // memoize Method will return 1 A function ,  This function integrates caching capabilities ,  Cache the calculated value into a local variable and return it directly on the next call 

  //  So if I compute theta 1 A large number of objects or data ,  Memory usage should be taken into account 

  _.memoize = function(func, hasher) {

    //  Used to store cached results memo object 

    var memo = {};

    // hasher The parameter should be 1 a function,  It's used to return 1 a key,  the key As the identity of the read cache 

    //  If not specified key,  The default is to use the column of the function 1 2 parameters as key,  If the order of the function 1 The parameters are composite data types ,  It might return something like that [Object object] the key,  this key It may cause incorrect data in subsequent calculations 

    hasher || ( hasher = _.identity);

    //  return 1 A function ,  This function starts by checking the cache ,  The call is then made to data that has not been cached 

    return function() {

      var key = hasher.apply(this, arguments);

      return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));

    };

  };

  //  Delay to perform 1 A function 

  // wait The unit is ms,  The first 3 The starting arguments are passed to the execution function in turn 

  _.delay = function(func, wait) {

    var args = slice.call(arguments, 2);

    return setTimeout(function() {

      return func.apply(null, args);

    }, wait);

  };

  //  Deferred execution function 

  // JavaScript In the setTimeout Will be put into 1 A separate function stack ,  Execution time is after all functions called on the current stack have been executed 

  // defer Set the function in 1ms After the implementation ,  The purpose is to func Functions are placed on a separate stack ,  Wait until the current function completes 

  // defer methods 1 Generally used for processing DOM The priority of the operation ,  Implement the right logic flow and a smoother interaction experience 

  _.defer = function(func) {

    return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));

  };

  //  Function throttling method , throttle Method is mainly used to control the execution frequency of the function ,  Within a controlled time interval ,  Frequently called functions are not executed more than once 

  //  If the function is called multiple times in the interval ,  The interval is automatically called when it ends 1 time ,  There is no need to wait until the deadline to call manually ( There will be no return value when it is called automatically )

  // throttle function 1 Generally used to handle complex and frequently invoked functions ,  Control the frequency of function calls through throttling ,  Save processing resources 

  //  For example, window.onresize Bound event function ,  or element.onmousemove Bound event function ,  You can use throttle For packaging 

  // throttle Method returns 1 A function ,  This function is called automatically func And throttling control 

  _.throttle = function(func, wait) {

    var context, args, timeout, throttling, more, result;

    // whenDone The variable called debounce methods ,  So when you call a function multiple times in a row ,  The last 1 The call overrides the timer from the previous call ,  The clear state function is only executed 1 time 

    // whenDone The function is at the end 1 Call when the time interval for the second function execution has expired ,  Clears records during throttling and invocation 1 Some state 

    var whenDone = _.debounce(function() {

      more = throttling = false;

    }, wait);

    //  return 1 A function ,  And throttling control within the function 

    return function() {

      //  Saves the execution context and arguments of the function 

      context = this;

      args = arguments;

      // later On the function in the 1 Executes when the time interval for the second function call ends 

      var later = function() {

        //  remove timeout handle ,  Under the convenient 1 Secondary function call 

        timeout = null;

        // more It was recorded on 1 Between the call and the time interval cutoff ,  Whether the function was repeatedly called 

        //  If the function is called repeatedly ,  The function is automatically called again at the end of the time interval 

        if(more)

          func.apply(context, args);

        //  call whenDone,  Used to clear throttled state after an interval 

        whenDone();

      };

      // timeout Recorded on the 1 Handle to the interval between second function executions 

      // timeout Called when the time interval is cut off later function , later Will be cleared timeout,  And check to see if the function needs to be called again 

      if(!timeout)

        timeout = setTimeout(later, wait);

      // throttling The variable records whether the time interval for the last call has ended ,  Whether or not it is throttling 

      // throttling Set to for each function call true,  Indicates that throttling is required ,  Set to at the end of the interval false( in whenDone Implementation in function )

      if(throttling) {

        //  Multiple calls are made during throttling ,  in more Recorded in the 1 A state of ,  Indicates that the function needs to be called automatically again when the time interval expires 

        more = true;

      } else {

        //  Not in the throttling process ,  May be the first 1 Secondary function ,  Or more than that 1 The interval between calls ,  You can call the function directly 

        result = func.apply(context, args);

      }

      //  call whenDone,  Used to clear throttled state after an interval 

      whenDone();

      // throttling Variables record the throttling state of a function call 

      throttling = true;

      //  Return the call result 

      return result;

    };

  };

  // debounce with throttle Methods the similar ,  For function throttling ,  The difference is that :

  // -- throttle Focus on how often the function executes ,  Functions are executed only at the specified frequency 1 time ;

  // -- debounce The function is more concerned with the interval between functions ,  That is, the function cannot be called twice in less than the specified time ;

  //  If the interval between two functions is less than wait,  The timer is cleared and recreated ,  This means calling the function frequently and continuously ,  function 1 Will not be executed ,  Until a 1 Call and up 1 The time of the call is not less than wait ms 

  // debounce function 1 Generally used for control needs 1 An operation that can not be performed until some time later ,  For example, at the end of user input 200ms Post prompt user ,  You can use debounce packaging 1 A function ,  Bound to the onkeyup The event 

  // ----------------------------------------------------------------

  // @param {Function} func  Represents the function being executed 

  // @param {Number} wait  Represents the allowed time interval ,  Repeated calls within this time range are rescheduled wait ms 

  // @param {Boolean} immediate  Indicates whether the function is executed immediately after the call , true For immediate call , false Is called at the time cutoff 

  // debounce Method returns 1 A function ,  This function is called automatically func And throttling control 

  _.debounce = function(func, wait, immediate) {

    // timeout Used for recording functions 1 The execution state of the call ( Timer handle )

    //  when timeout for null when ,  In the said 1 The call has ended 

    var timeout;

    //  return 1 A function ,  And throttling control within the function 

    return function() {

      //  Holds the context object and arguments of the function 

      var context = this, args = arguments;

      var later = function() {

        //  Set up the timeout for null

        // later The function is called at the end of the allowed time 

        //  When the function is called ,  Indicates that the 1 The sub-function execution time has exceeded the agreed time interval ,  Any subsequent calls are allowed 

        timeout = null;

        if(!immediate)

          func.apply(context, args);

      };

      //  If the function is set to execute immediately ,  And on the 1 The time interval for the call has passed ,  The function is called immediately 

      if(immediate && !timeout)

        func.apply(context, args);

      //  create 1 A timer is used to check and set the state of a function call 

      //  Clear the timer before you create it 1 time setTimeout handle ,  No matter on 1 Whether the subbound function has been executed 

      //  If this function is called ,  on 1 Secondary function execution has not yet begun (1 As is immediate Set to false when ),  The execution time of the function is delayed ,  so timeout The handle is recreated 

      clearTimeout(timeout);

      //  Called when the allowed time has expired later function 

      timeout = setTimeout(later, wait);

    };

  };

  //  create 1 One will only be executed 1 A function of time ,  If the function is called repeatedly ,  Returns the first 1 The result of the execution 

  //  This function is used to obtain and calculate the logic of fixed data ,  For example, get the type of browser the user is using 

  _.once = function(func) {

    // ran Records whether the function has been executed 

    // memo Recording function 1 The result of the execution 

    var ran = false, memo;

    return function() {

      //  If the function has been executed ,  Returns the value directly 1 The result of the execution 

      if(ran)

        return memo;

      ran = true;

      return memo = func.apply(this, arguments);

    };

  };

  //  return 1 A function ,  This function passes the current function as an argument to 1 A package function 

  //  In the package function, you can pass the 1 An argument to the current function ,  And return the result 

  // 1 Typically low coupled composite calls for multiple process handler functions 

  _.wrap = function(func, wrapper) {

    return function() {

      //  Takes the current function as the control 1 A parameter ,  Passed to the wrapper function 

      var args = [func].concat(slice.call(arguments, 0));

      //  return wrapper The result of processing the function 

      return wrapper.apply(this, args);

    };

  };

  //  Combine multiple functions into 1 since ,  In the order in which the parameters are passed ,  after 1 The return value of each function will be 1 Pass to before as a parameter 1 Continue processing of functions as parameters 

  // _.compose(A, B, C);  Is equivalent to  A(B(C()));

  //  The disadvantage of this method is that the number of arguments processed by the associated function can only be 1 a ,  If more than one parameter needs to be passed ,  Can be achieved by Array or Object Composite data types are assembled 

  _.compose = function() {

    //  Get a list of functions ,  All parameters must be Function type 

    var funcs = arguments;

    //  return 1 A handle to a function to call 

    return function() {

      //  Execute the function from back to front ,  The return value of the record is passed to the previous as a parameter 1 A function continues processing 

      var args = arguments;

      for(var i = funcs.length - 1; i >= 0; i--) {

        args = [funcs[i].apply(this, args)];

      }

      //  Returns the last 1 The return value of the called function 

      return args[0];

    };

  };

  //  return 1 A function ,  This function ACTS as a call counter ,  When the function is called times time ( Or more than times time ) after , func The function will be executed 

  // after methods 1 Generally used as an asynchronous counter ,  For example in multiple AJAX When all requests are completed, they need to be executed 1 A function ,  Then you can use after At the end of each AJAX Invoked when the request is complete 

  _.after = function(times, func) {

    //  If no specified or invalid number specified ,  the func Be called directly 

    if(times <= 0)

      return func();

    //  return 1 Counter function 

    return function() {

      //  Each time the counter function is called times Reduction of 1,  call times After execution func Function and returns func The return value of the function 

      if(--times < 1) {

        return func.apply(this, arguments);

      }

    };

  };

  //  Object correlation method 

  // ----------------

 

  //  To obtain 1 A list of property names for an object ( Does not contain attributes in the stereotype chain )

  _.keys = nativeKeys ||

  function(obj) {

    if(obj !== Object(obj))

      throw new TypeError('Invalid object');

    var keys = [];

    //  Records and returns all property names of the object 

    for(var key in obj)

    if(_.has(obj, key))

      keys[keys.length] = key;

    return keys;

  };

 

  //  return 1 A list of values for all attributes in an object ( Does not contain attributes in the stereotype chain )

  _.values = function(obj) {

    return _.map(obj, _.identity);

  };

  //  To obtain 1 The value of all attributes in the object is Function The type of key The list of ,  And according to the key Sort by name ( Contains attributes in the stereotype chain )

  _.functions = _.methods = function(obj) {

    var names = [];

    for(var key in obj) {

      if(_.isFunction(obj[key]))

        names.push(key);

    }

    return names.sort();

  };

  //  will 1 Properties of one or more objects ( Contains attributes in the stereotype chain ),  Copied to the obj object ,  Overrides if an attribute with the same name exists 

  _.extend = function(obj) {

    // each In the cycle parameter 1 Two or more objects 

    each(slice.call(arguments, 1), function(source) {

      //  Copies or overrides all properties in an object obj object 

      for(var prop in source) {

        obj[prop] = source[prop];

      }

    });

    return obj;

  };

  //  return 1 A new object ,  And from the obj Copies the specified property to the new object 

  //  The first 2 Starts with the specified property name to be copied ( Support for multiple parameters and deep arrays )

  _.pick = function(obj) {

    //  create 1 An object ,  Store the specified properties for replication 

    var result = {};

    //  From the first 2 The parameters start to merge as 1 An array of property names 

    each(_.flatten(slice.call(arguments, 1)), function(key) {

      //  A list of cyclic attribute names ,  if obj The attribute exists in ,  Copies it to result object 

      if( key in obj)

        result[key] = obj[key];

    });

    //  Return copy result 

    return result;

  };

  //  will obj Does not exist in or convert to Boolean The post-type value is false The properties of the ,  Specified from the argument 1 Copies to in one or more objects obj

  // 1 Generally used to specify a default value for an object 

  _.defaults = function(obj) {

    //  From the first 2 Multiple objects can be specified at the beginning of an argument ,  The properties in these objects will be copied in turn to obj In the object ( if obj Does not exist in the object )

    each(slice.call(arguments, 1), function(source) {

      //  Iterate through all the properties in each object 

      for(var prop in source) {

        //  if obj Does not exist or attribute values are converted to Boolean The post-type value is false,  Copies the property to obj In the 

        if(obj[prop] == null)

          obj[prop] = source[prop];

      }

    });

    return obj;

  };

  //  create 1 a obj A copy of the ,  return 1 A new object ,  The object contains obj State of all attributes and values in 

  // clone The function does not support deep replication ,  For example, obj One of the properties in 1 An object ,  The object will not be copied 

  //  if obj is 1 An array ,  Will create 1 The same array object 

  _.clone = function(obj) {

    //  Data of non-array and object types is not supported 

    if(!_.isObject(obj))

      return obj;

    //  Copies and returns an array or object 

    return _.isArray(obj) ? obj.slice() : _.extend({}, obj);

  };

  //  perform 1 A function ,  And will be obj Passed to the function as a parameter ,  The function finally returns after execution obj object 

  // 1 As you create 1 It will be used when you have a method chain tap methods ,  For example, :

  // _(obj).chain().tap(click).tap(mouseover).tap(mouseout);

  _.tap = function(obj, interceptor) {

    interceptor(obj);

    return obj;

  };

  // eq Function only in isEqual Call in a method ,  Used to compare two data values for equality 

  //  with  ===  Difference is that , eq Focus more on the value of the data 

  //  If you are comparing two compound data types ,  It's not just comparing whether it comes from the same 1 A reference ,  And deep comparisons will be made ( Compare the structure and data of the two objects )

  function eq(a, b, stack) {

    //  Check that the values of the two simple data types are equal 

    //  For composite data types ,  If they come from the same source 1 A reference ,  And they say it's the same 

    //  If the value being compared contains 0,  Check the other 1 Is the value of -0,  because  0 === -0  Is true 

    //  while  1 / 0 == 1 / -0  It doesn't make sense (1 / 0 A value of Infinity, 1 / -0 A value of -Infinity,  while Infinity Is not equal to -Infinity)

    if(a === b)

      return a !== 0 || 1 / a == 1 / b;

    //  If the value is after converting the data to a Boolean type false,  The data types of the two values are determined to be equal ( because null with undefined, false, 0,  An empty string ,  Values are equal under non-strict comparisons )

    if(a == null || b == null)

      return a === b;

    //  If the data you're comparing is 1 a Underscore Encapsulated object ( with _chain The object of the attribute is considered to be Underscore object )

    //  The object is unsealed to obtain its own data ( through _wrapped access ),  And then compare their own data 

    //  They have a similar relationship 1 a jQuery encapsulated DOM object ,  And the browser itself DOM object 

    if(a._chain)

      a = a._wrapped;

    if(b._chain)

      b = b._wrapped;

    //  If the object provides custom isEqual methods ( Here the isEqual Method is not Undersocre The object's isEqual methods ,  Because in the 1 Step has been to Undersocre Object to understand )

    //  Use object custom isEqual Methods with the other 1 I'm going to compare them 

    if(a.isEqual && _.isFunction(a.isEqual))

      return a.isEqual(b);

    if(b.isEqual && _.isFunction(b.isEqual))

      return b.isEqual(a);

    //  Validate the data types of the two data 

    //  Access to the object a Data type of ( through Object.prototype.toString methods )

    var className = toString.call(a);

    //  If the object a Data type and object b Don't match ,  The two data values are also considered to be mismatched 

    if(className != toString.call(b))

      return false;

    //  Execute here ,  You can ensure that the two data types you want to compare are composite data types ,  And the data types are the same 

    //  through switch Check the data type of the data ,  Make different comparisons for different data types 

    // ( Arrays and object types are not included here ,  Because they may contain deeper levels of data ,  A deeper comparison will be made later )

    switch (className) {

      case '[object String]':

        //  If the string type is being compared ( Among them a Is through new String() Created string )

        //  will B convert String Object is then matched ( Matches are not strictly checked for data types ,  Because they don't come from the same source 1 A reference to an object )

        //  In the call  ==  When comparing ,  Object will be called automatically toString() methods ,  Returns a string of two simple data types 

        return a == String(b);

      case '[object Number]':

        //  through +a will a into 1 a Number,  if a Not before and after conversion ,  Is that a is 1 a NaN type 

        //  because NaN with NaN It's not the same ,  So when a A value of NaN when ,  Can't be used simply a == b matching ,  I'm going to check it the same way b Whether it is NaN( namely  b != +b)

        //  when a The value is 1 A non NaN When the data ,  The check a Whether it is 0,  Because when b for -0 when , 0 === -0 Is true ( In fact, they logically belong to two different pieces of data )

        return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);

      case '[object Date]':

      //  No date type is used return or break,  So it's going to go down 1 step ( Whether the data type is Boolean type ,  Because of the 1 Step will be to Boolean Type check )

      case '[object Boolean]':

        //  Converts a date or Boolean type to a number 

        //  The date type is converted to a timestamp of the numeric type ( Invalid date format will be converted to NaN)

        //  Boolean type , true Is converted to 1, false Is converted to 0

        //  Compares whether two date or Boolean types are equal after being converted to Numbers 

        return +a == +b;

      case '[object RegExp]':

        //  Regular expression type ,  through source Access the string form of an expression 

        //  Checks if the string form of two expressions is equal 

        //  Check that the global properties of both expressions are the same ( including g, i, m)

        //  If it's exactly the same ,  The two data are considered equal 

        return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase;

    }

    //  When executed at this point , ab The two data types should be of the same object or array type 

    if( typeof a != 'object' || typeof b != 'object')

      return false;

    // stack( The heap ) Is in the isEqual call eq Function is an empty array passed internally ,  Called later in an internal iteration that compares objects and data eq The method will also pass 

    // length Record the length of the heap 

    var length = stack.length;

    while(length--) {

      //  If an object and data in the heap a matching ,  Is considered equal to 

      if(stack[length] == a)

        return true;

    }

    //  The data a Add to the heap 

    stack.push(a);

    //  define 1 Some local variables 

    var size = 0, result = true;

    //  Deep comparison of objects and arrays through recursion 

    if(className == '[object Array]') {

      //  The data being compared is of type array 

      // size Record the length of the array 

      // result Compare the length of two arrays 1 to ,  If the length is not 1 to ,  The end of the method returns result( namely false)

      size = a.length;

      result = size == b.length;

      //  If the length of two arrays 1 to 

      if(result) {

        //  call eq Method to iterate through the elements of an array ( If the array contains 2 Dimensional arrays or objects , eq The method makes a deep comparison )

        while(size--) {

          //  When ensuring that both arrays have elements of the current index ,  call eq Method deep comparison ( Pass the heap data to eq methods )

          //  Stores the results of the comparison to result variable ,  if result for false( That is, to get the data of an element in the comparison 1 to ),  Then stop iteration 

          if(!( result = size in a == size in b && eq(a[size], b[size], stack)))

            break;

        }

      }

    } else {

      //  The data being compared is the object type 

      //  If two objects are not the same 1 An instance of a class ( through constructor Attributes are ),  The two objects are not equal 

      if('constructor' in a != 'constructor' in b || a.constructor != b.constructor)

        return false;

      //  Deep comparison of data between two objects 

      for(var key in a) {

        if(_.has(a, key)) {

          // size Used to record the number of properties compared ,  Because what I'm doing here is I'm traversing a Properties of an object ,  And compare the b Object. The data for this property 

          //  when b There are too many attributes in the object a When the object ,  The logic here holds ,  But the two objects are not equal 

          size++;

          //  Iterative invoke eq methods ,  Deep comparison of property values between two objects 

          //  Record the results of the comparison result variable ,  Stop iterating when you compare unequal data 

          if(!( result = _.has(b, key) && eq(a[key], b[key], stack)))

            break;

        }

      }

      //  End of deep comparison ,  Here you can already make sure in the object a All the data in ,  object b The same data exists in 

      //  According to the size( Object attribute length ) Check the object b Is the number of attributes in the a equal 

      if(result) {

        //  Traverse object b All attributes in 

        for(key in b) {

          //  when size Have been to 0 when ( The object a The number of attributes in has been traversed ),  The object b There are also properties ,  The object b Contains more properties than objects a

          if(_.has(b, key) && !(size--))

            break;

        }

        //  When object b Contains more properties than objects a,  The two objects are not equal 

        result = !size;

      }

    }

    //  When the function finishes executing ,  Removes the control from the heap 1 A data ( When comparing objects or arrays ,  Will the iteration eq methods ,  There may be more than one data in the heap )

    stack.pop();

    //  The returned result The final comparison results are recorded 

    return result;

  }

 

  //  Compare the values of the two data ( Support for composite data types ),  Internal function eq External methods of 

  _.isEqual = function(a, b) {

    return eq(a, b, []);

  };

  //  Check that the data is null ,  contains '', false, 0, null, undefined, NaN,  An empty array ( The array length is 0) And empty object ( The object itself has no properties )

  _.isEmpty = function(obj) {

    // obj Is converted to Boolean The post-type value is false

    if(obj == null)

      return true;

    //  Check whether the object or string length is 0

    if(_.isArray(obj) || _.isString(obj))

      return obj.length === 0;

    //  Check the object ( use for in When looping, the properties of the object itself are first looped ,  Then there are the attributes in the prototype chain ),  So if the 1 Attributes belong to the object itself ,  So the object is not 1 An empty object 

    for(var key in obj)

    if(_.has(obj, key))

      return false;

    //  None of the data types passed validation ,  is 1 An empty data 

    return true;

  };

  //  Verify that the object is 1 a DOM object 

  _.isElement = function(obj) {

    return !!(obj && obj.nodeType == 1);

  };

  //  Verify that the object is 1 Array types ,  Take precedence over calls provided by the host environment isArray methods 

  _.isArray = nativeIsArray ||

  function(obj) {

    return toString.call(obj) =
                

Related articles: