整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          一些很實用的JavaScript函數封裝集合(ES6)

          項目地址: https://github.com/dragonir/Util.js
          

          til.js 是對常用函數的封裝,方便在實際項目中使用,主要內容包含:數組類、瀏覽器類、日期類、函數類、數學類、媒體類、節點類、對象類、字符串類、類型檢測類、正則表達式類等內容。

          數組

          • arrayMax

          返回數組中的最大值。

          將Math.max()與擴展運算符 (...) 結合使用以獲取數組中的最大值。

          const arrayMax = arr => Math.max(...arr);
          // arrayMax([10, 1, 5]) -> 10
          
          
          • arrayMin

          返回數組中的最小值。

          將Math.min()與擴展運算符 (...) 結合使用以獲取數組中的最小值。

          const arrayMin = arr => Math.min(...arr);
          // arrayMin([10, 1, 5]) -> 1
          
          • chunk

          將數組塊劃分為指定大小的較小數組。

          使用Array.from()創建新的數組, 這符合將生成的區塊數。使用Array.slice()將新數組的每個元素映射到size長度的區塊。如果原始數組不能均勻拆分, 則最終的塊將包含剩余的元素。

          const chunk = (arr, size) =>
          Array.from({length: Math.ceil(arr.length / size)}, (v, i) => arr.slice(i * size, i * size + size));
          // chunk([1,2,3,4,5], 2) -> [[1,2],[3,4],[5]]
          
          • compact

          從數組中移除 falsey 值。

          使用Array.filter()篩選出 falsey 值 (false、null、0、""、undefined和NaN).

          const compact = (arr) => arr.filter(Boolean);
          // compact([0, 1, false, 2, '', 3, 'a', 'e'*23, NaN, 's', 34]) -> [ 1, 2, 3, 'a', 's', 34 ]
          
          • countOccurrences

          計算數組中值的出現次數。

          使用Array.reduce()在每次遇到數組中的特定值時遞增計數器。

          const countOccurrences = (arr, value) => arr.reduce((a, v) => v === value ? a + 1 : a + 0, 0);
          // countOccurrences([1,1,2,1,2,3], 1) -> 3
          
          • deepFlatten

          深拼合數組。

          使用遞歸。使用Array.concat()與空數組 ([]) 和跨頁運算符 (...) 來拼合數組。遞歸拼合作為數組的每個元素。

          const deepFlatten = arr => [].concat(...arr.map(v => Array.isArray(v) ? deepFlatten(v) : v));
          // deepFlatten([1,[2],[[3],4],5]) -> [1,2,3,4,5]
          
          • difference

          返回兩個數組之間的差異。

          從b創建Set, 然后使用Array.filter() on 只保留a b中不包含的值.

          const difference = (a, b) => { const s = new Set(b); return a.filter(x => !s.has(x)); };
          // difference([1,2,3], [1,2,4]) -> [3]
          
          • distinctValuesOfArray

          返回數組的所有不同值。

          使用 ES6 Set和...rest運算符放棄所有重復的值。

          const distinctValuesOfArray = arr => [...new Set(arr)];
          // distinctValuesOfArray([1,2,2,3,4,4,5]) -> [1,2,3,4,5]
          
          • dropElements

          移除數組中的元素, 直到傳遞的函數返回true。返回數組中的其余元素。 在數組中循環, 使用Array.shift()將數組的第一個元素除去, 直到函數的返回值為true。返回其余元素。

          const dropElements = (arr, func) => {
          while (arr.length > 0 && !func(arr[0])) arr.shift();
          return arr;
          };
          // dropElements([1, 2, 3, 4], n => n >= 3) -> [3,4]
          
          • everyNth

          返回數組中的每個第 n 個元素。

          使用Array.filter()創建一個包含給定數組的每個第 n 個元素的新數組。

          const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === 0);
          // everyNth([1,2,3,4,5,6], 2) -> [ 1, 3, 5 ]
          
          • filterNonUnique

          篩選出數組中的非唯一值。

          對于只包含唯一值的數組, 請使用Array.filter()。

          const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i));
          // filterNonUnique([1,2,2,3,4,4,5]) -> [1,3,5]
          
          • flatten

          拼合數組。

          使用Array.reduce()獲取數組中的所有元素和concat()以拼合它們。

          const flatten = arr => arr.reduce((a, v) => a.concat(v), []);
          // flatten([1,[2],3,4]) -> [1,2,3,4]
          
          • flattenDepth

          將數組向上拼合到指定深度。

          使用遞歸, 遞減depth, 每層深度為1。使用Array.reduce()和Array.concat()來合并元素或數組。基本情況下, 對于等于1的depth停止遞歸。省略第二個元素,depth僅拼合到1的深度 (單個拼合)。

          const flattenDepth = (arr, depth = 1) =>
          depth != 1 ? arr.reduce((a, v) => a.concat(Array.isArray(v) ? flattenDepth(v, depth - 1) : v), [])
          : arr.reduce((a, v) => a.concat(v), []);
          // flattenDepth([1,[2],[[[3],4],5]], 2) -> [1,2,[3],4,5]
          
          • groupby

          根據給定函數對數組元素進行分組。

          使用Array.map()將數組的值映射到函數或屬性名。使用Array.reduce()創建一個對象, 其中的鍵是從映射的結果生成的。

          const groupBy = (arr, func) =>
          arr.map(typeof func === 'function' ? func : val => val[func])
          .reduce((acc, val, i) => { acc[val] = (acc[val] || []).concat(arr[i]); return acc; }, {});
          // groupBy([6.1, 4.2, 6.3], Math.floor) -> {4: [4.2], 6: [6.1, 6.3]}
          // groupBy(['one', 'two', 'three'], 'length') -> {3: ['one', 'two'], 5: ['three']}
          
          • head

          返回列表的頭。

          使用arr[0]可返回傳遞的數組的第一個元素。

          const head = arr => arr[0];
          // head([1,2,3]) -> 1
          
          • initial

          返回除最后一個數組之外的所有元素。

          使用 "arr.slice(0,-1)" 返回數組的最后一個元素。

          const initial = arr => arr.slice(0, -1);
          // initial([1,2,3]) -> [1,2]
          
          • initializeArrayWithRange

          初始化包含指定范圍內的數字的數組。

          使用Array(end-start)創建所需長度的數組Array.map()以填充區域中所需的值。可以省略start以使用默認值0.

          const initializeArrayWithRange = (end, start = 0) =>
          Array.from({ length: end - start }).map((v, i) => i + start);
          // initializeArrayWithRange(5) -> [0,1,2,3,4]
          
          • initializeArrayWithValues

          初始化并填充具有指定值的數組。

          使用Array(n)創建所需長度的數組,fill(v)以填充所需的值。可以省略value以使用默認值0.

          const initializeArrayWithValues = (n, value = 0) => Array(n).fill(value);
          // initializeArrayWithValues(5, 2) -> [2,2,2,2,2]
          
          • intersection

          返回兩個數組中存在的元素的列表。

          從b創建Set, 然后使用Array.filter()on a只保留b中包含的值.

          const intersection = (a, b) => { const s = new Set(b); return a.filter(x => s.has(x)); };
          // intersection([1,2,3], [4,3,2]) -> [2,3]
          
          • last

          返回數組中的最后一個元素。

          使用arr.length - 1可計算給定數組的最后一個元素的索引并返回它。

          const last = arr => arr[arr.length - 1];
          // last([1,2,3]) -> 3
          
          • mapObject

          使用函數將數組的值映射到對象, 其中鍵值對由原始值作為鍵和映射值組成。

          使用匿名內部函數范圍來聲明未定義的內存空間, 使用閉包來存儲返回值。使用新的Array可將該數組與函數的映射放在其數據集上, 而逗號運算符返回第二個步驟, 而不需要從一個上下文移動到另一個環境 (由于關閉和操作順序)。

          const mapObject = (arr, fn) => 
          (a => (a = [arr, arr.map(fn)], a[0].reduce( (acc,val,ind) => (acc[val] = a[1][ind], acc), {}) )) ( );
          /*
          const squareIt = arr => mapObject(arr, a => a*a)
          squareIt([1,2,3]) // { 1: 1, 2: 4, 3: 9 }
          */
          
          • nthElement

          返回數組的第 n 個元素。

          使用Array.slice()可獲取包含第 n 個元素的數組。如果索引超出界限, 則返回[]。省略第二個參數n, 以獲取數組的第一個元素。

          const nthElement = (arr, n=0) => (n>0? arr.slice(n,n+1) : arr.slice(n))[0];
          // nthElement(['a','b','c'],1) -> 'b'
          // nthElement(['a','b','b'],-3) -> 'a'
          
          • pick

          從對象中選取對應于給定鍵的鍵值對。

          使用Array.reduce()將篩選/選取的密鑰轉換回具有相應鍵值對的對象 (如果在 obj 中存在該鍵)。

          const pick = (obj, arr) =>
          arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {});
          // pick({ 'a': 1, 'b': '2', 'c': 3 }, ['a', 'c']) -> { 'a': 1, 'c': 3 }
          
          • pull

          對原始數組進行變異, 以篩選出指定的值。

          使用Array.filter()和Array.includes()來拉出不需要的值。使用Array.length = 0可將傳入的數組中的長度重置為零, 并將其設置為Array.push(), 以便僅使用所提取的值填充它。

          const pull = (arr, ...args) => {
          let pulled = arr.filter((v, i) => !args.includes(v));
          arr.length = 0; pulled.forEach(v => arr.push(v));
          };
          // let myArray = ['a', 'b', 'c', 'a', 'b', 'c'];
          // pull(myArray, 'a', 'c');
          // console.log(myArray) -> [ 'b', 'b' ]
          
          • remove

          從數組中移除給定函數返回false的元素. 使用Array.filter()查找返回 truthy 值的數組元素和Array.reduce()以使用Array.splice()刪除元素。使用三參數 (func value, index, array調用函數).

          const remove = (arr, func) =>
          Array.isArray(arr) ? arr.filter(func).reduce((acc, val) => {
          arr.splice(arr.indexOf(val), 1); return acc.concat(val);
          }, [])
          : [];
          // remove([1, 2, 3, 4], n => n % 2 == 0) -> [2, 4]
          
          • sample

          返回數組中的隨機元素。

          使用Math.random()生成一個隨機數, 將它與length相乘, 并使用數學將其舍入到最接近的整數Math.floor()。此方法也適用于字符串。

          const sample = arr => arr[Math.floor(Math.random() * arr.length)];
          // sample([3, 7, 9, 11]) -> 9
          
          • shuffle

          隨機數組值的順序。

          使用Array.sort()可在比較器中使用Math.random()重新排序元素。

          const shuffle = arr => arr.sort(() => Math.random() - 0.5);
          // shuffle([1,2,3]) -> [2,3,1]
          
          • similarity

          返回兩個數組中都顯示的元素的數組。

          使用filter()可刪除不屬于values的值, 使用includes()確定.

          const similarity = (arr, values) => arr.filter(v => values.includes(v));
          // similarity([1,2,3], [1,2,4]) -> [1,2]
          
          • symmetricDifference

          返回兩個數組之間的對稱差。

          從每個數組創建一個Set, 然后對它們中的每一個都使用Array.filter(), 以便只保留其他值中不包含的數值。

          const symmetricDifference = (a, b) => {
          const sA = new Set(a), sB = new Set(b);
          return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))];
          }
          // symmetricDifference([1,2,3], [1,2,4]) -> [3,4]
          
          • tail

          返回數組中的所有元素, 除第一個。

          如果數組的length大于1, 則返回arr.slice(1), 否則返回整個數組。

          const tail = arr => arr.length > 1 ? arr.slice(1) : arr;
          // tail([1,2,3]) -> [2,3]
          // tail([1]) -> [1]
          
          • take

          返回一個數組, 其中 n 個元素從開始處移除。

          使用Array.slice()創建數組的切片, 其中包含從開始處取出的n元素。

          const take = (arr, n = 1) => arr.slice(0, n);
          // take([1, 2, 3], 5) -> [1, 2, 3]
          // take([1, 2, 3], 0) -> []
          
          • takeRight

          返回一個數組, 其中 n 個元素從末尾移除。

          使用Array.slice()創建數組的切片, 其中包含從末尾取出的n元素。

          const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length);
          // takeRight([1, 2, 3], 2) -> [ 2, 3 ]
          // takeRight([1, 2, 3]) -> [3]
          
          • union

          返回在兩個數組中的任意一個中存在的每個元素。

          創建一個Set, 其中包含a和b的所有值, 并將其轉換為數組。

          const union = (a, b) => Array.from(new Set([...a, ...b]));
          // union([1,2,3], [4,3,2]) -> [1,2,3,4]
          
          • without

          篩選出數組中具有指定值之一的元素。

          使用Array.filter()創建不包括的數組 (使用!Array.includes()) 所有給定值。

          const without = (arr, ...args) => arr.filter(v => !args.includes(v));
          // without([2, 1, 2, 3], 1, 2) -> [3]
          
          • zip

          創建基于原始數組中的位置分組的元素數組。

          使用Math.max.apply()獲取參數中最長的數組。創建一個以該長度為返回值的數組, 并使用 map 函數創建一個分組元素的數組Array.from()如果參數數組的長度不同, 則在未找到任何值的情況下使用undefined。

          const zip = (...arrays) => {
          const maxLength = Math.max(...arrays.map(x => x.length));
          return Array.from({length: maxLength}).map((_, i) => {
          return Array.from({length: arrays.length}, (_, k) => arrays[k][i]);
          })
          }
          //zip(['a', 'b'], [1, 2], [true, false]); -> [['a', 1, true], ['b', 2, false]]
          //zip(['a'], [1, 2], [true, false]); -> [['a', 1, true], [undefined, 2, false]]
          

          瀏覽器

          • bottomVisible

          如果頁的底部可見, 則返回true, 否則為false。

          使用scrollY、scrollHeight和clientHeight來確定頁面底部是否可見。

          const bottomVisible = () =>
          document.documentElement.clientHeight + window.scrollY >= document.documentElement.scrollHeight || document.documentElement.clientHeight;
          // bottomVisible() -> true
          
          • currentURL

          返回當前 URL。

          使用window.location.href獲取當前 URL。

          const currentURL = () => window.location.href;
          // currentUrl() -> 'https://google.com'
          
          • elementIsVisibleInViewport

          如果指定的元素在視區中可見, 則返回true, 否則為false。

          使用Element.getBoundingClientRect()和window.inner(Width|Height)值以確定給定元素在視區中是否可見。省略第二個參數以確定該元素是否完全可見, 或指定true以確定它是否部分可見。

          const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
          const { top, left, bottom, right } = el.getBoundingClientRect();
          return partiallyVisible
          ? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&
          ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
          : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
          };
          // e.g. 100x100 viewport and a 10x10px element at position {top: -1, left: 0, bottom: 9, right: 10}
          // elementIsVisibleInViewport(el) -> false (not fully visible)
          // elementIsVisibleInViewport(el, true) -> true (partially visible)
          
          • getScrollPosition

          返回當前頁的滾動位置。

          如果已定義, 則使用pageXOffset和pageYOffset, 否則scrollLeft和scrollTop。可以省略el以使用window的默認值.

          const getScrollPosition = (el = window) =>
          ({x: (el.pageXOffset !== undefined) ? el.pageXOffset : el.scrollLeft,
          y: (el.pageYOffset !== undefined) ? el.pageYOffset : el.scrollTop});
          // getScrollPosition() -> {x: 0, y: 200}
          
          • getURLParameters

          返回一個包含當前 URL 參數的對象。

          使用match()與適當的正則表達式來獲取所有鍵值對,Array.reduce()可將它們映射并合并到單個對象中。將location.search作為要應用于當前url的參數傳遞.

          const getURLParameters = url =>
          url.match(/([^?=&]+)(=([^&]*))/g).reduce(
          (a, v) => (a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1), a), {}
          );
          // getURLParameters('http://url.com/page?name=Adam&surname=Smith') -> {name: 'Adam', surname: 'Smith'}
          
          • redirect

          重定向到指定的 URL。

          使用window.location.href或window.location.replace()重定向到url。傳遞第二個參數以模擬鏈接單擊 (true-默認值) 或 HTTP 重定向 (false).

          const redirect = (url, asLink = true) =>
          asLink ? window.location.href = url : window.location.replace(url);
          // redirect('https://google.com')
          
          • scrollToTop

          平滑滾動到頁面頂部。

          使用document.documentElement.scrollTop或document.body.scrollTop從頂部獲取距離。從頂部的距離的一小部分滾動。使用window.requestAnimationFrame()對滾動進行動畫處理。

          const scrollToTop = () => {
          const c = document.documentElement.scrollTop || document.body.scrollTop;
          if (c > 0) {
          window.requestAnimationFrame(scrollToTop);
          window.scrollTo(0, c - c / 8);
          }
          };
          // scrollToTop()
          

          日期

          • getDaysDiffBetweenDates

          返回兩個日期之間的差異 (以天為值)。

          計算Date對象之間的差異 (以天為)。

          const getDaysDiffBetweenDates = (dateInitial, dateFinal) => (dateFinal - dateInitial) / (1000 * 3600 * 24);
          // getDaysDiffBetweenDates(new Date("2017-12-13"), new Date("2017-12-22")) -> 9
          
          • JSONToDate

          將 JSON 對象轉換為日期。

          使用Date(), 將 JSON 格式的日期轉換為可讀格式 (dd/mm/yyyy日)).

          const JSONToDate = arr => {
          const dt = new Date(parseInt(arr.toString().substr(6)));
          return `${ dt.getDate() }/${ dt.getMonth() + 1 }/${ dt.getFullYear() }`
          };
          // JSONToDate(/Date(1489525200000)/) -> "14/3/2017"
          
          • toEnglishDate

          將日期從美國格式轉換為英文格式。

          使用Date.toISOString()、split('T')和replace()將日期從美式格式轉換為英文格式。如果傳遞的時間不能轉換為日期, 則拋出錯誤。

          const toEnglishDate = (time) =>
          {try{return new Date(time).toISOString().split('T')[0].replace(/-/g, '/')}catch(e){return}};
          // toEnglishDate('09/21/2010') -> '21/09/2010'
          

          函數類

          • chainAsync

          鏈異步函數。

          循環遍歷包含異步事件的函數數組, 當每個異步事件完成時調用next。

          const chainAsync = fns => { let curr = 0; const next = () => fns[curr++](next); next(); };
          /*
          chainAsync([
           next => { console.log('0 seconds'); setTimeout(next, 1000); },
           next => { console.log('1 second'); setTimeout(next, 1000); },
           next => { console.log('2 seconds'); }
          ])
          */
          
          • compose

          執行從右向左的函數組合。

          使用Array.reduce()執行從右向左的函數組合。最后一個 (最右邊) 的函數可以接受一個或多個參數;其余的函數必須是一元的。

          const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
          /*
          const add5 = x => x + 5
          const multiply = (x, y) => x * y
          const multiplyAndAdd5 = compose(add5, multiply)
          multiplyAndAdd5(5, 2) -> 15
          */
          
          • curry

          Curries a function.

          使用遞歸。如果提供的參數 (變量) 的數量足夠, 請調用傳遞的函數args f。否則, 返回需要其余參數的擴充函數f。如果你想咖喱一個函數, 接受可變數目的參數 (如Math.min()), 可以選擇將參數的個數傳遞到第二個參數arity(可變函數).

          const curry = (fn, arity = fn.length, ...args) =>
          arity <= args.length
          ? fn(...args)
          : curry.bind(null, fn, arity, ...args);
          // curry(Math.pow)(2)(10) -> 1024
          // curry(Math.min, 3)(10)(50)(2) -> 2
          
          • functionName

          記錄函數的名稱。

          使用console.debug()和傳遞的方法的name屬性將方法的名稱記錄到控制臺的debug通道中。

          const functionName = fn => (console.debug(fn.name), fn);
          // functionName(Math.max) -> max (logged in debug channel of console)
          
          • pipe

          執行從左向右的函數組合。

          使用Array.reduce()與擴展運算符 (...) 執行從左向右的函數組合。第一個 (最左邊的) 函數可以接受一個或多個參數;其余的函數必須是一元的。

          const pipeFunctions = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));
          /*
          const add5 = x => x + 5
          const multiply = (x, y) => x * y
          const multiplyAndAdd5 = pipeFunctions(multiply, add5)
          multiplyAndAdd5(5, 2) -> 15
          */
          
          • promisify

          轉換異步函數以返回一個承諾。

          使用討好返回一個返回調用原始函數的Promise的函數。使用...rest運算符傳入所有參數。 在節點 8 + 中, 可以使用 util.promisify

          const promisify = func =>
          (...args) =>
          new Promise((resolve, reject) =>
          func(...args, (err, result) =>
          err ? reject(err) : resolve(result))
          );
          // const delay = promisify((d, cb) => setTimeout(cb, d))
          // delay(2000).then(() => console.log('Hi!')) -> Promise resolves after 2s
          
          • runPromisesInSeries

          運行一系列的承諾系列。

          使用Array.reduce()創建一個承諾鏈, 每個承諾在解決時返回下一個承諾。

          const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve());
          // const delay = (d) => new Promise(r => setTimeout(r, d))
          // runPromisesInSeries([() => delay(1000), () => delay(2000)]) -> executes each promise sequentially, taking a total of 3 seconds to complete
          
          • sleep

          延遲異步函數的執行。

          延遲執行async函數的一部分, 將其放入休眠狀態, 返回Promise.

          const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
          /*
          async function sleepyWork() {
           console.log('I\'m going to sleep for 1 second.');
           await sleep(1000);
           console.log('I woke up after 1 second.');
          }
          */
          

          數學

          • arrayAverage

          返回數字數組的平均值。

          使用Array.reduce()將每個值添加到累加器中, 并以0的值初始化, 除以數組的length。

          const arrayAverage = arr => arr.reduce((acc, val) => acc + val, 0) / arr.length;
          // arrayAverage([1,2,3]) -> 2
          
          • arraySum

          返回一個數字數組的總和。

          使用Array.reduce()將每個值添加到累加器中, 并以0值初始化.

          const arraySum = arr => arr.reduce((acc, val) => acc + val, 0);
          // arraySum([1,2,3,4]) -> 10
          
          • collatz

          應用 Collatz 算法。

          如果n是偶數, 則返回n/2。否則返回3n+1.

          const collatz = n => (n % 2 == 0) ? (n / 2) : (3 * n + 1);
          // collatz(8) --> 4
          // collatz(5) --> 16
          
          • collatz

          將數字轉換為數字數組。

          將數字轉換為字符串, 在 ES6 ([...string]) 中使用擴展運算符生成數組。使用Array.map()和parseInt()將每個值轉換為整數。

          const digitize = n => [...''+n].map(i => parseInt(i));
          // digitize(2334) -> [2, 3, 3, 4]
          
          • digitize

          返回兩點之間的距離。

          使用Math.hypot()計算兩個點之間的歐氏距離。

          const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0);
          // distance(1,1, 2,3) -> 2.23606797749979
          
          • distance

          計算數字的階乘。

          使用遞歸。如果n小于或等于1, 則返回1。否則, 返回n的乘積和n - 1的階乘。如果n為負數, 則引發異常。

          const factorial = n =>
          n < 0 ? (() => { throw new TypeError('Negative numbers are not allowed!') })()
          : n <= 1 ? 1 : n * factorial(n - 1);
          // factorial(6) -> 720
          
          • fibonacci

          生成一個數組, 包含斐波那契數列, 直到第 n 個項。

          創建一個指定長度的空數組, 初始化前兩個值 (0和1)。使用Array.reduce()可將值添加到數組中, 方法是使用前兩個值的總和, 但前兩個數值除外。

          const fibonacci = n =>
          Array(n).fill(0).reduce((acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), []);
          // fibonacci(5) -> [0,1,1,2,3]
          
          • gcd

          計算兩個數字之間最大的公共除數。

          使用遞歸。基本情況是當y等于0時。在這種情況下, 返回x。否則, 返回y的 GCD 和除法的其余部分x/y.

          const gcd = (x, y) => !y ? x : gcd(y, x % y);
          // gcd (8, 36) -> 4
          
          • hammingDistance

          計算兩個值之間的漢明距離。

          使用 XOR 運算符 (^) 可查找兩個數字之間的位差, 使用toString(2)轉換為二進制字符串。使用match(/1/g)計算并返回字符串中1的數目。.

          const hammingDistance = (num1, num2) =>
          ((num1 ^ num2).toString(2).match(/1/g) || '').length;
          // hammingDistance(2,3) -> 1
          
          • isDivisible

          檢查第一個數值參數是否可被另一個數字變量整除。

          使用模數運算符 (%) 檢查余數是否等于0.

          const isDivisible = (dividend, divisor) => dividend % divisor === 0;
          // isDivisible(6,3) -> true
          
          • iseven

          如果給定的數字為偶數, 則返回true, 否則為false。

          檢查一個數字是奇數還是使用模數 (%) 運算符。如果數字為偶數, 則返回true, 如果數字為奇數, 則為false。

          const isEven = num => num % 2 === 0;
          // isEven(3) -> false
          
          • lcm

          返回兩個數字中最不常見的倍數。

          使用最大的公共除數 (GCD) 公式和Math.abs()來確定最不常見的倍數。GCD 公式使用遞歸。

          const lcm = (x,y) => {
          const gcd = (x, y) => !y ? x : gcd(y, x % y);
          return Math.abs(x*y)/(gcd(x,y));
          };
          // lcm(12,7) -> 84
          
          • median

          返回數字數組的中間值。

          找到數組的中間, 使用Array.sort()來對值進行排序。如果length為奇數, 則返回中點的數字, 否則為兩個中間數的平均值。

          const median = arr => {
          const mid = Math.floor(arr.length / 2), nums = arr.sort((a, b) => a - b);
          return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2;
          };
          // median([5,6,50,1,-5]) -> 5
          // median([0,10,-2,7]) -> 3.5
          
          • palindrome

          如果給定字符串為回文, 則返回true, 否則為false。

          轉換字符串toLowerCase()并使用replace()從其中刪除非字母數字字符。然后,split('')到各個字符,reverse(),join(''), 并將其與原始的、不可逆轉的字符串進行比較, 然后將其轉換為tolowerCase().

          const palindrome = str => {
          const s = str.toLowerCase().replace(/[\W_]/g,'');
          return s === s.split('').reverse().join('');
          }
          // palindrome('taco cat') -> true
          
          • percentile

          使用百分比公式計算給定數組中有多少個數小于或等于給定值。

          使用Array.reduce()計算值的下面有多少, 有多少個數是相同的值, 并應用百分比公式。

          const percentile = (arr, val) =>
          100 * arr.reduce((acc,v) => acc + (v < val ? 1 : 0) + (v === val ? 0.5 : 0), 0) / arr.length;
          // percentile([1,2,3,4,5,6,7,8,9,10], 6) -> 55
          
          • powerset

          返回給定數組的真子集。

          使用Array.reduce()與Array.map()組合, 以循環訪問元素并將其合并到包含所有組合的數組中。

          const powerset = arr =>
          arr.reduce((a, v) => a.concat(a.map(r => [v].concat(r))), [[]]);
          // powerset([1,2]) -> [[], [1], [2], [2,1]]
          
          • randomIntegerInRange

          返回指定范圍內的隨機整數。

          使用Math.random()生成一個隨機數并將其映射到所需的范圍, 使用Math.floor()使其成為整數。

          const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
          // randomIntegerInRange(0, 5) -> 2
          
          • randomNumberInRange

          返回指定范圍內的隨機數。

          使用Math.random()生成隨機值, 并使用乘法將其映射到所需的范圍。

          const randomNumberInRange = (min, max) => Math.random() * (max - min) + min;
          // randomNumberInRange(2,10) -> 6.0211363285087005
          
          • round

          將數字四舍五入到指定的位數。

          使用Math.round()和模板文本將數字舍入到指定的位數。省略第二個參數,decimals舍入為整數。

          const round = (n, decimals=0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`);
          // round(1.005, 2) -> 1.01
          
          • standardDeviation

          返回數字數組的標準偏差。

          使用Array.reduce()計算值的平均值、方差和方差的總和, 值的方差, 然后確定標準偏差。可以省略第二個參數以獲取樣本標準偏差, 或將其設置為true以獲取總體標準偏差。

          const standardDeviation = (arr, usePopulation = false) => {
          const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length;
          return Math.sqrt(
          arr.reduce((acc, val) => acc.concat(Math.pow(val - mean, 2)), [])
          .reduce((acc, val) => acc + val, 0) / (arr.length - (usePopulation ? 0 : 1))
          );
          };
          // standardDeviation([10,2,38,23,38,23,21]) -> 13.284434142114991 (sample)
          // standardDeviation([10,2,38,23,38,23,21], true) -> 12.29899614287479 (population)
          

          媒體

          • speechSynthesis

          執行語音合成 (實驗)。

          使用SpeechSynthesisUtterance.voice和window.speechSynthesis.getVoices()將郵件轉換為語音。使用window.speechSynthesis.speak()播放該消息。了解有關Web 語音 API 的 SpeechSynthesisUtterance 接口的詳細信息.

          const speechSynthesis = message => {
          const msg = new SpeechSynthesisUtterance(message);
          msg.voice = window.speechSynthesis.getVoices()[0];
          window.speechSynthesis.speak(msg);
          };
          // speechSynthesis('Hello, World') -> plays the message
          

          節點

          • JSONToFile

          將 JSON 對象寫入文件。

          使用fs.writeFile()、模板文本和JSON.stringify()將json對象寫入.json文件。

          const fs = require('fs');
          const JSONToFile = (obj, filename) => fs.writeFile(`${filename}.json`, JSON.stringify(obj, null, 2))
          // JSONToFile({test: "is passed"}, 'testJsonFile') -> writes the object to 'testJsonFile.json'
          
          • readFileLines

          返回指定文件中的行的數組。

          在fs節點包中使用readFileSync函數可以從文件創建Buffer。使用toString(encoding)函數將緩沖區轉換為字符串。通過spliting 文件內容行從文件內容創建數組 (每個\n).

          const fs = require('fs');
          const readFileLines = filename => fs.readFileSync(filename).toString('UTF8').split('\n');
          /*
          contents of test.txt :
           line1
           line2
           line3
           ___________________________
          let arr = readFileLines('test.txt')
          console.log(arr) // -> ['line1', 'line2', 'line3']
          */
          

          對象

          • cleanObj

          移除從 JSON 對象指定的屬性之外的任何特性。

          使用Object.keys()方法可以遍歷給定的 json 對象并刪除在給定數組中不是included 的鍵。另外, 如果給它一個特殊的鍵 (childIndicator), 它將在里面深入搜索, 并將函數應用于內部對象。

          const cleanObj = (obj, keysToKeep = [], childIndicator) => {
          Object.keys(obj).forEach(key => {
          if (key === childIndicator) {
          cleanObj(obj[key], keysToKeep, childIndicator);
          } else if (!keysToKeep.includes(key)) {
          delete obj[key];
          }
          })
          }
          /*
           const testObj = {a: 1, b: 2, children: {a: 1, b: 2}}
           cleanObj(testObj, ["a"],"children")
           console.log(testObj)// { a: 1, children : { a: 1}}
          */
          
          • objectFromPairs

          從給定的鍵值對創建對象。

          使用Array.reduce()創建和組合鍵值對。

          const objectFromPairs = arr => arr.reduce((a, v) => (a[v[0]] = v[1], a), {});
          // objectFromPairs([['a',1],['b',2]]) -> {a: 1, b: 2}
          
          • objectToPairs

          從對象創建鍵值對數組的數組。

          使用Object.keys()和Array.map()循環訪問對象的鍵并生成具有鍵值對的數組。

          const objectToPairs = obj => Object.keys(obj).map(k => [k, obj[k]]);
          // objectToPairs({a: 1, b: 2}) -> [['a',1],['b',2]])
          
          • shallowClone

          創建對象的淺表克隆。

          使用Object.assign()和一個空對象 ({}) 創建原始的淺克隆。

          const shallowClone = obj => Object.assign({}, obj);
          /*
          const a = { x: true, y: 1 };
          const b = shallowClone(a);
          a === b -> false
          */
          
          • truthCheckCollection

          檢查謂詞 (第二個參數) 是否 truthy 集合的所有元素 (第一個參數)。

          使用Array.every()檢查每個傳遞的對象是否具有指定的屬性, 以及是否返回 truthy 值。

          truthCheckCollection = (collection, pre) => (collection.every(obj => obj[pre]));
          // truthCheckCollection([{"user": "Tinky-Winky", "sex": "male"}, {"user": "Dipsy", "sex": "male"}], "sex") -> true
          

          字符串

          • anagrams

          生成字符串的所有字謎 (包含重復項)。

          使用遞歸。對于給定字符串中的每個字母, 為其其余字母創建所有部分字謎。使用Array.map()將字母與每個部分變位詞組合在一起, 然后將Array.reduce()組合在一個數組中的所有字謎。基本情況為字符串length等于2或1.

          const anagrams = str => {
          if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str];
          return str.split('').reduce((acc, letter, i) =>
          acc.concat(anagrams(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)), []);
          };
          // anagrams('abc') -> ['abc','acb','bac','bca','cab','cba']
          
          • Capitalize

          將字符串的第一個字母大寫。

          使用 destructuring 和toUpperCase()可將第一個字母、...rest用于獲取第一個字母之后的字符數組, 然后是Array.join('')以使其成為字符串。省略lowerRest參數以保持字符串的其余部分不變, 或將其設置為true以轉換為小寫。

          const capitalize = ([first,...rest], lowerRest = false) =>
          first.toUpperCase() + (lowerRest ? rest.join('').toLowerCase() : rest.join(''));
          // capitalize('myName') -> 'MyName'
          // capitalize('myName', true) -> 'Myname'
          
          • capitalizeEveryWord

          將字符串中每個單詞的首字母大寫。

          使用replace()匹配每個單詞和toUpperCase()的第一個字符以將其大寫。

          const capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase());
          // capitalizeEveryWord('hello world!') -> 'Hello World!'
          
          • escapeRegExp

          轉義要在正則表達式中使用的字符串。

          使用replace()可轉義特殊字符。

          const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
          // escapeRegExp('(test)') -> \\(test\\)
          
          • fromCamelCase

          從匹配轉換字符串。

          使用replace()可刪除下劃線、連字符和空格, 并將單詞轉換為匹配。省略第二個參數以使用默認分隔符_.

          const fromCamelCase = (str, separator = '_') =>
          str.replace(/([a-z\d])([A-Z])/g, '$1' + separator + '$2')
          .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + separator + '$2').toLowerCase();
          // fromCamelCase('someDatabaseFieldName', ' ') -> 'some database field name'
          // fromCamelCase('someLabelThatNeedsToBeCamelized', '-') -> 'some-label-that-needs-to-be-camelized'
          // fromCamelCase('someJavascriptProperty', '_') -> 'some_javascript_property'
          
          • reverseString

          反轉字符串。

          使用數組 destructuring 和Array.reverse()可反轉字符串中字符的順序。使用join('')組合字符以獲取字符串.

          const reverseString = str => [...str].reverse().join('');
          // reverseString('foobar') -> 'raboof'
          
          • sortCharactersInString

          按字母順序對字符串中的字符進行排序。

          使用split('')、Array.sort()利用localeCompare()重新組合使用join('').

          const sortCharactersInString = str =>
          str.split('').sort((a, b) => a.localeCompare(b)).join('');
          // sortCharactersInString('cabbage') -> 'aabbceg'
          
          • toCamelCase

          將字符串轉換為匹配。

          使用replace()可刪除下劃線、連字符和空格, 并將單詞轉換為匹配。

          const toCamelCase = str =>
          str.replace(/^([A-Z])|[\s-_]+(\w)/g, (match, p1, p2, offset) => p2 ? p2.toUpperCase() : p1.toLowerCase());
          // toCamelCase("some_database_field_name") -> 'someDatabaseFieldName'
          // toCamelCase("Some label that needs to be camelized") -> 'someLabelThatNeedsToBeCamelized'
          // toCamelCase("some-javascript-property") -> 'someJavascriptProperty'
          // toCamelCase("some-mixed_string with spaces_underscores-and-hyphens") -> 'someMixedStringWithSpacesUnderscoresAndHyphens'
          
          • truncateString

          將字符串截斷為指定長度。

          確定字符串的length是否大于num。將截斷的字符串返回到所需的長度, 并將...追加到末尾或原始字符串。

          const truncateString = (str, num) =>
          str.length > num ? str.slice(0, num > 3 ? num - 3 : num) + '...' : str;
          // truncateString('boomerang', 7) -> 'boom...'
          

          實用

          • coalesce

          返回第一個非空/未定義參數。

          使用Array.find()返回第一個非null/undefined的參數。

          const coalesce = (...args) => args.find(_ => ![undefined, null].includes(_))
          // coalesce(null,undefined,"",NaN, "Waldo") -> ""
          
          • coalesceFactory

          返回自定義的聯合函數, 返回從提供的參數驗證函數返回true的第一個參數。

          使用Array.find()返回從提供的參數驗證函數返回true的第一個參數。

          const coalesceFactory = valid => (...args) => args.find(valid);
          // const customCoalesce = coalesceFactory(_ => ![null, undefined, "", NaN].includes(_))
          // customCoalesce(undefined, null, NaN, "", "Waldo") //-> "Waldo"
          
          • extendHex

          將3位色碼擴展為6位色碼。

          使用Array.map()、split()和Array.join()來加入映射數組, 將3位的 RGB notated 十六進制 color-code 轉換為6位數字形式。Array.slice()用于從字符串啟動中刪除#, 因為它添加了一次。

          const extendHex = shortHex =>
          '#' + shortHex.slice(shortHex.startsWith('#') ? 1 : 0).split('').map(x => x+x).join('')
          // extendHex('#03f') -> '#0033ff'
          // extendHex('05a') -> '#0055aa'
          
          • gettype

          返回值的本機類型。

          如果值未定義或為 null, 則返回小寫的構造函數名稱、"未定義" 或 "null"

          const getType = v =>
          v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase();
          // getType(new Set([1,2,3])) -> "set"
          
          • hexToRGB

          將 colorcode 轉換為rgb()字符串。

          使用按位右運算符和掩碼位與&(and) 運算符將十六進制顏色代碼 (前綴為#) 轉換為具有 RGB 值的字符串。如果它是一個3位數的 colorcode, 那么用 extendHex () 函數 (ref.extendHex代碼段) 擴展的6位 colorcode 進行相同的處理

          const hexToRgb = hex => {
          const extendHex = shortHex =>
          '#' + shortHex.slice(shortHex.startsWith('#') ? 1 : 0).split('').map(x => x+x).join('');
          const extendedHex = hex.slice(hex.startsWith('#') ? 1 : 0).length === 3 ? extendHex(hex) : hex;
          return `rgb(${parseInt(extendedHex.slice(1), 16) >> 16}, ${(parseInt(extendedHex.slice(1), 16) & 0x00ff00) >> 8}, ${parseInt(extendedHex.slice(1), 16) & 0x0000ff})`;
          }
          // hexToRgb('#27ae60') -> 'rgb(39, 174, 96)'
          // hexToRgb('#acd') -> 'rgb(170, 204, 221)'
          
          • isArray

          檢查給定參數是否為數組。

          使用Array.isArray()檢查某個值是否屬于數組。

          const isArray = val => !!val && Array.isArray(val);
          // isArray(null) -> false
          // isArray([1]) -> true
          
          • isBoolean

          檢查給定的參數是否為本機布爾元素。

          使用typeof檢查某個值是否被歸類為布爾基元。

          const isBoolean = val => typeof val === 'boolean';
          // isBoolean(null) -> false
          // isBoolean(false) -> true
          
          • isFunction

          檢查給定參數是否為函數。

          使用typeof檢查某個值是否被歸類為函數基元。

          const isFunction = val => val && typeof val === 'function';
          // isFunction('x') -> false
          // isFunction(x => x) -> true
          
          • isNumber

          檢查給定參數是否為數字。

          使用typeof檢查某個值是否歸類為數字基元。

          const isNumber = val => typeof val === 'number';
          // isNumber('1') -> false
          // isNumber(1) -> true
          
          • isString

          檢查給定參數是否為字符串。

          使用typeof檢查某個值是否屬于字符串基元。

          const isString = val => typeof val === 'string';
          // isString(10) -> false
          // isString('10') -> true
          
          • isSymbol

          檢查給定參數是否為符號。

          使用typeof檢查某個值是否被歸類為符號基元。

          const isSymbol = val => typeof val === 'symbol';
          // isSymbol('x') -> false
          // isSymbol(Symbol('x')) -> true
          
          • RGBToHex

          將 RGB 組件的值轉換為 colorcode。

          使用按位左移位運算符 (<<) 和toString(16)將給定的 RGB 參數轉換為十六進制字符串, 然后padStart(6,'0')以獲取6位十六進制值。

          const RGBToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0');
          // RGBToHex(255, 165, 1) -> 'ffa501'
          
          • timeTaken

          測量執行函數所用的時間。

          使用console.time()和console.timeEnd()來測量開始和結束時間之間的差異, 以確定回調執行所用的時間。

          const timeTaken = callback => {
          console.time('timeTaken'); const r = callback();
          console.timeEnd('timeTaken'); return r;
          };
          // timeTaken(() => Math.pow(2, 10)) -> 1024
          // (logged): timeTaken: 0.02099609375ms
          
          • toOrdinalSuffix

          將序號后綴添加到數字。

          使用模數運算符 (%) 查找單個和十位數字的值。查找匹配的序號模式數字。如果在青少年模式中發現數字, 請使用青少年序號。

          const toOrdinalSuffix = num => {
          const int = parseInt(num), digits = [(int % 10), (int % 100)],
          ordinals = ['st', 'nd', 'rd', 'th'], oPattern = [1, 2, 3, 4],
          tPattern = [11, 12, 13, 14, 15, 16, 17, 18, 19];
          return oPattern.includes(digits[0]) && !tPattern.includes(digits[1]) ? int + ordinals[digits[0] - 1] : int + ordinals[3];
          };
          // toOrdinalSuffix("123") -> "123rd"
          
          • UUIDGenerator

          生成 UUID。

          使用cryptoAPI 生成 UUID, 符合RFC4122版本4。

          const UUIDGenerator = () =>
          ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
          (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
          );
          // UUIDGenerator() -> '7982fcfe-5721-4632-bede-6000885be57d'
          
          • validateEmail

          如果給定的字符串是有效的電子郵件, 則返回true, 否則為false。

          使用正則表達式檢查電子郵件是否有效。如果電子郵件有效, 則返回 true, 如果沒有, 則返回false。

          const validateEmail = str =>
           /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(str);
          // validateEmail(mymail@gmail.com) -> true
          
          • validateNumber

          如果給定值為數字, 則返回true, 否則為false。

          將!isNaN與parseFloat()結合使用, 以檢查參數是否為數字。使用isFinite()檢查數字是否是有限的。使用Number()檢查強制是否保持。



          在前面

          今年國慶假期終于可以憋在家里了不用出門了,不用出去看后腦了,真的是一種享受。這么好的光陰怎么浪費,睡覺、吃飯、打豆豆這怎么可能(耍多了也煩),完全不符合我們程序員的作風,趕緊起來把文章寫完。

          這篇文章比較基礎,在國慶期間的業余時間寫的,這幾天又完善了下,力求把更多的前端所涉及到的關于文件上傳的各種場景和應用都涵蓋了,若有疏漏和問題還請留言斧正和補充。

          自測讀不讀

          以下是本文所涉及到的知識點,break or continue ?

          • 文件上傳原理
          • 最原始的文件上傳
          • 使用 koa2 作為服務端寫一個文件上傳接口
          • 單文件上傳和上傳進度
          • 多文件上傳和上傳進度
          • 拖拽上傳
          • 剪貼板上傳
          • 大文件上傳之分片上傳
          • 大文件上傳之斷點續傳
          • node 端文件上傳

          原理概述

          原理很簡單,就是根據 http 協議的規范和定義,完成請求消息體的封裝和消息體的解析,然后將二進制內容保存到文件。

          我們都知道如果要上傳一個文件,需要把 form 標簽的enctype設置為multipart/form-data,同時method必須為post方法。

          那么multipart/form-data表示什么呢?

          multipart互聯網上的混合資源,就是資源由多種元素組成,form-data表示可以使用HTML Forms 和 POST 方法上傳文件,具體的定義可以參考RFC 7578。

          multipart/form-data 結構

          看下 http 請求的消息體



          • 請求頭:

          Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDCntfiXcSkPhS4PN 表示本次請求要上傳文件,其中boundary表示分隔符,如果要上傳多個表單項,就要使用boundary分割,每個表單項由———XXX開始,以———XXX結尾。

          • 消息體- Form Data 部分

          每一個表單項又由Content-Type和Content-Disposition組成。

          Content-Disposition: form-data 為固定值,表示一個表單元素,name 表示表單元素的 名稱,回車換行后面就是name的值,如果是上傳文件就是文件的二進制內容。

          Content-Type:表示當前的內容的 MIME 類型,是圖片還是文本還是二進制數據。

          解析

          客戶端發送請求到服務器后,服務器會收到請求的消息體,然后對消息體進行解析,解析出哪是普通表單哪些是附件。

          可能大家馬上能想到通過正則或者字符串處理分割出內容,不過這樣是行不通的,二進制buffer轉化為string,對字符串進行截取后,其索引和字符串是不一致的,所以結果就不會正確,除非上傳的就是字符串。

          不過一般情況下不需要自行解析,目前已經有很成熟的三方庫可以使用。

          至于如何解析,這個也會占用很大篇幅,后面的文章在詳細說。

          最原始的文件上傳

          使用 form 表單上傳文件

          在 ie時代,如果實現一個無刷新的文件上傳那可是費老勁了,大部分都是用 iframe 來實現局部刷新或者使用 flash 插件來搞定,在那個時代 ie 就是最好用的瀏覽器(別無選擇)。

          DEMO



          這種方式上傳文件,不需要 js ,而且沒有兼容問題,所有瀏覽器都支持,就是體驗很差,導致頁面刷新,頁面其他數據丟失。

          HTML

           <form method="post" action="http://localhost:8100" enctype="multipart/form-data">
          
                  選擇文件:
                      <input type="file" name="f1"/> input 必須設置 name 屬性,否則數據無法發送<br/>
          <br/>
                      標題:<input type="text" name="title"/><br/><br/><br/>
          
                  <button type="submit" id="btn-0">上 傳</button>
          
          </form>
          
          復制代碼

          文件上傳接口

          服務端文件的保存基于現有的庫koa-body結合 koa2實現服務端文件的保存和數據的返回。

          在項目開發中,文件上傳本身和業務無關,代碼基本上都可通用。

          在這里我們使用koa-body庫來實現解析和文件的保存。

          koa-body 會自動保存文件到系統臨時目錄下,也可以指定保存的文件路徑。



          然后在后續中間件內得到已保存的文件的信息,再做二次處理。

          • ctx.request.files.f1 得到文件信息,f1為input file 標簽的 name
          • 獲得文件的擴展名,重命名文件

          NODE

          /**
           * 服務入口
           */
          var http = require('http');
          var koaStatic = require('koa-static');
          var path = require('path');
          var koaBody = require('koa-body');//文件保存庫
          var fs = require('fs');
          var Koa = require('koa2');
          
          var app = new Koa();
          var port = process.env.PORT || '8100';
          
          var uploadHost= `http://localhost:${port}/uploads/`;
          
          app.use(koaBody({
              formidable: {
                  //設置文件的默認保存目錄,不設置則保存在系統臨時目錄下  os
                  uploadDir: path.resolve(__dirname, '../static/uploads')
              },
              multipart: true // 開啟文件上傳,默認是關閉
          }));
          
          //開啟靜態文件訪問
          app.use(koaStatic(
              path.resolve(__dirname, '../static') 
          ));
          
          //文件二次處理,修改名稱
          app.use((ctx) => {
              var file = ctx.request.files.f1;//得道文件對象
              var path = file.path;
              var fname = file.name;//原文件名稱
              var nextPath = path+fname;
              if(file.size>0 && path){
                  //得到擴展名
                  var extArr = fname.split('.');
                  var ext = extArr[extArr.length-1];
                  var nextPath = path+'.'+ext;
                  //重命名文件
                  fs.renameSync(path, nextPath);
              }
              //以 json 形式輸出上傳文件地址
              ctx.body = `{
                  "fileUrl":"${uploadHost}${nextPath.slice(nextPath.lastIndexOf('/')+1)}"
              }`;
          });
          
          /**
           * http server
           */
          var server = http.createServer(app.callback());
          server.listen(port);
          console.log('demo1 server start ......   ');
          復制代碼

          CODE

          https://github.com/Bigerfe/fe-learn-code/


          下拉列表select在表單中應該算是使用頻率比較高的一個元素,而我們也知道頁面上默認的select標簽樣式是很難看的。基本所有的團隊都會選擇使用Bootstrap或者自己封裝一個下拉列表的組件,今天我們也試著使用CSS3新特性來寫一個帶動畫效果的下拉列表樣式吧。

          CSS3

          感興趣的可以自行去github上學習源碼,github地址如下。

          https://github.com/zhouxiongking/article-pages/blob/master/articles/dropdown/dropdown.html

          頁面效果

          首先,我們來看看帶動畫效果的下拉列表效果是什么樣的,如下圖所示。

          效果圖

          頁面組成

          這里我們簡單分析下頁面的構成,實際上非常的簡單,主要包含以下三個元素。

          1. 表示外層容器的div元素

          2. 上面是一個span元素

          3. 下面是一個ul元素,每個li表示一個列表項

          在整個下拉列表中,主要有兩個動畫。

          1. 下拉菜單旁邊的小三角圖標,在鼠標懸浮和離開時動態變換

          2. 下方的列表,在鼠標懸浮和離開上面的span元素時,列表會有動畫顯示和消失的效果。

          代碼構成

          下面我們來看看整個頁面的代碼構成。

          • HTML

          首先是頁面的HTML代碼,正如上一部分所講,頁面主要為一個外層容器div,內部是一個span和ul元素,每個列表項li內包含一個a元素,所有HTML部分代碼如下所示。

          HTML代碼

          • CSS

          CSS部分的代碼才是整個實現效果的核心,我們具體來分析。

          首先是頁面的全局基本配置樣式。

          全局配置

          然后是外層容器的樣式。

          外層容器樣式

          然后是span元素的樣式,旁邊的上三角形通過偽元素::after來實現,在::after偽元素中通過border-color屬性來控制三角形的朝向,然后通過transition屬性設置變換樣式。

          span元素以及上三角形

          當鼠標停留在span元素上時,span元素背景色會改變,上三角會變成下三角。

          鼠標停留后span的樣式

          緊接著是下面的ul以及li元素的樣式,都是一些很普通的屬性。

          ul與li樣式

          然后是li下的a元素的樣式。

          li下a元素樣式

          然后是css中最核心的地方,ul的動畫效果,通過translate3d屬性改變ul所處的位置,再以transform屬性添加動畫效果。

          ul初始狀態

          至此,所有代碼講解完畢,如果運行成功后,會得到文章開始時的下拉列表效果。

          結束語

          本篇文章主要給大家介紹了如何使用CSS編寫一個帶動畫的下拉列表效果,你學會了嗎?


          主站蜘蛛池模板: 91久久精品无码一区二区毛片 | 日韩一区二区在线观看视频| 国产精品福利区一区二区三区四区| 亚洲综合一区二区精品导航| 国产一区二区三区露脸| 蜜臀AV一区二区| 一区二区三区视频在线| 久久久久国产一区二区| 福利一区二区在线| 国产激情一区二区三区| 国语精品一区二区三区| 亚洲码一区二区三区| 亚洲AV无码一区二区三区网址 | 国产一区二区精品在线观看| 精品女同一区二区三区免费播放 | 无码少妇一区二区三区芒果| 午夜福利无码一区二区| 日韩经典精品无码一区| 东京热无码av一区二区| 本免费AV无码专区一区| 亚洲熟妇av一区二区三区下载| 无码中文字幕人妻在线一区二区三区| 国产成人精品一区二区三区免费| 文中字幕一区二区三区视频播放| 91成人爽a毛片一区二区| 性色AV一区二区三区无码| 欧洲精品一区二区三区在线观看| 中文字幕久久亚洲一区| 国产精品视频分类一区| 日韩国产一区二区| 激情无码亚洲一区二区三区| 国产内射在线激情一区| chinese国产一区二区| 亚洲欧洲日韩国产一区二区三区| 日韩视频免费一区二区三区| 国产福利视频一区二区| 国产无吗一区二区三区在线欢| 日本大香伊一区二区三区| 免费看AV毛片一区二区三区| 国产香蕉一区二区精品视频| 波多野结衣免费一区视频 |