Rumah  >  Artikel  >  hujung hadapan web  >  Underscore.js 1.3.3 中文注释翻译说明_基础知识

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

WBOY
WBOYasal
2016-05-16 15:52:541294semak imbas
// 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函数
Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn