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

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

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

    我是FE,也是Fe

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

    統(tǒng)計

    留言簿(15)

    閱讀排行榜

    評論排行榜

    underscore中的function類函數(shù)解析

    underscore是一個非常不錯的基礎(chǔ)javascript庫,他提供了很多實用的方法,彌補了javascript原生 API調(diào)用的一些不足,讀他的文檔的時候,讀到array,object,uitlity的時候已經(jīng)非常興奮了。覺得足夠用了。但是我看到function這部分的時候,發(fā)現(xiàn)這些函數(shù)真的非常的有意義,結(jié)合源代碼來看看這部分function相關(guān)的功能。

    _.bind方法

    最常見的方法。作用是改變默認(rèn)的function中的this指向。需要說明的是在ECMA 5這個版本中function已經(jīng)自帶了一個bind方法,參見這里(該文章具體介紹了bind的集中使用場景)。bind的使用方法是:
    _.bind(function, object, [*arguments]) 

     下面是一個使用demo:

    var func = function(greeting){ 
        
    //this指向的是bind的第二個參數(shù)
         //  greeting 是bind的第三個參數(shù)
        return greeting + ': ' + this.name 
    };
    // bind返回的是一個新的function對象
    var newfunc = _.bind(func, {name : 'moe'}, 'hi');

    func();


    原本以為這個bind的源碼會很簡單,無非就是用apply返回新的function,但是看源碼發(fā)現(xiàn)挺講究:

     

    _.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;
        
    //將后面的參數(shù)轉(zhuǎn)化成數(shù)組
        args = slice.call(arguments, 2);
        
    return bound = function() {
          
    //如果當(dāng)前的this已經(jīng)指向的一個function的實例,就不需要再改變this的指向,因為此時的function已經(jīng)作為一個構(gòu)造函數(shù)在使用
          if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
          
    //否則function視為構(gòu)造函數(shù)使用,要保證this為構(gòu)造函數(shù)的實例
          ctor.prototype = func.prototype;
          
    var self = new ctor;
          
    //將function強制轉(zhuǎn)換成一個類的構(gòu)造函數(shù)
          var result = func.apply(self, args.concat(slice.call(arguments)));
          
    //Object(result) === result 只有當(dāng)result是Object時才會成立,基本的數(shù)據(jù)類型如number,string則不成立
          if (Object(result) === result) return result;
          
    return self;
        };
      };
    代碼實現(xiàn)還是考慮了普通函數(shù)調(diào)用,構(gòu)造函數(shù)調(diào)用,通過成員函數(shù)調(diào)用的情況,邏輯實現(xiàn)的很全面。

    _.bindAll

    bindAll方法可以將一個對象中所有的成員函數(shù)的this都指向這個對象,什么情況下對象的成員函數(shù)的this不指向?qū)ο竽兀勘热纾?/p>

    var buttonView = {
      label   : 'underscore',
      onClick : 
    function(){ alert('clicked: ' + this.label); },
      onHover : 
    function(){ console.log('hovering: ' + this.label); }
    };
    _.bindAll(buttonView);
    //當(dāng)成員函數(shù)作為事件監(jiān)聽的時候,因為默認(rèn)的事件監(jiān)聽,this都會指向當(dāng)前事件源
    //
    bindAll之后可以保證onClick中的this仍指向buttonView
    jQuery('#underscore_button').bind('click', buttonView.onClick);

    _.memoize(function, [hashFunction])

    該方法可以緩存函數(shù)返回結(jié)果,如果一個函數(shù)計算需要很長的時間,多次反復(fù)計算可以只計算一次緩存結(jié)果,默認(rèn)的緩存key是函數(shù)調(diào)用時的第一個參數(shù),也可以自己定義function(第二個參數(shù))來計算key

    _.memoize = function(func, hasher) {
        
    var memo = {};//緩存存放位置
         //_.indentity默認(rèn)取數(shù)組第一個元素
        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后面執(zhí)行函數(shù)與setTimeout功能一致

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

    defer也是延遲執(zhí)行方法,不同的是他能保證在當(dāng)前堆棧中的所有的代碼跑完之后再執(zhí)行function。其實就是setTimeout(fn,1);

    throttle:_.throttle(function, wait)

    throttle這個單詞的意思是使減速,用于控制頻繁觸發(fā)的 function的的頻率,比如,拖動頁面滾動條時scroll方法會以很高的頻率觸發(fā),如果在scroll的處理事件中做了很費時的操作,會導(dǎo)致瀏覽器假死,如果使用了throttle后,function被觸發(fā)的頻率可以降低。

    document.body.onscroll = _.throttle(function(){
            console.log(
    "scrolling:"+(document.body.scrollTop|| document.body.scrollTop);
        },
    100); 

    scroll事件默認(rèn)50ms觸發(fā)一次,但是使用throttle之后事件觸發(fā)頻率為100ms一次

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

    debounce 本意是“使反跳”,這個翻譯是在讓人看不明白。同樣用于處理頻繁觸發(fā)的事件,處理方法時,對于頻繁處理的時間,只在第一次觸發(fā)(是否觸發(fā)取決于immdiate 參數(shù)),和事件頻繁觸發(fā)最后一次觸發(fā)(有最多wait的延時)。拿滾動事件為例,滾動事件50ms觸發(fā)一次,如果設(shè)置wait為100ms。則在最后一次觸發(fā)scroll事件時,也就是停止?jié)L動時,在100ms后觸發(fā)function。如果immediate參數(shù)為true,開始滾動時也會觸發(fā)function

    document.body.onscroll = _.debounce(function(){
            
    //一次滾動過程觸發(fā)兩次該函數(shù)
            console.log("scrolling:"+(document.body.scrollTop|| document.body.scrollTop);
        },
    100,true);

    在整個滾動過程中觸發(fā)function,對于只關(guān)注整個滾動前后變化的處理非常有用。

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

    _.debounce = function(func, wait, immediate) {
        
    var timeout, result;
        
    return function() {
              
    var context = this, args = arguments;
              
    var later = function() {
                timeout 
    = null;//最后一次調(diào)用時清除延時
                if (!immediate) result = func.apply(context, args);
              };
              
    var callNow = immediate && !timeout;
              
    //每次func被調(diào)用,都是先清除延時再重新設(shè)置延時,這樣只有最后一次觸發(fā)func再經(jīng)過wait延時后才會調(diào)用func
              clearTimeout(timeout);//
              timeout = setTimeout(later, wait);
              
    //如果第一次func被調(diào)用 && immediate ->立即執(zhí)行func
              if (callNow) result = func.apply(context, args);
              
    return result;
        };
    };

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

        
    //延時wait后將more  throttling 設(shè)置為false
        var whenDone = _.debounce(function(){ 
            more 
    = throttling = false
        }, wait);
        
    return function() {
            context 
    = this; args = arguments;
            
    var later = function() {
                timeout 
    = null;
                
    if (more) { //more:最后一次func調(diào)用時,確保還能再調(diào)用一次
                      result = func.apply(context, args);
                }
                whenDone();
            };
            
    if (!timeout) timeout = setTimeout(later, wait);
            
    if (throttling) {
                more 
    = true;
            } 
    else {
                
    //每次觸發(fā)func 有會保證throttling 設(shè)置為true
                throttling = true;
                result 
    = func.apply(context, args);
            }
            
    //每次觸發(fā)func 在 wait延時后將 more  throttling 設(shè)置為false
            whenDone();
            
    return result;
        };
    };

    once: _.once(function)

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

    _.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可以將函數(shù)再包裹一層,返回一個新的函數(shù),新的函數(shù)里面可以調(diào)用原來的函數(shù),可以將原函數(shù)的處理結(jié)果再處理一次返回。類似與AOP切面。在函數(shù)處理前/后動態(tài)的添加一些額外的處理,下面是一個使用demo

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

    wrap的源碼:

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

    compose: _.compose(*functions)

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

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

    after:_.after(count, function)

    創(chuàng)建一個新的函數(shù),當(dāng)func反復(fù)調(diào)用時,count次才調(diào)用一次,比如:

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

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


    源碼:

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


    總結(jié):
    上面這些函數(shù)在開發(fā)中經(jīng)常能用到,能解決很多特定的問題。undercore的源碼也看得出非常老道,可以非常好的學(xué)習(xí)資料。

    參考資料:
    ES5中的bind介紹
    從underscore.js的源碼學(xué)習(xí)javascript
    帶注釋的underscore源碼



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

    評論

    # re: underscore中的function類函數(shù)解析 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   回復(fù)  更多評論   

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

    @digno

    很對  回復(fù)  更多評論   

    主站蜘蛛池模板: 亚洲国产AV无码一区二区三区 | 国产亚洲精品成人AA片| 啦啦啦中文在线观看电视剧免费版 | 亚洲国产精品18久久久久久| 四虎国产精品免费视| a国产成人免费视频| 亚洲va在线va天堂va手机| 日韩亚洲国产综合久久久| 久久青草国产免费观看| 亚洲欧洲免费无码| 亚洲国产精品高清久久久| 免费看韩国黄a片在线观看| 91国内免费在线视频| 亚洲日韩乱码中文字幕| 亚洲AV永久无码精品水牛影视| 久久无码av亚洲精品色午夜| 亚洲AV综合色区无码一区| 男女啪啪永久免费观看网站| 日本高清免费观看| 亚洲一久久久久久久久| 亚洲精品高清无码视频| 波多野结衣久久高清免费| 三年片在线观看免费大全电影| 国产亚洲婷婷香蕉久久精品| 啦啦啦www免费视频| 91青青青国产在观免费影视| 一级做a爱过程免费视频高清| 免费va人成视频网站全| 久9久9精品免费观看| 麻豆安全免费网址入口| 亚洲六月丁香婷婷综合| 亚洲人成网站影音先锋播放| 免费午夜爽爽爽WWW视频十八禁| 污网站在线观看免费| 成人亚洲国产va天堂| 久久亚洲中文字幕精品有坂深雪| 久草免费福利视频| 无码 免费 国产在线观看91| 国产成人精品亚洲日本在线| 亚洲一区二区电影| 亚洲av无码国产精品色午夜字幕 |