源碼閱讀計劃之timeago
前言
大概半個月之前我看到了這個小型的javascript庫,這個庫的功能就一個用來將datetime時間轉化成類似於*** 時間前的描述字元串,例如:「3小時前」。
但是卻非常火爆,因為作者圍繞這個核心功能開發一些很實用的特性,當初保存這個庫的時候就有個想法好好學習一下它的源碼。而且作者也是個中國人,中文文檔也是齊全的。測試也是寫了的。這就很方便學習了。
正文
先去timeago的github地址下載整個源文件,然後來看下測試文件。
根據功能來看 timeago分
1.設置相對日期n2.格式化時間戳,字元串n3.自動實時渲染n4.本地化和多語言支持n
來直接看源碼
// 插件普遍起手寫法,兼容import導入n/*n// 這麼寫會報錯,因為這是一個函數定義:nfunction() {}()nn// 常見的(多了一對括弧),調用匿名函數:n(function() {})()nn// 但在前面加上一個布爾運算符(只多了一個感嘆號),就是表達式了,將執行後面的代碼,也就合法實現調用,而 + - || 都有這樣的功能n!function() {}()nn*/n!function (root, factory) {n if (typeof module === object && module.exports)n module.exports = factory(root);n elsen root.timeago = factory(root);n}(typeof window !== undefined ? window : this,nfunction () {n var cnt = 0, // timer計數n // 英文 年月日時分秒轉成數組n indexMapEn = second_minute_hour_day_week_month_year.split(_),n // 中文年 月日時分秒轉成數組n indexMapZh = 秒_分鐘_小時_天_周_月_年.split(_),n // 地區化,默認是en和zh_CN兩種n locales = {n en: function(number, index) {n if (index === 0) return [just now, right now];n var unit = indexMapEn[parseInt(index / 2)];n if (number > 1) unit += s;n return [number + + unit + ago, in + number + + unit];n },n zh_CN: function(number, index) {n if (index === 0) return [剛剛, 片刻後];n var unit = indexMapZh[parseInt(index / 2)];n return [number + unit + 前, number + unit + 後];n }n },n // second, minute, hour, day, week, month, year(365 days)n SEC_ARRAY = [60, 60, 24, 7, 365/7/12, 12],n SEC_ARRAY_LEN = 6,n ATTR_DATETIME = datetime;nn // 格式化日期,將日期,字元串,時間戳轉為日期實例n function toDate(input) {n // 如果傳進來的是日期實例則直接返回n if (input instanceof Date) return input;n // 如果是時間戳n if (!isNaN(input)) return new Date(toInt(input));n if (/^d+$/.test(input)) return new Date(toInt(input, 10));n // 如果是字元串n input = (input || ).trim().replace(/.d+/, ) // remove millisecondsn .replace(/-/, /).replace(/-/, /)n .replace(/T/, ).replace(/Z/, UTC)n .replace(/([+-]dd):?(dd)/, $1$2); // -04:00 -> -0400n return new Date(input);n }n // 強制轉為數字整型n function toInt(f) {n return parseInt(f);n }n // 根據指定的本地化語言,格式化輸出diff為多少秒之前n function formatDiff(diff, locale, defaultLocale) {n // 如果沒指定則默認為en語言包n locale = locales[locale] ? locale : (locales[defaultLocale] ? defaultLocale : en);n // 判斷傳進來的是diff是當前時間之前還是之後n var i = 0,n agoin = diff < 0 ? 1 : 0; // 1之前 / 0之後n // 取絕對值n diff = Math.abs(diff);n // SEC_ARRAY = [60, 60, 24, 7, 365/7/12, 12] 秒_分鐘_小時_天_周_月_年n // diff 是 時間差,用秒來做單位 diff一次從秒開始除直到無法整除時便計算出最終間隔(小時/月/年)n for (; diff >= SEC_ARRAY[i] && i < SEC_ARRAY_LEN; i++) {n diff /= SEC_ARRAY[i];n }n diff = toInt(diff);n i *= 2;n // 用來定位語言包的合適位置n if (diff > (i === 0 ? 9 : 1)) i += 1;n return locales[locale](diff, i)[agoin].replace(%s, diff);n }n // 返回指定時間與當前時間的時間差,用秒作為單位n function diffSec(date, nowDate) {n nowDate = nowDate ? toDate(nowDate) : new Date();n return (nowDate - toDate(date)) / 1000;n }n // 計算下一個周期n function nextInterval(diff) {n var rst = 1, i = 0, d = Math.abs(diff);n for (; diff >= SEC_ARRAY[i] && i < SEC_ARRAY_LEN; i++) {n diff /= SEC_ARRAY[i];n rst *= SEC_ARRAY[i];n }n // return leftSec(d, rst);n d = d % rst;n d = d ? rst - d : rst;n return Math.ceil(d);n }n // 從給定的節點中獲取date屬性的值n function getDateAttr(node) {n if (node.getAttribute) return node.getAttribute(ATTR_DATETIME);n if(node.attr) return node.attr(ATTR_DATETIME);n }n // 渲染函數n function Timeago(nowDate, defaultLocale) {n var timers = {}; n if (! defaultLocale) defaultLocale = en; n // 渲染,加個cnt好分辨是哪個定時器,到時候清空好清空n function doRender(node, date, locale, cnt) {n var diff = diffSec(date, nowDate);n node.innerHTML = formatDiff(diff, locale, defaultLocale);n // 每秒渲染一次n timers[k + cnt] = setTimeout(function() {n doRender(node, date, locale, cnt);n }, nextInterval(diff) * 1000);n }n // 調用私有函數formatn this.format = function(date, locale) {n return formatDiff(diffSec(date, nowDate), locale, defaultLocale);n };n // 調用私有函數rendern this.render = function(nodes, locale) {n if (nodes.length === undefined) nodes = [nodes];n for (var i = 0; i < nodes.length; i++) {n doRender(nodes[i], getDateAttr(nodes[i]), locale, ++ cnt); // render itemn }n };n // 清除渲染n this.cancel = function() {n for (var key in timers) {n clearTimeout(timers[key]);n }n timers = {};n };n // 設置語言n this.setLocale = function(locale) {n defaultLocale = locale;n };n // 返回n return this;n }n n // 返回新對象n function timeagoFactory(nowDate, defaultLocale) {n return new Timeago(nowDate, defaultLocale);n }n // 重新指定語言類型n timeagoFactory.register = function(locale, localeFunc) {n locales[locale] = localeFunc;n };nn return timeagoFactory;n});n
結尾
看完這個源碼非常簡單而且很適合新手學習它的組織結構。而且還可以深入思考一下,有些插件其實並不需要做的很全,只要你把核心功能寫的明白瞭然,它也是獨一無二的。
推薦閱讀:
※怎麼用backbone+react架構前端?
※為什麼張鑫旭說「沒有比『學 CSS 看看 CSS 中文手冊就夠了』更愚蠢的言論了」?
※一篇文章讓你知道被 Google 攻破的 SHA-1 是什麼
※本人前端,剛入手了mac本,以前沒用過,請各位大大推薦一下mac本上做前端的編碼開發或者調試輔助工具?