JavaScript utility library.

Many util methods about Date, Cookie, Function, url, detector, collection, timeout, and so on.

Support tree shaking.

Installation

Usage

import { dateFormat } from "relax-utils";
// or:
import * as utils from "relax-utils";
const { dateFormat } = require("relax-utils");
// or:
const utils = require("relax-utils");
define( ["./index.umd.js"] , (utils)=>{ /*...*/ } )
<script src="http://wangwl.net/static/demo/relax-utils/index.umd.js"></script>
importScripts("./index.umd.js")

Issue

https://wangwl.net/static/pages/mynpm_utils.html

Tree Shaking

relax-utils supports tree shaking by default.

You just import like this:

import {dateFormat, dateParse, isSafari} from 'relax-utils';

If you prefer commonjs syntax, like this:

const {dateFormat} = require('relax-utils');

By mini tool's(eg: terser) dead-code elimination, tree-shaking can also work.

API

tick

function tick(preFix?: string): string;

return timestamp.

utils.tick();          //  "1632199932127"
utils.tick('prefix');  //  "prefix1632199932127"

noop

function noop(): void;

Equivalent: function(){};

isUrl

function isUrl(str: string): boolean;

isArrayLike

function isArrayLike(obj: any): boolean;

If isArrayLike return true, obj can be converted to Array by Array#slice or Array.from.

isAndroid、isIos、isWeiXin

function (ua = navigator.userAgent): boolean;

Detect System by navigator.userAgent.

You can also pass a custom userAgent string.

var ua = "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1";
utils.isIos();      //false
utils.isIos(ua);    //true 

isWindows、isMac

function(): boolean

Detect System by navigator.platform.

isWifi

function isWifi(): boolean | undefined;

If network type is detected successfully, return Boolean value.

If detecting is failed(eg: browser do not support some API), return undefined.

isIE、isEdge、isChrome、isFirefox、isSafari

function isIE(ua = navigator.userAgent): null | string;

function isEdge(ua = navigator.userAgent): null | string;

function isChrome(ua = navigator.userAgent): null | string;

function isFirefox(ua = navigator.userAgent): null | string;

function isSafari(ua = navigator.userAgent): null | string;

Detect browser by navigator.userAgent. If matched, return the browser version. If Not matched, return null.

var ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38";
utils.isChrome();   //null;
utils.isSafari(ua); //"604"

defer

function defer(): { promise: Promise, resolve: (data: any) => void, reject: (err: any) => void };

Just return a defer-object.

promisify

function promisify(original: Function, context?: object): Function

将node.js回调风格的函数,转换为返回promise的函数。

模仿node.js中的util.promisify,调用方式一致。

通过可选的context参数设置original执行时的this值。

//node.js异步回调风格: 最后一个参数为回调函数,且回调函数的第一个参数为err。
var add = function (a, b, callback) {
    if (typeof a !== 'number') callback('not a number');
    else callback(null, a + b);
}

utils.promisify(add)(1, 2).then(
    (result) => console.log(result === 3),//true
    (err) => console.log(err)           //该回调不会执行
);

utils.promisify(add)('abc', 2).then(
    (result) => console.log(result), //该回调不执行
    (err) => console.log(err)       //"not a number"
)

与node.js中util.promisify的区别在于,

  1. 该promisify()会判断当传入参数个数小于original时,会补全参数。
  2. 支持callback返回多个值的情况。
// nodejs环境
const nodeUtil = require('util');
const relaxUtil = require('relax-utils');

const print = function (a, b, cb) {
    cb(null, a, b);
}
const printSingle = function (a, cb) {
    cb(null, a);
}

const nodePromisify = nodeUtil.promisify(print);
const relaxPromisify = relaxUtil.promisify(print);
const relaxPromisifySingle = relaxUtil.promisify(printSingle);

nodePromisify('param1').then(
    (result) => console.log('success:', result),
    (error) => console.log('error:', error)
);
//error: TypeError: cb is not a function

nodePromisify('param1', 'param2').then(
    (result) => console.log('success', result),
);
//success: param1

relaxPromisify('param1').then(
    (result) => console.log('success:', result),
)
//success: ['param1',undefined]

relaxPromisify('param1', 'param2').then(
    (results) => console.log('success:', results),
);
//success: ['param1','param2']

relaxPromisifySingle('param1').then(
    (result) => console.log('success:', result)
);
//success: 'param1'

可以通过设置original[utils.promisify.custom]来自定义promise的返回值。

当original不是标准的node.js回调风格函数时候,utils.promisify.custom会比较有用。

var add = function (cb, a, b) {
    return cb(a + b);
};

add[utils.promisify.custom] = function (a, b) {
    return new Promise(function (resolve, reject) {
        add(resolve, a + 1, b);
    })
}

utils.promisify(add)(1, 2).then((result) => {
    console.log(result)
}) //4

pick

function pick(tar: object, keys: (key: string) => boolean | string[]): object;

创建只包含指定属性的对象。

var tar = {
    name: 'wwl',
    sex: 'male',
    birth: '03'
};
utils.pick(tar, ['sex', 'name']) // {name: 'wwl', sex: 'male'}
utils.pick(tar, (key) => {
    return key === 'name'
}); // {name: 'wwl'}

each

function each(
    obj: Object | Array<any>,
    fn: (value: any, index: string | number, obj: object | Array) => void,
    context?: any): void;

Foreach array or object.

When obj is object, this method just foreach enumerable/self property. Because in the underlying
using Object.keys(obj) to iterate.

map

function map(
    obj: Object | Array<any>,
    fn: (value: any, index: string | number, obj: Object | Array) => any,
    context?: any): any[];

映射一个新的数组或对象。

如果obj为对象,则借助Object.keys(obj)进行映射。

find

function find(
    obj: Array | Object,
    fn: (value: any, index: string | number, obj: Array | Object) => boolean,
    context: Object): any | undefined;

返回fn为true时的值。

如果obj为数组,则调用Array#find() , 如果obj为对象,则借助Object.keys(obj)#find()进行查找。

unique

function unique<T>(
    arr: Array<T>,
    isSort = false,
    map?: (item: T, index: number, arr: Array<T>) => any,
    context?: Object): Array<any>;

不改变数组顺序的情况下去重,返回一个新的数据。uniq为unique的别名。

isSort:是否已排序,默认false。如果未排序,则借助includes判断是否存在。

map:映射函数,根据map函数的返回值进行比较。会调用arr.length次map函数。

context:map函数的this值。

当未指定map且支持Set时: 则忽略isSort,优先借助Set进行去重。

utils.unique([5, 2, 3, '5', 2]);   //[5,2,3,5]
utils.unique([5, 2, 3, '5', 2], item => parseInt(item));    //[5,2,3]

loop

function loop(fn: Function, tick: number, immediate = false ): string;

根据setTimeout循环执行fn,支持fn返回一个promise来控制是否继续循环

返回一个key值。在clearLoop()中传入该值来取消循环。

clearLoop

function clearLoop(key: string): void;

取消循环执行。

key为loop()方法返回的值。

timeout

function timeout(wait = 0, fn?: (…args) => T): Promise;

setTimeout的promise版本。返回一个Promise对象。 fn是可选的,如果传入fn,则该Promise返回的是fn的返回的值,如果未传入fn,则该Promise返回undefined。

返回的Promise对象带有abort()方法,该方法内部调用clearTimeout,可以通过该方法取消该定时任务。

var promise = utils.timeout(() => {
    return 5
}, 1000);

promise.then(data => data === 5); //true
typeof promise.abort === 'function'; //true

download

function download(url: string, fileName: string): void;

触发下载指定的url,而不是打开一个新窗口。

在node环境下,该方法为noop()。

param

function param(params: object, encodeEx?: boolean | string[] ): string;

将对象转换为key=val&key1=value1的字符串形式。

value默认通过encodeURIComponent转义,

encodeEx设置为true,则不进行转义,或者设置为一个数组[key1,key2]指定特定的key不进行转义。

utils.param({name: '+wwl'});         //"name=%2Bwwl"
utils.param({name: '+wwl'}, true);    //"name=+wwl"
utils.param({name: '+wwl'}, ['name']);//"name=+wwl"

resolveUrl

function resolveUrl(url: string, param?: object , encodeEx?: boolean | string[] ): string;

在指定的url上添加查询字符串。

encodeEx参数默认为false,具体规则和param()encodeEx参数规则一致。

//该方法不是一个绝对安全的方法,可能会改变原url中查询字符串中参数的顺序,以及丢失无法解析的值。
//例如:
resolveUrl('localhost?name=wwl&abc', {sex: 'male'});
//会返回: localhost?sex=male&name=wwl

parseParam

function parseParam(paramStr: string, decodeEx?: boolean | string[] ): {};

将key=value&key1=value1形式的字符串转换成对象,param的反向操作。

value默认会通过decodeURIComponent进行解密。

通过设置decodeEx参数为true则不进行解密。或者设置为数组[key1,key2]指定特定的key不进行解密。

utils.parseParam('name=%2Bwwl')             //{name:"+wwl"}
utils.parseParam('name=%2Bwwl', true)        //{name:"%2Bwwl"}
utils.parseParam('name=%2Bwwl', ['name'])    //{name:"%2Bwwl"}

注意,当paramStr参数不规范时候,decodeURIComponent会报错,例如:

decodeURIComponent('sex=%');
// Uncaught URIError: URI malformed

这里,utils.parseParamdecodeURIComponent保持一样的逻辑,当参数不规范时候,会抛出错误。

utils.parseParam('name=wwl&sex=%');
// Uncaught URIError: URI malformed (malformed key: sex)

getQuery

function getQuery(url = location.search, decodeEx?: boolean | string[] ): object;

返回代表查询字符串的键值对。

默认处理当前页面的url。

默认会使用decodeURIComponent对解析到的值进行解密。

可通过设置decodeEx不进行解密,具体规则和parseParam()中的decodeEx参数规则一致。

utils.getQuery().id;
utils.getQuery('localhost/indexhtml?id=idinfo').id

类似parseParam,如果参数不规范,向上传递decodeURIComponent的报错。

utils.getQuery('?name=wwl&sex=%');
// Uncaught URIError: URI malformed (malformed key: sex)

countStr

function countStr(txt: string, fullVal = 1, halfVal = 0.5, enterVal = 1): number;

计算字符长度。该方法区分全角字符和半角字符。

fullVal: 全角字符的权重值,默认为1

halfVal: 半角字符的权重值,默认为0.5

enterVal: 回车字符的权重值,默认为1

copyTxt

function copyTxt(txt: string): boolean;

复制txt到剪切板。

如果操作成功,则返回true。

如果操作失败(浏览器不支持),则返回false。

htmlEncode 、 htmlDecode

function htmlEncode(txt: string): string;

function htmlDecode(val: string): string;

html转义和解密。

如果是node环境,htmlDecode只能正确解密由htmlEncode()返回的内容

utils.htmlEncode('<body>');     //&#60;body&#62;
utils.htmlDecode('&lt;body')    //<body

camelCase

function camelCase(…args: string[]): string;

"camel-case"转换为"camelCase"。

或者传入多个字符串,合并为驼峰式。

utils.camelCase('camel-case');  //camelCase
utils.camelCase('I-am', 'wwl'); //IAmWwl

kebabCase

function kebabCase(…args: string[]): string;

将驼峰命名法字符串,转换为小写的短横线分隔形式。
"kebabCase"转换为"kebab-case"。 或传入多个字符串,合并为短横线分隔形式。

utils.kebabCase('this', 'IsTest');   //this-is-test
utils.kebabCase('KebabCase');       //kebab-case

paddingLeft

function paddingLeft(target = '', len: number, paddingChar = " "): string

补齐位数。

utils.paddingLeft('1', 3, '0');  //001
utils.paddingLeft('12345', 3);  //12345

template

function template(template : string, data: object): string

模板函数。计算表达式生成字符串。支持ES6模板字符串语法。

utils.template('hello,${firstName+secondName}', {firstName: 'wang', secondName: 'wl'});
//"hello,wangwl"

retry

function retry(fn, max: number, wait=0, context?: object) : function

创建重试函数。

创建的新函数,内部实际执行fn,如果fn返回了失败的Promise,则会在间隔wait时间之后,重新执行fn,最多执行max次。

其中wait默认为0,即立即进行重试。如果wait大于0,则使用setTimeout在间隔wait之后进行重试。

通过context设置fn执行时候的this的值,默认和返回的函数执行时的this一致。

var cnt = 0;
var fn = function () {
    console.log('retry', ++cnt, this);
    return Promise.reject();
}
utils.retry(fn, 3, 300).bind({test: 'this'})();
// retry1 {test:"this"}
// retry2 {test:"this"}
// retry3 {test:"this"}

cache

function cache(
    fn: Function,
    context?: Object,
    predicate?: (...args) => boolean): (refresh, ...args) => any;

返回一个新的缓存函数。

返回的函数签名为:function(refresh,…args); refresh判断是否强制刷新,剩余参数传给fn。

predicate为可选的,如果传入predicate,在refresh为false时,会根据predicate的返回值判断是否需要刷新。

context为可选的,可以通过context设置返回函数和predicate的this值,

var ori = function (a, b) {
    return a;
};
var cache = utils.cache(ori, function (a, b) {
    return a > b
});

cache(true, 1, 2);    //1
cache();            //1
cache(false, 5, 6);   //1
cache(true, 5, 6);    //5
cache(false, 8, 7);   //8 

throttle 、 debounce

function throttle(fn: Function, alwaysFn?: Function, immediately?: boolean, wait=300, context?: object)

function debounce(fn: Function, alwaysFn?: Function, immediately?: boolean, wait=300, context?: object)

截流和防抖动。

fn: 目标执行方法。

alwaysFn: 每次调用都会执行的方法。

immediately: 是否立即执行。如果为true,则会在第一次调用时立即执行fn,忽略后续的调用。

wait:指定时间,以毫秒为单位。

context: 指定alwaysFn和fn的this值,如果省略,则为结果函数的this值。

var obj = {};
var fn = function () {
    console.log(this === obj)
};
obj.fn = utils.debounce(fn, () => {
    console.log(1)
}, 1000);
obj.fn();
obj.fn();
//1
//1
//true

dateFormat

function dateFormat(date: Date, fmt = 'yyyy-MM-dd hh:mm:ss'): string;

格式化时间。

  1. 支持:年y, 月M, 天d, 24小时制H, 12小时制h, 分m, 秒s, 毫秒S, am/pm a。
  2. 支持转义: 使用中括号对以上字符进行转义。

年份根据y的数量截取,其他值,只补齐不截取。

utils.dateFormat(new Date(), 'yy-MM-dd HH:mm:ss'); //"17-10-30 18:08:08"
utils.dateFormat(new Date(), 'yyyy-M-d h:m:s a'); //"2017-10-30 6:8:8 pm"
//转义:
utils.dateFormat(new Date(), '[today] M-d')  //"today 10-30"

dateParse

function dateParse(str: string, fmt = 'yyyy-MM-dd hh:mm:ss'): Date;

根据时间字符串和指定的格式,返回Date对象。

  1. 支持:年y, 月M, 天d, 24小时制H, 12小时制h, 分m, 秒s, 毫秒S, am/pm a。
  2. 支持转义: 使用中括号对以上字符进行转义。
utils.dateFormat(
    utils.dateParse('2017-10-30 18:8:8', 'yyyy-M-d H:m:s'),
    'yy-MM-dd HH-mm-ss'); //"17-10-30 18-08-08"

utils.dateFormat(
    utils.dateParse('today 6:10 pm', '[today] h:m a'),
    'HH:mm'); //"18:10"

dateAdd(日期计算)

function dateAdd(date, config: number | { year?: number, month?: number, day?: number, hour?: number, min?: number,
sec?: number }): Date;

日期加减法。返回新的Date对象。

var today = utils.dateParse('2017,10,10 10:10:10', 'yyyy,MM,dd hh:mm:ss');
utils.dateAdd(today, -2);        //2017,10,8 10:10:10    等效于utils.dateAdd(today,{day:-2});
utils.dateAdd(today, {hour: 2});  //2017,10,10 12:10:10

firstDateInMonth、 lastDateInMonth

function firstDateInMonth(date: Date): Date;

function lastDateInMonth(date: Date): Date;

返回传入date所在月份的第一天、最后一天的Date对象。

firstWeekInMonth 、 lastWeekInMonth

function firstWeekInMonth(date: Date): Date;

function lastWeekInMonth(date: Date): Date;

返回传入日期所在月份的,第一周的周一、最后一周的周日。

weekRange

function weekRange(
    startDate: Date,
    endDate: Date,
    splitDay = 1): Array<{ start: Date, end: Date, duration: number }>

返回开始日期和结束日期的周。计算时忽略时间,只计算日期。

splitDay:分割点。对应date.getDay()取值0~6。 例如取周一至周日,则splitDay传入1,取周日至周六,splitDay传入0。

返回周的数组。 每一项为:{start:date,end:date,duration:number}。

//获取该月的所有周。
var today = new Date();
utils.weekRange(utils.firstWeekInMonth(today), utils.lastWeekInMonth(today));

weekendsCount

function weekendsCount(startDate: Date, endDate: Date): number

计算开始日期和结束日期共有周六日多少天。计算时忽略时间,只计算日期。

getCookie

function getCookie(refresh = false): object;

返回一个指代当前cookie的对象。兼容.NET中的多值cookie。

该方法内部缓存一个cookie对象,当多次调用时,只对document.cookie解析一次;

refresh: 传入true,强制重新解析cookie并返回。

utils.getCookie().cookieName.value;          //单值cookie,获取键为cookieName的cookie的值。
if (utils.getCookie().multiCookie.values) {    //多值cookie,获取multiCookie中key1的值。
    utils.getCookie().multiCookie.values.key1;
}

setCookie

function setCookie(
    key: string,
    value: string | object,
    option?: {
        path?: string,
        domain?: string,
        secure?: boolean,
        expires?: Date | { day: number, hour: number, min: number, sec: number }
    }): string

设置或添加一个cookie,返回cookie的值。

key:cookie名称

value:cookie的值。 如果传入一个对象,则认为是多值cookie

option.path: cookie路径,默认为当前路径。

option.domain: cookie的域名,默认为当前域名。

option.secure: 是否加密,默认为false

option.expires: 过期时间,默认为session-Cookie。可以传入对象,或一个类似{day?:num,hour?:num,min?:num,sec?:num}的对象向后递推时间。例如{expires:{day:
1}}代表该cookie有效时间为1天。

deleteCookie

function deleteCookie(key: string, option): boolean;

删除一个cookie

cookie对象

let cookie: {
    del: typeof deleteCookie,
    delete: typeof deleteCookie,
    set: typeof setCookie,
    get(name: string, refresh = false): string | undefined
};

cookie.del 、cookie.delete 为 deleteCookie的别名。

cookie.set 为setCookie的别名。

get(name,refresh),获取指定名称的cookie值,只支持单值cookie

注意

在node环境下,'isWifi', 'download', 'copyTxt','getCookie', 'setCookie', 'deleteCookie'不可用;