当前位置:编程学习 > 网站相关 >>

underscore中的function类函数解析

underscore是一个非常不错的基础javascript库,他提供了很多实用的方法,弥补了javascript原生 API调用的一些不足,读他的文档的时候,读到array,object,uitlity的时候已经非常兴奋了。觉得足够用了。但是我看到function这部分的时候,发现这些函数真的非常的有意义,结合源代码来看看这部分function相关的功能。
_.bind方法

最常见的方法。作用是改变默认的function中的this指向。需要说明的是在ECMA 5这个版本中function已经自带了一个bind方法,参见这里(该文章具体介绍了bind的集中使用场景)。bind的使用方法是:
_.bind(function, object, [*arguments])
 下面是一个使用demo:

var func = function(greeting){
    //this指向的是bind的第二个参数
     //  greeting 是bind的第三个参数
    return greeting + ': ' + this.name
};
// bind返回的是一个新的function对象
var newfunc = _.bind(func, {name : 'moe'}, 'hi');

func();

原本以为这个bind的源码会很简单,无非就是用apply返回新的function,但是看源码发现挺讲究:

 

_.bind = function bind(func, context) {
    var bound, args;
    //如果function存在原生的bind方法使用原生的bind
    if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
    //不是function,抛异常
    if (!_.isFunction(func)) throw new TypeError;
    //将后面的参数转化成数组
    args = slice.call(arguments, 2);
    return bound = function() {
      //如果当前的this已经指向的一个function的实例,就不需要再改变this的指向,因为此时的function已经作为一个构造函数在使用
      if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
      //否则function视为构造函数使用,要保证this为构造函数的实例
      ctor.prototype = func.prototype;
      var self = new ctor;
      //将function强制转换成一个类的构造函数
      var result = func.apply(self, args.concat(slice.call(arguments)));
      //Object(result) === result 只有当result是Object时才会成立,基本的数据类型如number,string则不成立
      if (Object(result) === result) return result;
      return self;
    };
  };
代码实现还是考虑了普通函数调用,构造函数调用,通过成员函数调用的情况,逻辑实现的很全面。
_.bindAll

bindAll方法可以将一个对象中所有的成员函数的this都指向这个对象,什么情况下对象的成员函数的this不指向对象呢?比如:

var buttonView = {
  label   : 'underscore',
  onClick : function(){ alert('clicked: ' + this.label); },
  onHover : function(){ console.log('hovering: ' + this.label); }
};
_.bindAll(buttonView);
//当成员函数作为事件监听的时候,因为默认的事件监听,this都会指向当前事件源
//bindAll之后可以保证onClick中的this仍指向buttonView
jQuery('#underscore_button').bind('click', buttonView.onClick);
_.memoize(function, [hashFunction])

该方法可以缓存函数返回结果,如果一个函数计算需要很长的时间,多次反复计算可以只计算一次缓存结果,默认的缓存key是函数调用时的第一个参数,也可以自己定义function(第二个参数)来计算key

_.memoize = function(func, hasher) {
    var memo = {};//缓存存放位置
     //_.indentity默认取数组第一个元素
    hasher || (hasher = _.identity);
    return function() {
      var key = hasher.apply(this, arguments);
      return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
    };
  };
delay : _.delay(function, wait, [*arguments])

delay方法在指定的wait后面执行函数与setTimeout功能一致

defer: _.defer(function, [*arguments])

defer也是延迟执行方法,不同的是他能保证在当前堆栈中的所有的代码跑完之后再执行function。其实就是setTimeout(fn,1);

throttle:_.throttle(function, wait)

throttle这个单词的意思是使减速,用于控制频繁触发的 function的的频率,比如,拖动页面滚动条时scroll方法会以很高的频率触发,如果在scroll的处理事件中做了很费时的操作,会导致浏览器假死,如果使用了throttle后,function被触发的频率可以降低。

document.body.onscroll = _.throttle(function(){
        console.log("scrolling:"+(document.body.scrollTop|| document.body.scrollTop);
    },100);
scroll事件默认50ms触发一次,但是使用throttle之后事件触发频率为100ms一次

debounce: _.debounce(function, wait, [immediate])

debounce 本意是“使反跳”,这个翻译是在让人看不明白。同样用于处理频繁触发的事件,处理方法时,对于频繁处理的时间,只在第一次触发(是否触发取决于immdiate 参数),和事件频繁触发最后一次触发(有最多wait的延时)。拿滚动事件为例,滚动事件50ms触发一次,如果设置wait为100ms。则在最后一次触发scroll事件时,也就是停止滚动时,在100ms后触发function。如果immediate参数为true,开始滚动时也会触发function

document.body.onscroll = _.debounce(function(){
        //一次滚动过程触发两次该函数
        console.log("scrolling:"+(document.body.scrollTop|| document.body.scrollTop);
    },100,true);
在整个滚动过程中触发function,对于只关注整个滚动前后变化的处理非常有用。

下面是_.debounce和_.throttle的源码:

_.debounce = function(func, wait, immediate) {
    var timeout, result;
    return function() {
          var context = this, args = arguments;
          var later = function() {
            timeout = null;//最后一次调用时清除延时
            if (!immediate) result = func.apply(context, args);
          };
          var callNow = immediate && !timeout;
          //每次func被调用,都是先清除延时再重新设置延时,这样只有最后一次触发func再经过wait延时后才会调用func
          clearTimeout(timeout);//
          timeout = setTimeout(later, wait);
          //如果第一次func被调用 && immediate ->立即执行func
          if (callNow) result = func.apply(context, args);
          return result;
    };
};

_.throttle = function(func, wait) {
    var context, args, timeout, throttling, more, result;

    //延时wait后将more  throttling 设置为false
    var whenDone = _.debounce(function(){
        more = throttling = false;
    }, wait);
    return function() {
        context = this; args = arguments;
        var later = function() {
            timeout = null;
       &nb

补充:综合编程 , 其他综合 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,