Home  >  Article  >  Web Front-end  >  Underscore.js 1.3.3 中文注释翻译说明_基础知识

Underscore.js 1.3.3 中文注释翻译说明_基础知识

WBOY
WBOYOriginal
2016-05-16 15:52:541294browse
// 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() {

 

  // 创建一个全局对象, 在浏览器中表示为window对象, 在Node.js中表示global对象

  var root = this;

 

  // 保存"_"(下划线变量)被覆盖之前的值

  // 如果出现命名冲突或考虑到规范, 可通过_.noConflict()方法恢复"_"被Underscore占用之前的值, 并返回Underscore对象以便重新命名

  var previousUnderscore = root._;

 

  // 创建一个空的对象常量, 便于内部共享使用

  var breaker = {};

 

  // 将内置对象的原型链缓存在局部变量, 方便快速调用

  var ArrayProto = Array.prototype, //

  ObjProto = Object.prototype, //

  FuncProto = Function.prototype;

 

  // 将内置对象原型中的常用方法缓存在局部变量, 方便快速调用

  var slice = ArrayProto.slice, //

  unshift = ArrayProto.unshift, //

  toString = ObjProto.toString, //

  hasOwnProperty = ObjProto.hasOwnProperty;

 

  // 这里定义了一些JavaScript 1.6提供的新方法

  // 如果宿主环境中支持这些方法则优先调用, 如果宿主环境中没有提供, 则会由Underscore实现

  var nativeForEach = ArrayProto.forEach, //

  nativeMap = ArrayProto.map, //

  nativeReduce = ArrayProto.reduce, //

  nativeReduceRight = ArrayProto.reduceRight, //

  nativeFilter = ArrayProto.filter, //

  nativeEvery = ArrayProto.every, //

  nativeSome = ArrayProto.some, //

  nativeIndexOf = ArrayProto.indexOf, //

  nativeLastIndexOf = ArrayProto.lastIndexOf, //

  nativeIsArray = Array.isArray, //

  nativeKeys = Object.keys, //

  nativeBind = FuncProto.bind;

 

  // 创建对象式的调用方式, 将返回一个Underscore包装器, 包装器对象的原型中包含Underscore所有方法(类似与将DOM对象包装为一个jQuery对象)

  var _ = function(obj) {

    // 所有Underscore对象在内部均通过wrapper对象进行构造

    return new wrapper(obj);

  };

  // 针对不同的宿主环境, 将Undersocre的命名变量存放到不同的对象中

  if( typeof exports !== 'undefined') {// Node.js环境

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

      exports = module.exports = _;

    }

    exports._ = _;

  } else {// 浏览器环境中Underscore的命名变量被挂在window对象中

    root['_'] = _;

  }

 

  // 版本声明

  _.VERSION = '1.3.3';

 

  // 集合相关的方法(数据和对象的通用处理方法)

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

 

  // 迭代处理器, 对集合中每一个元素执行处理器方法

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

    // 不处理空值

    if(obj == null)

      return;

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

      // 如果宿主环境支持, 则优先调用JavaScript 1.6提供的forEach方法

      obj.forEach(iterator, context);

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

      // 对中每一个元素执行处理器方法

      for(var i = 0, l = obj.length; i 中每一个元素执行处理器方法

      for(var key in obj) {

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

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

            return;

        }

      }

    }

  };

  // 迭代处理器, 与each方法的差异在于map会存储每次迭代的返回值, 并作为一个新的数组返回

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

    // 用于存放返回值的数组

    var results = [];

    if(obj == null)

      return results;

    // 优先调用宿主环境提供的map方法

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

      return obj.map(iterator, context);

    // 迭代处理集合中的元素

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

      // 将每次迭代处理的返回值存储到results数组

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

    });

    // 返回处理结果

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

      results.length = obj.length;

    return results;

  };

  // 将集合中每个元素放入迭代处理器, 并将本次迭代的返回值作为"memo"传递到下一次迭代, 一般用于累计结果或连接数据

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

    // 通过参数数量检查是否存在初始值

    var initial = arguments.length > 2;

    if(obj == null)

      obj = [];

    // 优先调用宿主环境提供的reduce方法

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

      if(context)

        iterator = _.bind(iterator, context);

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

    }

    // 迭代处理集合中的元素

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

      if(!initial) {

        // 如果没有初始值, 则将第一个元素作为初始值; 如果被处理的是对象集合, 则默认值为第一个属性的值

        memo = value;

        initial = true;

      } else {

        // 记录处理结果, 并将结果传递给下一次迭代

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

      }

    });

    if(!initial)

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

    return memo;

  };

  // 与reduce作用相似, 将逆向迭代集合中的元素(即从最后一个元素开始直到第一个元素)

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

    var initial = arguments.length > 2;

    if(obj == null)

      obj = [];

    // 优先调用宿主环境提供的reduceRight方法

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

      if(context)

        iterator = _.bind(iterator, context);

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

    }

    // 逆转集合中的元素顺序

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

    if(context && !initial)

      iterator = _.bind(iterator, context);

    // 通过reduce方法处理数据

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

  };

  // 遍历集合中的元素, 返回第一个能够通过处理器验证的元素

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

    // result存放第一个能够通过验证的元素

    var result;

    // 通过any方法遍历数据, 并记录通过验证的元素

    // (如果是在迭代中检查处理器返回状态, 这里使用each方法会更合适)

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

      // 如果处理器返回的结果被转换为Boolean类型后值为true, 则当前记录并返回当前元素

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

        result = value;

        return true;

      }

    });

    return result;

  };

  // 与find方法作用类似, 但filter方法会记录下集合中所有通过验证的元素

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

    // 用于存储通过验证的元素数组

    var results = [];

    if(obj == null)

      return results;

    // 优先调用宿主环境提供的filter方法

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

      return obj.filter(iterator, context);

    // 迭代集合中的元素, 并将通过处理器验证的元素放到数组中并返回

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

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

        results[results.length] = value;

    });

    return results;

  };

  // 与filter方法作用相反, 即返回没有通过处理器验证的元素列表

  _.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;

  };

  // 如果集合中所有元素均能通过处理器验证, 则返回true

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

    var result = true;

    if(obj == null)

      return result;

    // 优先调用宿主环境提供的every方法

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

      return obj.every(iterator, context);

    // 迭代集合中的元素

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

      // 这里理解为 result = (result && iterator.call(context, value, index, list))

      // 验证处理器的结果被转换为Boolean类型后是否为true值

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

        return breaker;

    });

    return !!result;

  };

  // 检查集合中任何一个元素在被转换为Boolean类型时, 是否为true值?或者通过处理器处理后, 是否值为true?

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

    // 如果没有指定处理器参数, 则默认的处理器函数会返回元素本身, 并在迭代时通过将元素转换为Boolean类型来判断是否为true值

    iterator || ( iterator = _.identity);

    var result = false;

    if(obj == null)

      return result;

    // 优先调用宿主环境提供的some方法

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

      return obj.some(iterator, context);

    // 迭代集合中的元素

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

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

        return breaker;

    });

    return !!result;

  };

  // 检查集合中是否有值与目标参数完全匹配(同时将匹配数据类型)

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

    var found = false;

    if(obj == null)

      return found;

    // 优先调用宿主环境提供的Array.prototype.indexOf方法

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

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

    // 通过any方法迭代集合中的元素, 验证元素的值和类型与目标是否完全匹配

    found = any(obj, function(value) {

      return value === target;

    });

    return found;

  };

  // 依次调用集合中所有元素的同名方法, 从第3个参数开始, 将被以此传入到元素的调用方法中

  // 返回一个数组, 存储了所有方法的处理结果

  _.invoke = function(obj, method) {

    // 调用同名方法时传递的参数(从第3个参数开始)

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

    // 依次调用每个元素的方法, 并将结果放入数组中返回

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

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

    });

  };

  // 遍历一个由对象列表组成的数组, 并返回每个对象中的指定属性的值列表

  _.pluck = function(obj, key) {

    // 如果某一个对象中不存在该属性, 则返回undefined

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

      return value[key];

    });

  };

  // 返回集合中的最大值, 如果不存在可比较的值, 则返回undefined

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

    // 如果集合是一个数组, 且没有使用处理器, 则使用Math.max获取最大值

    // 一般会是在一个数组存储了一系列Number类型的数据

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

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

    // 对于空值, 直接返回负无穷大

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

      return -Infinity;

    // 一个临时的对象, computed用于在比较过程中存储最大值(临时的)

    var result = {

      computed : -Infinity

    };

    // 迭代集合中的元素

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

      // 如果指定了处理器参数, 则比较的数据为处理器返回的值, 否则直接使用each遍历时的默认值

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

      // 如果比较值相比上一个值要大, 则将当前值放入result.value

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

        value : value,

        computed : computed

      });

    });

    // 返回最大值

    return result.value;

  };

  // 返回集合中的最小值, 处理过程与max方法一致

  _.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 之间

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

      // 将已经随机得到的元素放到shuffled数组末尾

      shuffled[index] = shuffled[rand];

      // 在前面得到的随机数的位置插入最新值

      shuffled[rand] = value;

    });

    // 返回一个数组, 该数组中存储了经过随机混排的集合元素

    return shuffled;

  };

  // 对集合中元素, 按照特定的字段或值进行排列

  // 相比Array.prototype.sort方法, sortBy方法支持对对象排序

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

    // val应该是对象的一个属性, 或一个处理器函数, 如果是一个处理器, 则应该返回需要进行比较的数据

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

      return obj[val];

    };

    // 调用顺序: _.pluck(_.map().sort());

    // 调用_.map()方法遍历集合, 并将集合中的元素放到value节点, 将元素中需要进行比较的数据放到criteria属性中

    // 调用sort()方法将集合中的元素按照criteria属性中的数据进行顺序排序

    // 调用pluck获取排序后的对象集合并返回

    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 : 0;

    }), 'value');

  };

  // 将集合中的元素, 按处理器返回的key分为多个数组

  _.groupBy = function(obj, val) {

    var result = {};

    // val将被转换为进行分组的处理器函数, 如果val不是一个Function类型的数据, 则将被作为筛选元素时的key值

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

      return obj[val];

    };

    // 迭代集合中的元素

    each(obj, function(value, index) {

      // 将处理器的返回值作为key, 并将相同的key元素放到一个新的数组

      var key = iterator(value, index);

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

    });

    // 返回已分组的数据

    return result;

  };

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

    iterator || ( iterator = _.identity);

    var low = 0, high = array.length;

    while(low > 1;

      iterator(array[mid]) = 0;

      });

    });

  };

  // 筛选并返回当前数组中与指定数据不相等的差异数据

  // 该函数一般用于删除数组中指定的数据, 并得到删除后的新数组

  // 该方法的作用与without相等, without方法参数形式上不允许数据被包含在数组中, 而difference方法参数形式上建议是数组(也可以和without使用相同形式的参数)

  _.difference = function(array) {

    // 对第2个参数开始的所有参数, 作为一个数组进行合并(仅合并第一层, 而并非深层合并)

    // rest变量存储验证数据, 在本方法中用于与原数据对比

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

    // 对合并后的数组数据进行过滤, 过滤条件是当前数组中不包含参数指定的验证数据的内容

    // 将符合过滤条件的数据组合为一个新的数组并返回

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

      return !_.include(rest, value);

    });

  };

  // 将每个数组的相同位置的数据作为一个新的二维数组返回, 返回的数组长度以传入参数中最大的数组长度为准, 其它数组的空白位置使用undefined填充

  // zip方法应该包含多个参数, 且每个参数应该均为数组

  _.zip = function() {

    // 将参数转换为数组, 此时args是一个二维数组

    var args = slice.call(arguments);

    // 计算每一个数组的长度, 并返回其中最大长度值

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

    // 依照最大长度值创建一个新的空数组, 该数组用于存储处理结果

    var results = new Array(length);

    // 循环最大长度, 在每次循环将调用pluck方法获取每个数组中相同位置的数据(依次从0到最后位置)

    // 将获取到的数据存储在一个新的数组, 放入results并返回

    for(var i = 0; i = 0; i--) {

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

      }

      // 返回最后一次调用函数的返回值

      return args[0];

    };

  };

  // 返回一个函数, 该函数作为调用计数器, 当该函数被调用times次(或超过times次)后, func函数将被执行

  // after方法一般用作异步的计数器, 例如在多个AJAX请求全部完成后需要执行一个函数, 则可以使用after在每个AJAX请求完成后调用

  _.after = function(times, func) {

    // 如果没有指定或指定无效次数, 则func被直接调用

    if(times  " ' \

  _.escape = function(string) {

    return ('' + string).replace(/&/g, '&').replace(/, '/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/');

  };

  // 指定一个对象的属性, 返回该属性对应的值, 如果该属性对应的是一个函数, 则会执行该函数并返回结果

  _.result = function(object, property) {

    if(object == null)

      return null;

    // 获取对象的值

    var value = object[property];

    // 如果值是一个函数, 则执行并返回, 否则将直接返回

    return _.isFunction(value) ? value.call(object) : value;

  };

  // 添加一系列自定义方法到Underscore对象中, 用于扩展Underscore插件

  _.mixin = function(obj) {

    // obj是一个集合一系列自定义方法的对象, 此处通过each遍历对象的方法

    each(_.functions(obj), function(name) {

      // 通过addToWrapper函数将自定义方法添加到Underscore构建的对象中, 用于支持对象式调用

      // 同时将方法添加到 _ 本身, 用于支持函数式调用

      addToWrapper(name, _[name] = obj[name]);

    });

  };

  // 获取一个全局唯一标识, 标识从0开始累加

  var idCounter = 0;

  // prefix表示标识的前缀, 如果没有指定前缀则直接返回标识, 一般用于给对象或DOM创建唯一ID

  _.uniqueId = function(prefix) {

    var id = idCounter++;

    return prefix ? prefix + id : id;

  };

  // 定义模板的界定符号, 在template方法中使用

  _.templateSettings = {

    // JavaScript可执行代码的界定符

    evaluate : //g,

    // 直接输出变量的界定符

    interpolate : //g,

    // 需要将HTML输出为字符串(将特殊符号转换为字符串形式)的界定符

    escape : //g

  };

 

  var noMatch = /.^/;

 

  // escapes对象记录了需要进行相互换转的特殊符号与字符串形式的对应关系, 在两者进行相互转换时作为索引使用

  // 首先根据字符串形式定义特殊字符

  var escapes = {

    '\\' : '\\',

    "'" : "'",

    'r' : '\r',

    'n' : '\n',

    't' : '\t',

    'u2028' : '\u2028',

    'u2029' : '\u2029'

  };

  // 遍历所有特殊字符字符串, 并以特殊字符作为key记录字符串形式

  for(var p in escapes)

  escapes[escapes[p]] = p;

  // 定义模板中需要替换的特殊符号, 包含反斜杠, 单引号, 回车符, 换行符, 制表符, 行分隔符, 段落分隔符

  // 在将字符串中的特殊符号转换为字符串形式时使用

  var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;

  // 在将字符串形式的特殊符号进行反转(替换)时使用

  var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;

 

  // 反转字符串中的特殊符号

  // 在模板中涉及到需要执行的JavaScript源码, 需要进行特殊符号反转, 否则如果以HTML实体或字符串形式出现, 会抛出语法错误

  var unescape = function(code) {

    return code.replace(unescaper, function(match, escape) {

      return escapes[escape];

    });

  };

  // Underscore模板解析方法, 用于将数据填充到一个模板字符串中

  // 模板解析流程:

  // 1. 将模板中的特殊符号转换为字符串

  // 2. 解析escape形式标签, 将内容解析为HTML实体

  // 3. 解析interpolate形式标签, 输出变量

  // 4. 解析evaluate形式标签, 创建可执行的JavaScript代码

  // 5. 生成一个处理函数, 该函数在得到数据后可直接填充到模板并返回填充后的字符串

  // 6. 根据参数返回填充后的字符串或处理函数的句柄

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

  // 在模板体内, 可通过argments获取2个参数, 分别为填充数据(名称为obj)和Underscore对象(名称为_)

  _.template = function(text, data, settings) {

    // 模板配置, 如果没有指定配置项, 则使用templateSettings中指定的配置项

    settings = _.defaults(settings || {}, _.templateSettings);

 

    // 开始将模板解析为可执行源码

    var source = "__p+='" + text.replace(escaper, function(match) {

      // 将特殊符号转移为字符串形式

      return '\\' + escapes[match];

    }).replace(settings.escape || noMatch, function(match, code) {

      // 解析escape形式标签 , 将变量中包含的HTML通过_.escape函数转换为HTML实体

      return "'+\n_.escape(" + unescape(code) + ")+\n'";

    }).replace(settings.interpolate || noMatch, function(match, code) {

      // 解析interpolate形式标签 , 将模板内容作为一个变量与其它字符串连接起来, 则会作为一个变量输出

      return "'+\n(" + unescape(code) + ")+\n'";

    }).replace(settings.evaluate || noMatch, function(match, code) {

      // 解析evaluate形式标签 , evaluate标签中存储了需要执行的JavaScript代码, 这里结束当前的字符串拼接, 并在新的一行作为JavaScript语法执行, 并将后面的内容再次作为字符串的开始, 因此evaluate标签内的JavaScript代码就能被正常执行

      return "';\n" + unescape(code) + "\n;__p+='";

    }) + "';\n";

    if(!settings.variable)

      source = 'with(obj||{}){\n' + source + '}\n';

    source = "var __p='';" + "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" + source + "return __p;\n";

 

    // 创建一个函数, 将源码作为函数执行体, 将obj和Underscore作为参数传递给该函数

    var render = new Function(settings.variable || 'obj', '_', source);

    // 如果指定了模板的填充数据, 则替换模板内容, 并返回替换后的结果

    if(data)

      return render(data, _);

    // 如果没有指定填充数据, 则返回一个函数, 该函数用于将接收到的数据替换到模板

    // 如果在程序中会多次填充相同模板, 那么在第一次调用时建议不指定填充数据, 在获得处理函数的引用后, 再直接调用会提高运行效率

    var template = function(data) {

      return render.call(this, data, _);

    };

    // 将创建的源码字符串添加到函数对象中, 一般用于调试和测试

    template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';

    // 没有指定填充数据的情况下, 返回处理函数句柄

    return template;

  };

  // 支持Underscore对象的方法链操作, 可参考 wrapper.prototype.chain

  _.chain = function(obj) {

    return _(obj).chain();

  };

  // Underscore对象封装相关方法

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

 

  // 创建一个包装器, 将一些原始数据进行包装

  // 所有的undersocre对象, 内部均通过wrapper函数进行构造和封装

  // Underscore与wrapper的内部关系:

  // -内部定义变量_, 将Underscore相关的方法添加到_, 这样就可以支持函数式的调用, 如_.bind()

  // -内部定义wrapper类, 将_的原型对象指向wrapper类的原型

  // -将Underscore相关的方法添加到wrapper原型, 创建的_对象就具备了Underscore的方法

  // -将Array.prototype相关方法添加到wrapper原型, 创建的_对象就具备了Array.prototype中的方法

  // -new _()时实际创建并返回了一个wrapper()对象, 并将原始数组存储到_wrapped变量, 并将原始值作为第一个参数调用对应方法

  var wrapper = function(obj) {

    // 原始数据存放在包装对象的_wrapped属性中

    this._wrapped = obj;

  };

  // 将Underscore的原型对象指向wrapper的原型, 因此通过像wrapper原型中添加方法, Underscore对象也会具备同样的方法

  _.prototype = wrapper.prototype;

 

  // 返回一个对象, 如果当前Underscore调用了chain()方法(即_chain属性为true), 则返回一个被包装的Underscore对象, 否则返回对象本身

  // result函数
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn