<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)中。

    方法二:模塊模式

    我們可以通過被認(rèn)為歸功于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)生比較清晰的解決方案。我認(rèn)為,這個方法在理念上要比模式模塊方法更為簡單。

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

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

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

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

    當(dāng)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)該學(xué)習(xí)Scheme,因為它有很多函數(shù)式編程相關(guān)的東西,而這些東西會出現(xiàn)在JavaScript中。

    確定頁面滾動距離

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

    在拖放庫中,經(jīng)常需要使用由鼠標(biāo)事件提供的光標(biāo)位置信息。鼠標(biāo)事件給予的光標(biāo)坐標(biāo)相對于瀏覽器窗口而不是頁面。加上頁面滾動距離鼠標(biāo)的窗口坐標(biāo)的距離即可得到鼠標(biāo)相對于頁面的坐標(biāo)。所以我們需要一個反饋頁面滾動的函數(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ū)ο蟊愠獭J忻嫔嫌泻芏嘀攸c著墨于面向?qū)ο笤O(shè)計模式的書都可以應(yīng)用到JavaScript編程中。不過卻沒有多少書涉及函數(shù)式設(shè)計模式的例子。對于JavaScript社區(qū)來說,還需要很長時間來積累良好的函數(shù)式模式。

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

    更新

    這個模式雖然有趣,但由于大量使用閉包,可能會由于內(nèi)存管理的不善而導(dǎo)致性能問題。來自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 on 2009-03-13 17:04 二胡 閱讀(156) 評論(0)  編輯  收藏 所屬分類: JS
    主站蜘蛛池模板: 亚洲一级黄色大片| 亚洲天堂中文字幕| 一个人看的www在线观看免费 | 乱人伦中文视频在线观看免费| 激情婷婷成人亚洲综合| 蜜臀91精品国产免费观看| 亚洲精品无码专区在线播放| 男人都懂www深夜免费网站| 国产精品成人四虎免费视频| 亚洲日本VA中文字幕久久道具| 大学生美女毛片免费视频| 亚洲av无码偷拍在线观看| 无码专区一va亚洲v专区在线| 亚洲伊人久久精品| 久久这里只精品99re免费| 2022年亚洲午夜一区二区福利| 亚洲黄色免费观看| 亚洲不卡av不卡一区二区| 无遮挡免费一区二区三区 | 国产亚洲美日韩AV中文字幕无码成人| 一级A毛片免费观看久久精品 | 亚洲国产av一区二区三区丶| 成人免费视频一区二区三区| 久久亚洲精品无码VA大香大香| aⅴ在线免费观看| 亚洲Aⅴ在线无码播放毛片一线天| 亚洲第一区精品日韩在线播放| 亚洲大尺度无码无码专线一区| 亚洲精品成a人在线观看| 特黄aa级毛片免费视频播放| 色播在线永久免费视频| 一级a性色生活片久久无少妇一级婬片免费放| 亚洲综合久久夜AV | 最近中文字幕mv免费高清视频8| 亚洲色偷偷色噜噜狠狠99网| 亚洲欧洲精品成人久久奇米网| 免费观看久久精彩视频| 亚洲乱色伦图片区小说| 亚洲中久无码永久在线观看同| 亚洲av成人无码网站…| 亚洲欧洲日产国码av系列天堂 |