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

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

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

    憨厚生

    ----Java's Slave----
    ***Java's Host***

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      165 隨筆 :: 17 文章 :: 90 評論 :: 0 Trackbacks

    #

    轉(zhuǎn) http://realazy.org/blog/2007/08/16/lazy-function-definition-pattern/

    惰性函數(shù)定義模式

    這 篇文章闡述的是一種函數(shù)式編程(functional-programming)設(shè)計模式,我稱之為惰性函數(shù)定義(Lazy Function Definition)。我不止一次發(fā)現(xiàn)這種模式在JavaScript中大有用處,尤其是編寫跨瀏覽器的、高效運行的庫之時。

    熱身問題

    編寫一個函數(shù)foo,它返回的是Date對象,這個對象保存的是foo首次調(diào)用的時間。

    方法一:上古時代的技術(shù)

    這個最簡陋的解決方案使用了全局變量t來保存Date對象。foo首次調(diào)用時會把時間保存到t中。接下來的再次調(diào)用,foo只會返回保存在t中的值。

    var t;
    function foo() {
    if (t) {
    return t;
    }
    t = new Date();
    return t;
    }

    但是這樣的代碼有兩個問題。第一,變量t是一個多余的全局變量,并且在 foo調(diào)用的間隔期間有可能被更改。第二,在調(diào)用時這些代碼的效率并沒有得到優(yōu)化因為每次調(diào)用 foo都必須去求值條件。雖然在這個例子中,求值條件并不顯得低效,但在現(xiàn)實世界的實踐例子中常常會有極為昂貴的條件求值,比如在if-else-else-…的結(jié)構(gòu)中。

    方法二:模塊模式

    我們可以通過被認為歸功于CornfordCrockford模塊模式來彌補第一種方法的缺陷。使用閉包可以隱藏全局變量t,只有在 foo內(nèi)的代碼才可以訪問它。

    var foo = (function() {
    var t;
    return function() {
    if (t) {
    return t;
    }
    t = new Date();
    return t;
    }
    })();

    但這仍然沒有優(yōu)化調(diào)用時的效率,因為每次調(diào)用foo依然需要求值條件。

    雖然模塊模式是一個強大的工具,但我堅信在這種情形下它用錯了地方。

    方法三:函數(shù)作為對象

    由于JavaScript的函數(shù)也是對象,所以它可以帶有屬性,我們可以據(jù)此實現(xiàn)一種跟模塊模式質(zhì)量差不多的解決方案。

    function foo() {
    if (foo.t) {
    return foo.t;
    }
    foo.t = new Date();
    return foo.t;
    }

    在一些情形中,帶有屬性的函數(shù)對象可以產(chǎn)生比較清晰的解決方案。我認為,這個方法在理念上要比模式模塊方法更為簡單。

    這個解決方案避免了第一種方法中的全局變量t,但仍然解決不了foo每次調(diào)用所帶來的條件求值。

    方法四:惰性函數(shù)定義

    現(xiàn)在,這是你閱讀這篇文章的理由:

    var foo = function() {
    var t = new Date();
    foo = function() {
    return t;
    };
    return foo();
    };

    foo首次調(diào)用,我們實例化一個新的Date對象并重置 foo到一個新的函數(shù)上,它在其閉包內(nèi)包含Date對象。在首次調(diào)用結(jié)束之前,foo的新函數(shù)值也已調(diào)用并提供返回值。

    接下來的foo調(diào)用都只會簡單地返回t保留在其閉包內(nèi)的值。這是非常快的查找,尤其是,如果之前那些例子的條件非常多和復(fù)雜的話,就會顯得很高效。

    弄清這種模式的另一種途徑是,外圍(outer)函數(shù)對foo的首次調(diào)用是一個保證(promise)。它保證了首次調(diào)用會重定義foo為一個非常有用的函數(shù)?;\統(tǒng)地說,術(shù)語“保證” 來自于Scheme的惰性求值機制(lazy evaluation mechanism)。每一位JavaScript程序員真的都應(yīng)該學習Scheme,因為它有很多函數(shù)式編程相關(guān)的東西,而這些東西會出現(xiàn)在JavaScript中。

    確定頁面滾動距離

    編寫跨瀏覽器的JavaScript, 經(jīng)常會把不同的瀏覽器特定的算法包裹在一個獨立的JavaScript函數(shù)中。這就可以通過隱藏瀏覽器差異來標準化瀏覽器API,并讓構(gòu)建和維護復(fù)雜的頁 面特性的JavaScript更容易。當包裹函數(shù)被調(diào)用,就會執(zhí)行恰當?shù)臑g覽器特定的算法。

    在拖放庫中,經(jīng)常需要使用由鼠標事件提供的光標位置信息。鼠標事件給予的光標坐標相對于瀏覽器窗口而不是頁面。加上頁面滾動距離鼠標的窗口坐標的距離即可得到鼠標相對于頁面的坐標。所以我們需要一個反饋頁面滾動的函數(shù)。演示起見,這個例子定義了一個函數(shù)getScrollY。因為拖放庫在拖拽期間會持續(xù)運行,我們的getScrollY必須盡可能高效。

    不過卻有四種不同的瀏覽器特定的頁面滾動反饋算法。Richard Cornford在他的feature detection article文章中提到這些算法。最大的陷阱在于這四種頁面滾動反饋算法其中之一使用了 document.body. JavaScript庫通常會在HTML文檔的<head>加載,與此同時docment.body并不存在。所以在庫載入的時候,我們并不能使用特性檢查(feature detection)來確定使用哪種算法。

    考慮到這些問題,大部分JavaScript庫會選擇以下兩種方法中的一種。第一個選擇是使用瀏覽器嗅探navigator.userAgent,為該瀏覽器創(chuàng)建高效、簡潔的getScrollY. 第二個更好些的選擇是getScrollY在每一次調(diào)用時都使用特性檢查來決定合適的算法。但是第二個選擇并不高效。

    好消息是拖放庫中的getScrollY只會在用戶與頁面的元素交互時才會用到。如果元素業(yè)已出現(xiàn)在頁面中,那么document.body也會同時存在。getScrollY的首次調(diào)用,我們可以使用惰性函數(shù)定義模式結(jié)合特性檢查來創(chuàng)建高效的getScrollY.

    var getScrollY = function() {

    if (typeof window.pageYOffset == 'number') {
    getScrollY = function() {
    return window.pageYOffset;
    };

    } else if ((typeof document.compatMode == 'string') &&
    (document.compatMode.indexOf('CSS') >= 0) &&
    (document.documentElement) &&
    (typeof document.documentElement.scrollTop == 'number')) {
    getScrollY = function() {
    return document.documentElement.scrollTop;
    };

    } else if ((document.body) &&
    (typeof document.body.scrollTop == 'number')) {
    getScrollY = function() {
    return document.body.scrollTop;
    }

    } else {
    getScrollY = function() {
    return NaN;
    };

    }

    return getScrollY();
    }

    總結(jié)

    惰性函數(shù)定義模式讓我可以編寫一些緊湊、健壯、高效的代碼。用到這個模式的每一次,我都會抽空贊嘆JavaScript的函數(shù)式編程能力。

    JavaScript同時支持函數(shù)式和面向?qū)ο蟊愠?。市面上有很多重點著墨于面向?qū)ο笤O(shè)計模式的書都可以應(yīng)用到JavaScript編程中。不過卻沒有多少書涉及函數(shù)式設(shè)計模式的例子。對于JavaScript社區(qū)來說,還需要很長時間來積累良好的函數(shù)式模式。

    原文:Lazy Function Definition Pattern. 轉(zhuǎn)載沒有我的信息沒有關(guān)系,但你一定得寫上原文信息,謝謝。

    更新

    這個模式雖然有趣,但由于大量使用閉包,可能會由于內(nèi)存管理的不善而導致性能問題。來自FCKeditor的FredCK改進了getScrollY,既使用了這種模式,也避免了閉包:

    var getScrollY = function() {

    if (typeof window.pageYOffset == 'number')
    return (getScrollY = getScrollY.case1)();

    var compatMode = document.compatMode;
    var documentElement = document.documentElement;

    if ((typeof compatMode == 'string') &&
    (compatMode.indexOf('CSS') >= 0) &&
    (documentElement) &&
    (typeof documentElement.scrollTop == 'number'))
    return (getScrollY = getScrollY.case2)();

    var body = document.body ;
    if ((body) &&
    (typeof body.scrollTop == 'number'))
    return (getScrollY = getScrollY.case3)();

    return (getScrollY = getScrollY.case4)();
    };

    getScrollY.case1 = function() {
    return window.pageYOffset;
    };

    getScrollY.case2 = function() {
    return documentElement.scrollTop;
    };

    getScrollY.case3 = function() {
    return body.scrollTop;
    };

    getScrollY.case4 = function() {
    return NaN;
    };

    請看具體的評論



    posted @ 2009-03-13 17:04 二胡 閱讀(157) | 評論 (0)編輯 收藏

    僅列出標題
    共165頁: First 上一頁 100 101 102 103 104 105 106 107 108 下一頁 Last 
    主站蜘蛛池模板: 国产成人精品免费视频大全| 欧美三级在线电影免费| 四虎影库久免费视频| 亚洲人成人无码.www石榴| 在线观看免费人成视频色9| 亚洲精品福利在线观看| 美丽姑娘免费观看在线观看中文版| 日本一区二区三区免费高清 | 亚洲国产精品日韩专区AV| 亚洲AV色欲色欲WWW| 免费毛片a线观看| 免费观看黄网站在线播放| 中文文字幕文字幕亚洲色| 久久久久久国产精品免费免费| 欧洲 亚洲 国产图片综合| 日本一区午夜艳熟免费| 国产美女被遭强高潮免费网站| 亚洲精品私拍国产福利在线| 亚洲色欲色欱wwW在线| 免费观看毛片视频| 亚洲第一页中文字幕| 国产三级在线观看免费| 无遮挡a级毛片免费看| 亚洲伊人久久大香线蕉综合图片| 久久精品成人免费观看| 亚洲人成免费电影| 亚洲成a人片在线观看国产| 久久成人18免费网站| 亚洲国产日韩在线人成下载| 免费看www视频| 中国一级毛片免费看视频| 精品亚洲国产成人| 国产成人99久久亚洲综合精品| 久久久免费精品re6| 亚洲日韩国产一区二区三区在线 | 亚洲最大视频网站| 一级毛片不卡片免费观看| 亚洲AV无码专区在线电影成人| 久久亚洲国产精品123区| 91大神免费观看| 亚洲综合无码一区二区三区|