js公共函数总结2,非常用

220 阅读3分钟

1. 自己实现一个模板引擎

function render(template, data) {
  const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
  if (reg.test(template)) { // 判断模板里是否有模板字符串
    const name = reg.exec(template)[1]; // 查找当前模板里第一个模板字符串的字段
    template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
    return render(template, data); // 递归的渲染并返回渲染后的结构
  }
  return template; // 如果模板没有模板字符串直接返回
}
let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
  name: '姓名',
  age: 18
}
render(template, data); // 我是姓名,年龄18,性别undefined

2. 查看各种时间

/**
 * 查看各种时间
 * @export
 * @result
    DNS查询耗时 :0
    publicFunc.js?7fcd:9 TCP链接耗时 :0
    publicFunc.js?7fcd:10 request请求耗时 :1
    publicFunc.js?7fcd:11 解析dom树耗时 :-1597485845238
    publicFunc.js?7fcd:12 白屏时间 :4
    publicFunc.js?7fcd:13 domready时间 :504
    publicFunc.js?7fcd:14 onload时间 :-1597485844734
    publicFunc.js?7fcd:17 js内存使用占比 :96.83%
 */
export function consoleTime() {
    setTimeout(function () {
        let t = performance.timing
        console.log('DNS查询耗时 :' + (t.domainLookupEnd - t.domainLookupStart).toFixed(0))
        console.log('TCP链接耗时 :' + (t.connectEnd - t.connectStart).toFixed(0))
        console.log('request请求耗时 :' + (t.responseEnd - t.responseStart).toFixed(0))
        console.log('解析dom树耗时 :' + (t.domComplete - t.domInteractive).toFixed(0))
        console.log('白屏时间 :' + (t.responseStart - t.navigationStart).toFixed(0))
        console.log('domready时间 :' + (t.domContentLoadedEventEnd - t.navigationStart).toFixed(0))
        console.log('onload时间 :' + (t.loadEventEnd - t.navigationStart).toFixed(0))

        if (t = performance.memory) {
            console.log('js内存使用占比 :' + (t.usedJSHeapSize / t.totalJSHeapSize * 100).toFixed(2) + '%')
        }
    })
}

3. arrayBuffer和String的相互转换

// arrayBufferToString
function ab2str(buf) {
  return String.fromCharCode.apply(null, new Uint16Array(buf));
}

// stringToArrayBuffer
function str2ab(str) {
  var buf = new ArrayBuffer(str.length * 2); // 2 bytes for each char
  var bufView = new Uint16Array(buf);
  for (var i = 0, strLen = str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
  }
  return buf;
}

4. 判断是否是闰年

判断是否为闰年, isLeapYear通过返回bool值来确定年份是否为闰年。 是闰年的判断条件有2个 。

  1. 世纪年能被400整除的是闰年。(如2000年是闰年,1900年不是闰年)
  2. 普通年能被4整除且不能被100整除的为闰年。(如2004年就是闰年,1900年不是闰年)

只要满足这两个条件之一,就能确定是闰年

/**
 * @TODO 判断是否为闰年
 * @date 2012-8-9
 * @param intYear  代表年份的值
 * @return true 是闰年 false不是闰年
 */
export function isLeapYear(intYear) {
  if (intYear % 100 == 0) {
    if (intYear % 400 == 0) {
      return true;
    }
  } else {
    if ((intYear % 4) == 0) {
      return true;
    }
  }
  return false;
}

5. toFixed的精度问题及修正方法。

1.35.toFixed(1) // 1.4 正确
1.335.toFixed(2) // 1.33  错误
1.3335.toFixed(3) // 1.333 错误
1.33335.toFixed(4) // 1.3334 正确
1.333335.toFixed(5)  // 1.33333 错误
1.3333335.toFixed(6) // 1.333333 错误

解决toFixed。 在Number原型上重写 toFixed方法

 我们通过判断最后一位是否大于等于5来决定需不需要进位,如果需要进位先把小数乘以倍数变为整数,加1之后,再除以倍数变为小数,这样就不用一位一位的进行判断

// toFixed兼容方法
Number.prototype.toFixed = function(len){
    if(len>20 || len<0){
        throw new RangeError('toFixed() digits argument must be between 0 and 20');
    }
    // .123转为0.123
    var number = Number(this);
    if (isNaN(number) || number >= Math.pow(10, 21)) {
        return number.toString();
    }
    if (typeof (len) == 'undefined' || len == 0) {
        return (Math.round(number)).toString();
    }
    var result = number.toString(),
        numberArr = result.split('.');

    if(numberArr.length<2){
        //整数的情况
        return padNum(result);
    }
    var intNum = numberArr[0], //整数部分
        deciNum = numberArr[1],//小数部分
        lastNum = deciNum.substr(len, 1);//最后一个数字
    
    if(deciNum.length == len){
        //需要截取的长度等于当前长度
        return result;
    }
    if(deciNum.length < len){
        //需要截取的长度大于当前长度 1.3.toFixed(2)
        return padNum(result)
    }
    //需要截取的长度小于当前长度,需要判断最后一位数字
    result = intNum + '.' + deciNum.substr(0, len);
    if(parseInt(lastNum, 10)>=5){
        //最后一位数字大于5,要进位
        var times = Math.pow(10, len); //需要放大的倍数
        var changedInt = Number(result.replace('.',''));//截取后转为整数
        changedInt++;//整数进位
        changedInt /= times;//整数转为小数,注:有可能还是整数
        result = padNum(changedInt+'');
    }
    return result;
    //对数字末尾加0
    function padNum(num){
        var dotPos = num.indexOf('.');
        if(dotPos === -1){
            //整数的情况
            num += '.';
            for(var i = 0;i<len;i++){
                num += '0';
            }
            return num;
        } else {
            //小数的情况
            var need = len - (num.length - dotPos - 1);
            for(var j = 0;j<need;j++){
                num += '0';
            }
            return num;
        }
    }
}

6. 将字符串的空格去除

export function trim(str, pos = 'both') {
	if (pos == 'both') {
		return str.replace(/^\s+|\s+$/g, "");
	} else if (pos == "left") {
		return str.replace(/^\s*/, '');
	} else if (pos == 'right') {
		return str.replace(/(\s*$)/g, "");
	} else if (pos == 'all') {
		return str.replace(/\s+/g, "");
	} else {
		return str;
	}
}

7. 自动忽略console.log语句

在项目的根目录或者 main.js 引入这个函数并执行一次,就可以实现在本地环境显示console, 其他环境忽略 console.log 语句的效果。

export function rewriteLog() {
    console.log = (function (log) {
        return process.env.NODE_ENV === 'development'? log : function() {}
    }(console.log))
}

8. 复制内容到剪贴板

原理:

  1. 创建一个textare元素并调用select()方法选中
  2. document.execCommand('copy')方法,拷贝当前选中内容到剪贴板
export function copyToBoard(value) {
    const element = document.createElement('textarea')
    document.body.appendChild(element)
    element.value = value
    element.select()
    if (document.execCommand('copy')) {
        document.execCommand('copy')
        document.body.removeChild(element)
        return true
    }
    document.body.removeChild(element)
    return false
}

9. 对象转化为FormData对象

使用场景:上传文件时我们要新建一个FormData对象,然后有多少个参数就append多少次,使用该函数可以简化逻辑

/**
 * 对象转化为formdata
 * @param {Object} object
 */

export function getFormData(object) {
    const formData = new FormData()
    Object.keys(object).forEach(key => {
        const value = object[key]
        if (Array.isArray(value)) {
            value.forEach((subValue, i) =>
                formData.append(key + `[${i}]`, subValue)
            )
        } else {
            formData.append(key, object[key])
        }
    })
    return formData
}

10. 生成随机字符串

方法1. 通过for循环将随机字符串遍历添加到返回结果中(推荐使用)

/**
 * 生成随机字符串       
 * @param {string} chars 
 * @param {number} length 
 * @returns string
 */
export function uuid(chars, length = 4) {
    chars = chars || "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678" // 去除了一些容易有歧义的字母和数字
    let res = ''
    for (var i = length; i > 0; --i) {
        res += chars[Math.floor(Math.random() * chars.length)]
    }
    return res
}

方法2. Math.random().toString(36).slice(-4);

Math.random()  // 生成随机数字
.toString(36)  // 转化成36进制: "0.ihtkybjh3t"
.slice(-4)     // 截取后四位  "jh3t"

方法2缺点:

  1. 只能生成有 0-9、a-z字符组成的字符串

  2. 由于 Math.random()生成的18位小数,可能无法填充36位,最后几个字符串,只能在指定的几个字符中选择。导致随机性降低。

  3. 某些情况下会返回空值。例如,当随机数为 0, 0.5, 0.25, 0.125...时,返回为空值

11. 深度冻结

/**
 * 深冻结
 * 冻结对象 Object.freeze(obj)。 但是这种办法只能冻结一层。如果有多层就不起作用。 比如对象内部有数组或对象就无效。 所以需要遍历去深度冻结对象
* @param {*} obj 传入的对象参数
* result 结果和传参一致
*/
export function deepFreeze(obj) {
    if (!Object.isFrozen(obj)) {
        Object.freeze(obj)
    }
    for (let key in obj) {
        if (!obj.hasOwnProperty(key) || !_.isObject(obj[key])) {
            continue
        }
        this.deepFreeze(obj[key])
    }
}