Underscore.js 1.3.3 中文注释翻译说明_基础知识
程序员文章站
2022-03-09 13:32:19
...
// 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函数声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
相关文章
相关视频