前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >十个常用的工具函数​

十个常用的工具函数​

作者头像
多云转晴
发布2019-10-23 16:32:28
1K0
发布2019-10-23 16:32:28
举报
文章被收录于专栏:webTowerwebTower

10个常用的工具函数

几个比较常用的JavaScript工具函数汇总。

1. 数组扁平化

一次扁平化

代码语言:javascript
复制
function flat(arr){
    return Array.prototype.concat.apply([],arr);
}

上面函数只能扁平化二维的数组,比如:

代码语言:javascript
复制
console.log(flat([1,2,[3,4]]));     // [1,2,3,4]

// 更深层次的数组不能被扁平化
console.log(flat([1,[2,3,[4,5]]]));  // [1,2,3,[4,5]]

深层次扁平化

利用闭包和递归来实现。

代码语言:javascript
复制
function flat(arr){
    // 判断是不是数组的函数
    if(!Array.isArray(arr)){
        return arr;
    }
    function isArr(arr){
        return Object.prototype.toString.call(arr) === '[object Array]' ? true : false;
    }

    // 利用闭包存储结果
    var result = [];

    function getResult(array){
        for(let i = 0,len = array.length; i < len;i ++){
            if(isArr(array[i])){
                // 如果里面的项还是数组就递归
                getResult(array[i]);
            }else{
                result.push(array[i]);
            }
        }
        // 返回最终的结果,递归的出口
        return result;
    }
    return getResult(arr);
}

或者:

代码语言:javascript
复制
function flat(arr){
    if(!Array.isArray(arr)){
        return arr;
    }
    var result = [];
    (function fn(array){
        array.forEach(item => {
            if(Array.isArray(item)) fn(item);
            else result.push(item);
        })
    })(arr);

    return result;
}

ES6 中的数组扁平化

ES6 的数组中提供了 flat 函数。这个函数是 Array.prototype 上的一个函数。而且可以指定要提取嵌套数组的结构深度,默认值为 1。

代码语言:javascript
复制
var arr = [
    1,
    [
        2,
        [3,4]
    ]
]

console.log(arr.flat(1));   // [1,2,[3,4]]
console.log(arr.flat(2));   // [1,2,3,4]

使用 Infinity 作为深度,展开任意深度的嵌套数组。应当注意的是,这个 API 很新,浏览器兼容性会比较差。

代码语言:javascript
复制
console.log(arr.flat(Infinity));   // [1,2,3,4]

2. 深度拷贝

利用 JSON 序列化进行深度拷贝

代码语言:javascript
复制
function deepClone(data){
    return JSON.parse(JSON.stringify(data));
}

这种方式有些缺点,不支持函数和 undefined(undefined还会报错)。可以利用递归来实现深度拷贝。

代码语言:javascript
复制
function deepClone(data){

    function judge(val){
        var toString = Object.prototype.toString;
        switch(toString.call(val)){
            case '[object Object]':
                return 'object';
            case '[object Array]':
                return 'array';
        }
    }

    var result;

    switch(judge(data)){
        case 'array':
            result = [];
            break;
        case 'object':
            result = {};
            break;
            // deepClone 的出口
            // 返回对象和数组之外的类型
        default: return data;
    }

    for(let k in data){
        var value = data[k];
        if(judge(value) === 'object'){
            // 是对象或者数组时递归
            result[k] = deepClone(value);
        }else if(judge(value) === 'array'){
            result[k] = deepClone(value);
        }else{
            result[k] = value;
        }
    }
    return result;
}

解决循环引用问题

代码语言:javascript
复制
// 引入 WeakMap 是为了解决循环引用的问题
function deepClone(object, wMap = new WeakMap()){
    var res = null;
    // 不是对象时,直接返回 object
    // 因为 typeof null === 'object' 成立
    if(object === null || !typeof object === 'object'){
        return object;
    }
    // 是数组时 res 等于一个数组
    if(Array.isArray(object)){
        res = [];
    }else if(Object.prototype.toString.call(object) === "[object Object]"){
        // 是对象时,res 等于一个对象
        res = {};
    }
    const obj = wMap.get(object);
    if(obj){
        // 如果 WeakMap 实例中有了要遍历的对象
        // 直接引用即可,这样就避免了循环引用
        return obj;
    }
    // 没有的话,就设置上
    wMap.set(object,res);
    for(let p in object){
        if(typeof object[p] === "object"){
            // 是对象时,递归遍历对象
            res[p] = deepClone(object[p],wMap);
        }else{
            // 不是对象时直接赋值
            res[p] = object[p];
        }
    }
    // 返回每一个递归生成的 res 对象
    return res;
}

上面代码中只考虑了要克隆的对象是一个“纯对象”或者“纯数组”。对于正则表达式、函数等特殊的对象并没有考虑。 需要注意的是,wMap.set(object,res); 的值就应该是 res,理由如下:

代码语言:javascript
复制
var b = {};
var a = {a1: b,a2: b};
console.log(a.a1 === a.a2);     // true

var clone = deepClone(a);
// 如果 wMap 的值设置的是 res,则下面等式成立
console.log(clone.a1 === clone.a2);     // true

上面代码中,要克隆对象 a 中的 a1 和 a2 属性值都指向对象 b 的地址,因此这俩属性值相等。在克隆 a 后,克隆的对象中的 a1、a2 的属性值也应该相等。

3. 数组乱序

给定一个数组,将数组中的元素重新随机分配到不同的下标上。 使用交换算法实现:

代码语言:javascript
复制
/**
 * 数组乱序(洗牌算法)
 * @param {Array} array
 */
function shuffle(array){
    // 浅克隆
    var result = [...array],
        len = array.length;

    for(let i = 0;i < len;i ++){
        // 获取随机的下标
        var randomIdx = Math.floor(Math.random() * len);
        // 交换
        var temp = result[randomIdx];
        result[randomIdx] = result[i];
        result[i] = temp;
    }

    return result;
}

4. 数组去重

ES5 中利用对象去重

代码语言:javascript
复制
var unique = function(array){
    var obj = {},
        result = [];
    for(var v of array){
        if(!obj[v]){
            // 对象中没有这个属性时就添加
            result.push(v);
            obj[v] = v;
        }
    }
    return result;
}

ES6 中利用 Set 去重:

代码语言:javascript
复制
var unique = function(array){
    return [...new Set(array)];
}

5. 寻找数组中的最大项

ES5 中使用 Math.minMath.max 来实现。

代码语言:javascript
复制
var getMaxItem = function(array){
    return Math.max.apply(null,array);
}
var getMinItem = function(array){
    return Math.min.apply(null,array);
}

ES6 的实现

代码语言:javascript
复制
var getMaxItem = function(array){
    return Math.max(...array);
}
var getMinItem = function(array){
    return Math.min(...array);
}

6. 防抖

防抖是为了让在指定时间内一个事件只触发一次。比如点击提交按钮,0.5 秒内可能会点击许多次按钮,不做优化的话,这样会频繁进行网络请求。防抖可以优化高频触发事件。

代码语言:javascript
复制
function debounce(fn,delay){
    var timer = null;
    return function(){
        clearTimeout(timer);
        var self = this,
            args = arguments;
        timer = setTimeout(function(){
            fn.apply(self,args);
        },delay || 500);
    }
}

7. 节流

节流和防抖作用很像,不同的是节流会在一定时间范围里均匀的触发某个事件,没有做优化的事件可能一秒中触发了10次时间函数,而节流之后肯能一秒钟只触发3次事件。节流也可用于动画效果。

代码语言:javascript
复制
var throttle = function(fn,interval){
    var first = true,
        timer = null;
    return function(){
        var _args = arguments,
            self = this;
        if(first){
            fn.apply(self,_args);
            return first = false;
        }
        if(timer){
            return false;
        }
        timer = setTimeout(function(){
            clearTimeout(timer);
            timer = null;
            fn.apply(self,_args);
        },interval || 400);
    }
}

8. 判断一个日期是不是今天

输入一个形如 “2019/10/9” 或者 “2019-10-9” 的日期格式,判断输入的日期是不是今天。这个函数不止一种实现方式。利用 Date.toLocaleDateString() 方法实现就是其中一种。

代码语言:javascript
复制
function isToday(str){
    var s = str.replace(/\-/g,"/"),
        // 考虑到 IE 兼容性
        // 在 IE 中,toLocaleString() 的日期格式是这样的:"xxxx年xx月xx日"
        nowDate = (new Date()).toLocaleString().replace(/年|月|日/g,"/");
    if(s === nowDate){
        return true;
    }
    return false;
}

在 IE 浏览器中,toLocaleDateString() 方法返回的日期格式为 “xxxx年xx月xx日”(当然,只能在中文页面适用),因此需要将“年”、“月”、“日”替换成“/”。为了兼容,输入 “xxxx-xx-xx” 的日期格式时,应把这种格式转成“xxxx/xx/xx” 的格式。因为除了 IE 之外,其它浏览器的 toLocaleDateString() 的方法默认返回的都是 “xxxx/xx/xx” 格式的日期。

还有一个方法,将输入的字符串的年月日拆解出来,比较年月日的大小是否相同来实现。

代码语言:javascript
复制
function isToday(str){
    str = str.replace(/\-/g,'/');
    var s = str.split("/");
    var now = new Date();
    return (
        Number(s[0]) === now.getFullYear() &&
        Number(s[1]) === now.getMonth() + 1 &&
        Number(s[2]) === now.getDate()
    );
}

为了让如输入的日期格式正确,可以使用正则表达式做判断:

代码语言:javascript
复制
const reg = /^\d{4}(-|\/)(1[0-2]|(0?[1-9]))\1([0-2][1-9]|[1-9]|30|31)/;
if(!reg.test(str)){
    throw Error("The arguments' format must be like 'xxxx-xx-xx' or 'xxxx/xx/xx'");
}

9. sleep(seconds) 函数

这个函数很像 setTimeout 函数,可以作为延时函数。

代码语言:javascript
复制
var sleep = function(delay){
    return new Promise(resolve => {
        setTimeout(resolve,delay);
    });
}

// test
function getName(name){
    sleep(1000)
    .then(() => {
        console.log(name);
    })
}
// 执行后,大概一秒后才能打印出 name 的值
getName();

或者使用 ES7 中的 async/await

代码语言:javascript
复制
async function getName(name){
    await sleep(1000);
    console.log(name);
}

10. url 解析与对象序列化

一个有查询字段的 URL,如何将 URL 的查询字段转成一个对象?

代码语言:javascript
复制
const url = 'https://www.example.com/info.json?name=王小明&age=18&sex=male';

console.log(parseURL(url)); // {name: "王小明",age: 18,sex: male}

或者给你一个对象,将这个对象序列化成查询字段?

代码语言:javascript
复制
const object = {
    name: "王小明",
    age: 18,
    sex: "male"
};

console.log(stringifyURL(object));  // "name=王小明&age=18&sex=male"

序列化对象:

代码语言:javascript
复制
function stringifyURL(object){
    var str = "";
    for(let p in object){
        str = str + p + "=" + object[p] + "&";
    }
    return str.replace(/&$/,"");
}

解析 URL,可以使用现成的 URL 类,对输入的 URL进行操作。

代码语言:javascript
复制
function parseURL(url){
    url = new URL(url);
    var res = {},
        // 把查询参数的第一字符是问号,应该去除
        search = decodeURIComponent(url.search.replace("?",""));
        arr = search.split("&");
    arr.forEach(item => {
        var a = item.split("=");
        res[a[0]] = a[1];
    });
    return res;
}

URL 类的实例中也有一个 searchParams 属性,这个属性返回一个 Set 对象。里面存储着查询字符串的信息。

代码语言:javascript
复制
var url = new URL("https://www.example.com/info.json?name=王小明&age=18&sex=male");
console.log(url.searchParams.get("name"));      // 王小明

需要注意的是,URL 类不兼容 IE 浏览器。因此该方法并不推荐使用。可以利用正则表达式写一个兼容的解析函数。

代码语言:javascript
复制
function parseURL(url){
    // 匹配 URL 查询字段的字符串
    var reg = /\?((.+)\=(.*)&?)/g;
        query = url.match(reg);
    if(query){
        // 将第一个问号去掉
        query = decodeURIComponent(query[0].replace("?",""));
        var arr = query.split("&"),
            res = {};
        arr.forEach(item => {
            var a = item.split("=");
            res[a[0]] = a[1];
        });
        return res;
    }
    return null;
}

在 Node.js 中已经实现了 url 的解析与序列化。

代码语言:javascript
复制
const url = require("url");
const qs = require("querystring");

// url 解析:
console.log(
    qs.parse(
    url.parse(
        "https://www.example.com/info.json?name=%E7%8E%8B%E5%B0%8F%E6%98%8E&age=18&sex=male").query
    )
);

// 对象序列化:
const obj = {
    name: "王小明",
    age: 18,
    sex: "male",
};

console.log(qs.stringify(obj));     // name=%E7%8E%8B%E5%B0%8F%E6%98%8E&age=18&sex=male

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebTower 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 10个常用的工具函数
    • 1. 数组扁平化
      • 一次扁平化
    • 2. 深度拷贝
      • 3. 数组乱序
        • 4. 数组去重
          • 5. 寻找数组中的最大项
            • 6. 防抖
              • 7. 节流
                • 8. 判断一个日期是不是今天
                  • 9. sleep(seconds) 函数
                    • 10. url 解析与对象序列化
                    相关产品与服务
                    文件存储
                    文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档