<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    我是FE,也是Fe

    前端來源于不斷的點滴積累。我一直在努力。

    統計

    留言簿(15)

    閱讀排行榜

    評論排行榜

    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;
                
    if (more) { //more:最后一次func調用時,確保還能再調用一次
                      result = func.apply(context, args);
                }
                whenDone();
            };
            
    if (!timeout) timeout = setTimeout(later, wait);
            
    if (throttling) {
                more 
    = true;
            } 
    else {
                
    //每次觸發func 有會保證throttling 設置為true
                throttling = true;
                result 
    = func.apply(context, args);
            }
            
    //每次觸發func 在 wait延時后將 more  throttling 設置為false
            whenDone();
            
    return result;
        };
    };

    once: _.once(function)

    once能確保func只調用一次,如果用func返回一個什么對象,這個對象成了單例。源碼也比較簡單,無非就是用一個標志位來標示是否運行過,緩存返回值

    _.once = function(func) {
        
    var ran = false, memo;
        
    return function() {
          
    if (ran) return memo;
          ran 
    = true;
          memo 
    = func.apply(this, arguments);
          func 
    = null;
          
    return memo;
        };
      };

    wrap: _.wrap(function, wrapper)

    wrap可以將函數再包裹一層,返回一個新的函數,新的函數里面可以調用原來的函數,可以將原函數的處理結果再處理一次返回。類似與AOP切面。在函數處理前/后動態的添加一些額外的處理,下面是一個使用demo

    var hello = function(name) { return "hello: " + name; };
    //wrap返回一個新的函數
    hello = _.wrap(hello, function(func) {
      
    // 在新函數內部可以繼續調用原函數
      return "before, " + func("moe"+ ", after";
    });
    hello();

    wrap的源碼:

    _.wrap = function(func, wrapper) {
        
    return function() {
         
    //將原函數當新函數的一個參數傳入
          var args = [func];
          push.apply(args, arguments);
          
    return wrapper.apply(this, args);
        };
      };

    compose: _.compose(*functions)

    將多個函數處理過程合并,每個函數可以調用前面函數的運行結果,_.compose(func1,func2);相當于func1(func2())。看看他的源碼:

    _.compose = function() {
        
    var funcs = arguments;
        
    return function() {
          
    var args = arguments;
          
    //循環調用參數中的function
          for (var i = funcs.length - 1; i >= 0; i--) {
            args 
    = [funcs[i].apply(this, args)];
          }
          
    return args[0];//先調用的函數結果最為下一個函數的參數
        };
      };

    after:_.after(count, function)

    創建一個新的函數,當func反復調用時,count次才調用一次,比如:

    function a(){
        alert(
    "a");
    }

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


    源碼:

    _.after = function(times, func) {
        
    if (times <= 0return func();
        
    return function() {
          
    if (--times < 1) {
            
    return func.apply(this, arguments);
          }
        };
      };


    總結:
    上面這些函數在開發中經常能用到,能解決很多特定的問題。undercore的源碼也看得出非常老道,可以非常好的學習資料。

    參考資料:
    ES5中的bind介紹
    從underscore.js的源碼學習javascript
    帶注釋的underscore源碼



    posted on 2012-11-08 13:14 衡鋒 閱讀(2876) 評論(2)  編輯  收藏 所屬分類: javascriptWeb開發

    評論

    # re: underscore中的function類函數解析 2013-08-09 14:24 digno

    function a(som){
    alert(som);
    }
    afterA = _.after(3,a);
    afterA("a"); // no
    afterA("b"); // no
    afterA("c"); // yes
    afterA("d"); // yes
    afterA("e"); // yes
    afterA("f"); // yes   回復  更多評論   

    # re: underscore中的function類函數解析[未登錄] 2013-10-21 16:39 wj

    @digno

    很對  回復  更多評論   

    主站蜘蛛池模板: 亚洲av无码不卡私人影院| 亚洲高清专区日韩精品| aa级毛片毛片免费观看久| 亚洲一本综合久久| 精品免费国产一区二区| 免费人成在线观看视频高潮 | 亚洲色图校园春色| 国内一级一级毛片a免费| xvideos永久免费入口| 亚洲第一精品电影网| 亚洲第一黄色网址| 亚洲一级免费毛片| 免费一级毛片在线播放放视频| 亚洲ⅴ国产v天堂a无码二区| 国产精品jizz在线观看免费| 久久久久高潮毛片免费全部播放| 香蕉视频亚洲一级| 亚洲欧洲日产韩国在线| 亚洲综合色视频在线观看| 国产在线a免费观看| 成人A毛片免费观看网站| 日韩亚洲国产高清免费视频| 亚洲色欲久久久综合网| 免费爱爱的视频太爽了| 99久久免费中文字幕精品| 一级毛片免费全部播放| 亚洲不卡影院午夜在线观看| 久久亚洲国产成人亚| 亚洲国产成人乱码精品女人久久久不卡| xxxxwww免费| 久久免费高清视频| 天堂亚洲免费视频| 含羞草国产亚洲精品岁国产精品| 亚洲成人黄色在线观看| 久久亚洲精品中文字幕三区| 免费大片黄手机在线观看| 性做久久久久久久免费看| 午夜免费福利小电影| A级毛片高清免费视频在线播放| 免费又黄又爽又猛大片午夜| 色天使亚洲综合一区二区|