underscore学习总结

underscore介绍

underscore是一个很小很强大的函数库,帮助程序猿不用扩展内置的JS对象即可实现一系列常用的功能。对于“如果我面前是一个空白的HTML 页面,我想想立刻开发出网页,我需要做什么”这样的问题,underscore给出了答案。

underscore 提供了80多个 工具函数,来提供了常见的功能。比如map ,select,invoke,以及更专业的助手 函数绑定,javascript模板,深度相等测试。如果当今流行的浏览器中如果已经存内置方法,他会优先调用浏览器原生的方法比如ES5中新增的forEachmapreducefiltereverysome, indexOf 等。

underscore的函数主要包含了Collections,Arrays,Functions,Object,Utility,Chaining 等6块内容。对开发者来说是个很强的轮子。

underscore如何使用

相关文档

帮助文档

带注释的源码

Github;

获取方法:

开发环境版本 未压缩

生产环境版本 经过压缩混淆

也可以使用工具安装的方式来获取

  • Node.js npm install underscore
  • Require.js require([“underscore”], …
  • Bower bowser install underscore
  • Component component install jashkenas/underscore

使用Demo

之前使用过Jquery的时候我们学会了用$,使用underscore也同样的容易,只需要一个下划线_即可,
比如

_.each([1, 2, 3], alert);
=> alerts each number in turn...
_.each({one: 1, two: 2, three: 3}, alert);
=> alerts each number value in turn...

看到函数名字基本上就能猜到功能了,为了方便全面了解功能,这里仅列出函数的名字,仅对不熟悉的做一下解释,个别函数值列出了源码,其实看源码能学到更多。

Collections(数组,对象以及类数组可以调用的方法)

  • each
  • map
  • reduce
  • reduceright
  • find
  • filter
  • every
  • some 以上几个都是ES5 中的东东 就不在介绍了
  • where:选出所有的符合条件的
  • findWhere:只返回第一个符合条件的元素
  • reject 筛选出不符合条件的
  • contains
  • invoke依次传入集合中的方法,来调用一个函数
  • pluck 提取某个字段的集合
  • max 根据集合中的某一属性或者对其操作的结果,提取结果最大的元素。如果第二个参数省略,则根据数组中值得大小进行比较。比如

    var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
    _.max(stooges, function(stooge){ return stooge.age; });
    => {name: 'curly', age: 60};
    
  • min 根据集合中的某一属性或者对其操作的结果,提取结果最小的元素。

  • sortBy
  • groupBy
  • countBy
  • indexBy 给集合添加索引
  • shuffle打乱集合元素的顺序
  • sample从数组中随机子数组
  • toArray。将集合转化为数组

####Array Functions

  • first
  • initial 返回前几个元素比如

    `_.initial([5, 4, 3, 2, 1]); => [5, 4, 3, 2]`
    
  • last

  • rest
  • compact 返回一个数组的拷贝,其中不包括值为false的元素 比如

    _.compact([0, 1, false, 2, '', 3]);
    => [1, 2, 3]
    
  • flatten,把嵌套数组中的元素提取出来做为数组的元素。如果第二个参数为true 仅进行浅提取。比如

    _.flatten([1, [2], [3, [[4]]]]);
    => [1, 2, 3, 4];
    
    _.flatten([1, [2], [3, [[4]]]], true);
    => [1, 2, 3, [[4]]];
    
  • without

  • partition 将一个数组分组,其中的元素第一个满足第二个参数,第二个是不满足的
  • union 求数组的并集
  • intersection 交集
  • difference 差集
  • uniq 去掉数组中重复的元素
  • zip 看Demo

    _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
    => [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]]
    
  • object 把数组转为对象。

    _.object(['moe', 'larry', 'curly'], [30, 40, 50]);
    => {moe: 30, larry: 40, curly: 50}
    
    _.object([['moe', 30], ['larry', 40], ['curly', 50]]);
    => {moe: 30, larry: 40, curly: 50}
    
  • indexOf

  • `lastIndexOf
  • sortedIndex
  • range 产生一个连续的数组

Functions

  • bind 将函数绑定到具体的对象,并且可以绑定参数

    var func = function(greeting){ return greeting + ': ' + this.name };
    func = _.bind(func, {name: 'moe'}, 'hi');
    func();
    => 'hi: moe'
    
  • bindAll 同时绑定对象的方法,只需要指定相应的方法即可

    var buttonView = {
      label  : 'underscore',
      onClick: function(){ alert('clicked: ' + this.label); },
      onHover: function(){ console.log('hovering: ' + this.label); }
    };
    _.bindAll(buttonView, 'onClick', 'onHover');
    // When the button is clicked, this.label will have the correct value.
    jQuery('#underscore_button').bind('click', buttonView.onClick);
    
  • partial 绑定部分的参数。返回一个函数

    var add = function(a, b) { return a + b; };
    add5 = _.partial(add, 5);
    add5(10);
    => 15
    
  • memioze 缓存计算结果。

  • delay 延迟执行,类似于setTimeOut
  • defer也是延迟执行方法,不同的是他能保证在当前堆栈中的所有的代码跑完之后再执行function。其实就是setTimeout(fn,0);
  • throttle throttle这个单词的意思是使减速,用于控制频繁触发的 function的的频率,比如,拖动页面滚动条时scroll方法会以很高的频率触发,如果在scroll的处理事件中做了很费时的操作,会导致浏览器假死,如果使用了throttle后,function被触发的频率可以降低。

    var throttled = _.throttle(updatePosition, 100);
    $(window).scroll(throttled);
    
  • debounce 对于频繁处理的时间,只在第一次触发(是否触发取决于immdiate 参数),和事件频繁触发最后一次触发(有最多wait的延时)。拿滚动事件为例,滚动事件50ms触发一次,如果设置wait为100ms。则在最后一次触发scroll事件时,也就是停止滚动时,在100ms后触发function。如果immediate参数为true,开始滚动时也会触发function

    var lazyLayout = _.debounce(calculateLayout, 300);
    $(window).resize(lazyLayout);

  • once once能确保func只调用一次,如果用func返回一个什么对象,源码也比较简单,无非就是用一个标志位来标示是否运行过,缓存返回值
  • after 创建一个新的函数,当func反复调用时,count次才调用一次,比如:

    var afterA = _.after(3,a);
    afterA();//调用
    afterA();//不alert
    afterA();//不alert
    afterA();//调用

  • now 返回当前的时间戳 _.now = Date.now || function() { return new Date().getTime(); };

  • wrap
  • compose

####Objects

  • keys 返回 对象的key的数组

    _.keys({one: 1, two: 2, three: 3});
    => [“one”, “two”, “three”]

  • values 返回对象的value数组

    _.values({one: 1, two: 2, three: 3});
    => [1, 2, 3]

  • pairs 转为[key,valu]的集合

    _.values({one: 1, two: 2, three: 3});
    => [1, 2, 3]

  • invert 将 key和value反转

  • functions返回对象中方法的集合
  • extend 扩展对象的属性。

    _.extend({name: ‘moe’}, {age: 50});
    => {name: ‘moe’, age: 50}

  • pick 筛选字段,只选满足传入的key的字段

    _.pick({name: ‘moe’, age: 50, userid: ‘moe1’}, ‘name’, ‘age’);
    => {name: ‘moe’, age: 50}

  • omit 与上面相反

  • defaults
  • clone
  • tap 对象的拦截器,用于在对象方法链中,调试对象
  • has
  • matches
  • property

    var moe = {name: ‘moe’};
    ‘moe’ === _.property(‘name’)(moe);
    => true

  • isEqual 深度比较是否相等

    var moe   = {name: 'moe', luckyNumbers: [13, 27, 34]};
    var clone = {name: 'moe', luckyNumbers: [13, 27, 34]};
    moe == clone;
    => false
    _.isEqual(moe, clone);
    => true
    
  • isEmpty 判断对象是否为{}

    _.isEmpty([1, 2, 3]);
    => false
    _.isEmpty({});
    => true
    
  • isElement 判断是否为dom 元素

    _.isElement = function(obj) {

      return !!(obj && obj.nodeType === 1);
    };
    
  • isArray

  • isObject
  • isArguments
  • isFunction
  • isString
  • isNumber
  • isDate
  • isRegExp
    以上就不解释了,看源码吧

     each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
      _['is' + name] = function(obj) {
        return toString.call(obj) == '[object ' + name + ']';
      };
    });
    
  • isFinite 是否为有限数

    _.isFinite = function(obj) {
      return isFinite(obj) && !isNaN(parseFloat(obj));
    };
    
  • isBoolean 是否为bool值

    _.isBoolean = function(obj) {
      return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
    };
    
  • isNaN 注意typeof(NaA)为number

    _.isNaN = function(obj) {
      return _.isNumber(obj) && obj != +obj;
    };
    
  • isNull

    _.isNull = function(obj) {
        return obj === null;
      };
    
  • isUndefined

    _.isUndefined = function(obj) {
       return obj === void 0;
     };
    

####UTILITY FUNCTIONS

  • noConflict 这个 与jQuery中的类似,将_ 还原为之前代表的意思,以后使用

    var underscore = _.noConflict();

    _.noConflict = function() {
      root._ = previousUnderscore;
      return this;
    };
    
  • identify 返回用作参数的相同值。数学 f(x) = x、这个函数看着没有什么用途,但使用整个Underscore作为默认的迭代器

    _.identity = function(value) {
       return value;
     };
    
  • constant。创建一个返回与传入参数相同值得函数

    _.constant = function(value) {
        return function () {
          return value;
        };
      };
    
  • times 调用给定的迭代函数n次

     _.times = function(n, iterator, context) {
       var accum = Array(Math.max(0, n));
       for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
       return accum;
     };
    _(3).times(function(n){ dosomething });
    
  • radom 返回随机数

    _.random = function(min, max) {
      if (max == null) {
        max = min;
        min = 0;
      }
      return min + Math.floor(Math.random() * (max - min + 1));
    };
    
  • mixin 扩展underscore中的方法

    _.mixin({
      capitalize: function(string) {
        return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
      }
    });
    _("fabio").capitalize();
    => "Fabio"
    
  • uniqueId 为一个对象生成一个唯一的ID 可以自己加前缀

  • escape转义HTML字符串,替换&, <, >, “, ‘, /字符 替换为&, <, >, ", &#x27

    _.escape('Curly, Larry & Moe'); 
    => "Curly, Larry &amp; Moe" 
    
  • unescape 与上相反 替换 &, <, >, ", ' 替换为&, <, >, “, ‘, /

  • result _.result(object, property) 如果第二个参数是函数的话则调用这个函数,并将第一个参数作为函数的上下文,否则返回object[property]
  • template javascript模板编译函数使用<%= … %> 替换变量,<% … %>执行JS代码, <%- … %>让HTML被转义。

####Chaining
可以根据自己的喜好决定是函数式编程还是采用面向对象的方式来使用underscore,比如下的两种方式

_.map([1, 2, 3], function(n){ return n * 2; });
_([1, 2, 3]).map(function(n){ return n * 2; });

value 提取出_(obj)对象的值。比如

_([1, 2, 3]).value();
=> [1, 2, 3]

####OOP
如果_作为函数被调用的话,会返回一个包装的好的对象,这个对象拥有当前版本Underscore的所有方法。同时这个对象还可以通过调用chain实现对一个对象的包裹

链式调用

var lyrics = [
  {line: 1, words: "I'm a lumberjack and I'm okay"},
  {line: 2, words: "I sleep all night and I work all day"},
  {line: 3, words: "He's a lumberjack and he's okay"},
  {line: 4, words: "He sleeps all night and he works all day"}
];

_.chain(lyrics)
  .map(function(line) { return line.words.split(' '); })
  .flatten()
  .reduce(function(counts, word) {
    counts[word] = (counts[word] || 0) + 1;
    return counts;
  }, {})
  .value();
=> {lumberjack: 2, all: 4, night: 2 ... }

chain 的源码

_.chain = function(obj) {
    return _(obj).chain();
  };

同时下面还为_原型增加了方法

_.extend(_.prototype, {
  chain: function() {
    this._chain = true;
    return this;
  },
  value: function() {
    return this._wrapped;
  }
});

上面的例子中开始调用_.chain(lyrics),是调用的 .chain(obj)。然后经过(obj) 返回一个示例。接下来调用的是原型上的方法chain 将 this.chain = true; 然后返回this 这样就可以进行链式调用的,第一次调用的对象的chain,第二次调用的是_函数的原型对象的方法。接下来是一句神奇的代码

_.mixin(_);

然后调用了 mixin为把中的所有方法都赋给了_.prototype.于是经过包装的对象都是经过 new (obj)的,具有了对象的中的方法

mixin的源码 ,将传入对象的方法都添加到.prototype上

_.mixin = function(obj) {
  each(_.functions(obj), function(name) {
    var func = _[name] = obj[name];
    _.prototype[name] = function() {
      var args = [this._wrapped];
      push.apply(args, arguments);
      return result.call(this, func.apply(_, args));
    };
  });
};

看看执行_()发生了什么,回到源码开头

var _ = function(obj) {
   if (obj instanceof _) return obj; //如果传入的对象是 _的实例
   if (!(this instanceof _)) return new _(obj); // 如果不是通过new _()的,则new new _(obj) 一个实例继续调用
   this._wrapped = obj;// 传入其他参数 new 实例的时候传入的参数
 };

这是一个单例模式,通过创建一个underscore对象,如果以包裹的形式来调用的话。会将包裹的对象设置为内部的_wrapped变量。

git账户配置以及多账户配置

在使用git的时候,git与远程服务器是一般通过ssh传输的(也支持ftp,https),我们在管理远程分支之前 需要在本机上创建ssh-key密钥对,并把其中的公钥添加到github中。

单用户情况

如果你就会一直在你的计算计算机使用一个远程的Git服务器,并且账号是一个,比较简单,生成key的时候也没有太大注意的地方,直接运行如下的第一步然后按回车就可以了
1、在 gitbash上运行 ssh-keygen -t rsa -C “Github账户邮箱”
2、接下来会提示输入key的名字 默认名字为id_rsa .默认就行了
3、然后会提示输入口令,这里口令与Github中的密码无关,随便输入可以为空。

  1. 如果在第二步中的没有重新命名的话,则忽略此步骤,ssh agent默认只读取id_rsa,为了让SSH识别新的私钥,需将其添加到SSH agent中
    ssh-add id_rsa
    如果出现Could not open a connection to your authentication agent的错误,就试着先用以下命令:

    ssh-agent bash
    ssh-add id_rsa
    添加完之后 登陆Github 点击 网页右上侧的 Account Setting 按钮 - 选择 ssh-keys 点击Add SSH Key ,在title中输入名字,然后将公约即id_rsa.pub添加到ssh-key处。

在git bash的命令行中输入

ssh -T git@github.com 如果能正常访问即可

$ ssh -T git@github.com
Enter passphrase for key 'C:/Users/kunkun/.ssh/github.rsa':
Hi kunkun01! You've successfully authenticated, but GitHub does not provide shel
l access.

多账户配置

多账户又分为两种情况

  • 针对同一个服务器的同用户(比如 我平时开发开源的小东东,有的是一个账号是公司的账号对外开源项目用的,另外我自己也比较崇尚开源,所以自己也有了Github账号)
  • 针对不同服务器的用户(现在pass平台 部署应用都是通过git来管理的,比如常见的Openshift,Heroku appfog等,在这里我也注册了账号)

    在我们访问git服务器的时候,如果通过ssh的方式话,访问不同的服务器要使用不同的ssh-key。经过在第一步的过程中,在创建ssh-key的默认命名为id_rsa,如果使用不同的账户的,必须得给不同的key设置不同的名字,否则如果继续使用默认名字的话,会把之前的id_rsa覆盖掉。
    具体操作如下 user2是我的另外一个Github账户

    1、新建user2的SSH Key

    #新建SSH key:
    $ cd ~/.ssh # 切换到C:\Users\Administrator.ssh
    ssh-keygen -t rsa -C “mywork@email.com“ # 新建工作的SSH key

    设置名称为id_rsa_work

    Enter file in which to save the key (/c/Users/Administrator/.ssh/id_rsa): id_rsa_work
    ####2、新密钥添加到SSH agent中
    因为默认只读取id_rsa,为了让SSH识别新的私钥,需将其添加到SSH agent中:

    ssh-add ~/.ssh/id_rsa_work
    上面提到了,如果出现Could not open a connection to your authentication agent的错误,就试着用以下命令:

    ssh-agent bash
    ssh-add ~/.ssh/id_rsa_work

    3、修改config文件 将账户以及git服务器与对应的密钥关联。在C:\Users\kunkun\.ssh目录下找到config文件,如果没有就创建名字为config的文本文件(无后缀名),然后修改如下: 比如我的config配置如下:

    该文件用于配置私钥对应的服务器

    Default github user(first@mail.com)

    Host github.com
    HostName github.com
    User git
    IdentityFile C:/Users/Administrator/.ssh/id_rsa

    second user(second@mail.com)

    建一个github别名,新建的帐号使用这个别名做克隆和更新

    Host github2
    HostName github.com
    User git
    IdentityFile C:/Users/Administrator/.ssh/id_rsa_work
    其规则就是:从上至下读取config的内容,在每个Host下寻找对应的私钥。这里将GitHub SSH仓库地址中的git@github.com替换成新建的Host别名如:github2,那么原地址是:git@github.com:kunkun/Mywork.git,替换后应该是:github2:kunkun/Mywork.git。

4、用记事本打开新生成的~/.ssh/id_rsa2.pub 和id_rsa_work 复制里面的内容、登陆Github网站,将里面的内容添加到分别加入对应的ssh keys中。

5、测试:

$ ssh -T git@github.com
Hi BeginMan! You've successfully authenticated, but GitHub does not provide shel
l access.

$ ssh -T github2
Hi BeginMan! You've successfully authenticated, but GitHub does not provide shel
l access.

克隆的时候将Github.com替换为对应的别名即可
比如我的第二个账户中的一个仓库地址为git@github.com:kunkun12/PiesLayer.git。但是我以上的配置中使用

git clone git@github2:kunkun12/PiesLayer.git

注意:如果你只是通过这篇文章中所述配置了Host,那么你多个账号下面的提交用户会是一个人,所以需要通过命令git config –global –unset user.email删除用户账户设置,在每一个repo下面使用git config –local user.email ‘你的github邮箱@mail.com’ 命令单独设置用户账户信息