// 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 < l; i++) {

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

          return;

      }

    } else {

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

      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 "" + i);

    // 返回的结果是一个二维数组

    return results;

  };

  // 搜索一个元素在数组中首次出现的位置, 如果元素不存在则返回 -1

  // 搜索时使用 === 对元素进行匹配

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

    if(array == null)

      return -1;

    var i, l;

    if(isSorted) {

      i = _.sortedIndex(array, item);

      return array[i] === item " ' 
  _.escape = function(string) {

    return ('' + string).replace(/&/g, '&').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) "'" : "'",

    '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函数用于在构造方法链时返回Underscore的包装对象

  var result = function(obj, chain) {

    return chain ? _(obj).chain() : obj;

  };

  // 将一个自定义方法添加到Underscore对象中(实际是添加到wrapper的原型中, 而Underscore对象的原型指向了wrapper的原型)

  var addToWrapper = function(name, func) {

    // 向wrapper原型中添加一个name函数, 该函数调用func函数, 并支持了方法链的处理

    wrapper.prototype[name] = function() {

      // 获取func函数的参数, 并将当前的原始数据添加到第一个参数

      var args = slice.call(arguments);

      unshift.call(args, this._wrapped);

      // 执行函数并返回结果, 并通过result函数对方法链进行封装, 如果当前调用了chain()方法, 则返回封装后的Underscore对象, 否则返回对象本身

      return result(func.apply(_, args), this._chain);

    };

  };

  // 将内部定义的_(下划线, 即Underscore方法集合对象)中的方法复制到wrapper的原型链中(即Underscore的原型链中)

  // 这是为了在构造对象式调用的Underscore对象时, 这些对象也会具有内部定义的Underscore方法

  _.mixin(_);

 

  // 将Array.prototype中的相关方法添加到Underscore对象中, 因此在封装后的Underscore对象中也可以直接调用Array.prototype中的方法

  // 如: _([]).push()

  each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {

    // 获取Array.prototype中对应方法的引用

    var method = ArrayProto[name];

    // 将该方法添加到Underscore对象中(实际是添加到wrapper的原型对象, 因此在创建Underscore对象时同时具备了该方法)

    wrapper.prototype[name] = function() {

      // _wrapped变量中存储Underscore对象的原始值

      var wrapped = this._wrapped;

      // 调用Array对应的方法并返回结果

      method.apply(wrapped, arguments);

      var length = wrapped.length;

      if((name == 'shift' || name == 'splice') && length === 0)

        delete wrapped[0];

      // 即使是对于Array中的方法, Underscore同样支持方法链操作

      return result(wrapped, this._chain);

    };

  });

  // 作用同于上一段代码, 将数组中的一些方法添加到Underscore对象, 并支持了方法链操作

  // 区别在于上一段代码所添加的函数, 均返回Array对象本身(也可能是封装后的Array), concat, join, slice方法将返回一个新的Array对象(也可能是封装后的Array)

  each(['concat', 'join', 'slice'], function(name) {

    var method = ArrayProto[name];

    wrapper.prototype[name] = function() {

      return result(method.apply(this._wrapped, arguments), this._chain);

    };

  });

  // 对Underscore对象进行链式操作的声明方法

  wrapper.prototype.chain = function() {

    // this._chain用来标示当前对象是否使用链式操作

    // 对于支持方法链操作的数据, 一般在具体方法中会返回一个Underscore对象, 并将原始值存放在_wrapped属性中, 也可以通过value()方法获取原始值

    this._chain = true;

    return this;

  };

  // 返回被封装的Underscore对象的原始值(存放在_wrapped属性中)

  wrapper.prototype.value = function() {

    return this._wrapped;

  };

}).call(this);

华山资源网 Design By www.eoogi.com
广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
华山资源网 Design By www.eoogi.com