常用工具函数

常用工具函数

选择器

function selector() {
    var ls = document.querySelectorAll(arguments[0]);
    if (!ls) return null;
    if (ls.length === 1) return ls[0]
    return ls
}

计算rem

(function () {
    var root = document.documentElement;
    function init() {
        var width = root.clientWidth || window.innerWidth;
        width = width < 320 ? 320 : width; //最小屏幕设定为320px
        root.style.fontSize = width >= 640 ? '100px' : (width / 3.75 + 'px'); // 最大屏幕设定为640px;标准屏幕设定为375px
        document.body.style.fontSize = '16px';
    }
    window.addEventListener('orientationchange' in window ? 'orientationchange' : 'resize', init, false);
    document.addEventListener('DOMContentLoaded', init, false);
})()

es5导出模块

(function (global, factory) {
    "use strict"
    if (typeof module === "object" && typeof module.exports === "object") {
        module.exports = factory(global, true)
    } else if (typeof define === 'function' && define.amd) {
        define(function () { return factory(global) })
    } else {
        factory(global)
    }
})(typeof window !== "undefined" ? window : this, function (context) {
})

Ajax

  • get方法
/**
 * @param {string} url 请求地址
 * @param {string} params key=value&key=value 参数字符串
 * @param {Function} fn 回调函数
 */
function get(url, params, fn) {
  var xhr;
  if (window.XMLHttpRequest) {
    //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
    xhr = new XMLHttpRequest();
  } else {
    // IE6, IE5 浏览器执行代码
    xhr = new ActiveXObject("Microsoft.XMLHTTP");
  }
  if (!xhr) {
    return;
  }
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        fn(null, xhr.responseText);
      } else {
        fn(xhr.status, xhr.responseText);
      }
    }
  };
  xhr.open('GET', url + '?' + params, true);
  xhr.send(null);
}
  • post方法
/**
 * @param {string} url 请求地址
 * @param {string} params 1.参数字符串:key=value&key=value; 2.FormData
 * @param {Function} fn 回调函数
 */
function post(url, params, fn) {
  var xhr;
  if (window.XMLHttpRequest) {
    //  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
    xhr = new XMLHttpRequest();
  } else {
    // IE6, IE5 浏览器执行代码
    xhr = new ActiveXObject("Microsoft.XMLHTTP");
  }
  if (!xhr) {
    return;
  }
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        fn(null, xhr.responseText);
      } else {
        fn(xhr.status, xhr.responseText);
      }
    }
  };
  // 发送 FormData 数据, 会自动设置为 multipart/form-data
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.open('POST', url, true);
  xhr.send(params);
}

防抖(Debounce)

  • 什么是防抖

    在事件被触发n秒后再执行回调函数,如果在这n秒内又被触发,则重新计时。

  • 应用场景

    • 用户在输入框中连续输入一串字符后,只会在输入完后去执行最后一次的查询ajax请求,这样可以有效减少请求次数,节约请求资源;
    • window的resize、scroll事件,不断地调整浏览器的窗口大小、或者滚动时会触发对应事件,防抖让其只触发一次;
<button onclick="onclick1(this)">111111</button>
<button onclick="onclick2(this)">222222</button>
<script>
  /**
   * 防抖 构造函数
   * @param {Function} callback
   * @param {Number} delay 延时(毫秒)
   * @param {Boolean} immediate 立即执行: true:是; false:否;
   */
  function Debounce(callback, delay = 1000, immediate = false) {
    if (!callback) return
    var timeout
    var context
    return function () {
      context = this
      // 用户连续点击,则以最后一次点击或停顿为主,重新开始倒计时
      if (timeout) {
        clearTimeout(timeout)
      }
      // 立即执行返回结果
      if (immediate) {
        return callback.apply(context, arguments)
      } else {
        // 延迟执行
        timeout = setTimeout(function () {
          callback.apply(context, arguments)
        }, delay)
      }
    }
  }
  // eg:
  const cb = () => console.log('cb')
  const _debounce = new Debounce(cb)
  function onclick1() { _debounce() }
  // eg2:
  const cb2 = () => console.log('cb2')
  const _debounce2 = new Debounce(cb2)
  function onclick2() { _debounce2() }
</script>

节流(Throttle)

  • 什么是节流

    规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

  • 应用场景

    • 鼠标连续不断地触发某事件(如点击),只在单位时间内只触发一次;
    • 在页面的无限加载场景下,需要用户在滚动页面时,每隔一段时间发一次 ajax 请求,而不是在用户停下滚动页面操作时才去请求数据;
    • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断;
/**
 * 节流 构造函数
 * @param {Function} callback
 * @param {Number} delay 延时(毫秒)
 * @param {Boolean} immediate 立即执行: true:是; false:否;
 */
function Throttle(callback, delay = 500, immediate = false) {
    if(!callback) return
    let lastTime
    let timer
    let context
    let now
    return function () {
        context = this
        now = +new Date()
        if (immediate) return callback.apply(context, arguments) // 立即执行
        if (lastTime && now < lastTime + delay) { // 每隔 delay 秒都会执行一次
            clearTimeout(timer)
            timer = setTimeout(function () {
                lastTime = now
                callback.apply(context, arguments)
            }, delay)
        } else { // 第一次会立即执行
            lastTime = now
            callback.apply(context, args)
        }
    }
}
// eg2:
const cb = (args) => console.log(args)
const throttle = new Throttle(cb, 1000)
window.onscroll = function () { throttle(arguments) }

js圣杯模式的继承

const inherit = (Target, Origin) => {
    Target.prototype = new Origin()
    Target.prototype.constructor = Target
    Target.prototype.$prototype = Origin.prototype // 最终的原型指向
}
const Person = () => { this.name = '人' }
Person.prototype.get = () => this.name
const ChinaPerson = function () {
    Origin.apply(Target, null)
    inherit(ChinaPerson, Person)
    this.age = 10
}
ChinaPerson.prototype.get = () => `${this.name},${this.age}`
const p = new Person()
const cp = new ChinaPerson()
console.log(p.get())
console.log(cp, cp.get())

获得滚动条的滚动距离

function getScrollOffset() {
    if (window.pageXOffset) {
        return {
            x: window.pageXOffset,
            y: window.pageYOffset
        }
    } else {
        return {
            x: document.body.scrollLeft + document.documentElement.scrollLeft,
            y: document.body.scrollTop + document.documentElement.scrollTop
        }
    }
}

获得视口的尺寸

function getViewportOffset() {
    if (window.innerWidth) {
        return {
            w: window.innerWidth,
            h: window.innerHeight
        }
    } else {
        // ie8及其以下
        if (document.compatMode === "BackCompat") {
            // 怪异模式
            return {
                w: document.body.clientWidth,
                h: document.body.clientHeight
            }
        } else {
            // 标准模式
            return {
                w: document.documentElement.clientWidth,
                h: document.documentElement.clientHeight
            }
        }
    }
}

获取任一元素的任意属性

function getStyle(elem, prop) {
    return window.getComputedStyle ? window.getComputedStyle(elem, null)[prop] : elem.currentStyle[prop]
}

绑定、解绑事件的兼容代码

function addEvent(elem, type, handle) {
    if (elem.addEventListener) { //非ie和非ie9
        elem.addEventListener(type, handle, false);
    } else if (elem.attachEvent) { //ie6到ie8
        elem.attachEvent('on' + type, function () {
            handle.call(elem);
        })
    } else {
        elem['on' + type] = handle;
    }
}
function removeEvent(elem, type, handle) {
    if (elem.removeEventListener) { //非ie和非ie9
        elem.removeEventListener(type, handle, false);
    } else if (elem.detachEvent) { //ie6到ie8
        elem.detachEvent('on' + type, handle);
    } else {
        elem['on' + type] = null;
    }
}

取消冒泡的兼容代码

function stopBubble(e) {
    if (e && e.stopPropagation) {
        e.stopPropagation();
    } else {
        window.event.cancelBubble = true;
    }
}

检验字符串是否是回文

顺读和倒读都一样的字符串称为“回文”

function isPalindrome(str) {
    str = str.replace(/\W/g, '').toLowerCase();
    return (str == str.split('').reverse().join(''))
}

异步加载script

function loadScript(url, callback) {
    var el = document.createElement('script')
    if (el.readyState) { // ie8及以下版本
        el.onreadystatechange = function () {
            if (el.readyState === 'complete' || el.readyState === 'loaded') {
                callback()
            }
        }
    } else {
        el.onload = function () {
            callback()
        }
    }
    el.src = url
    document.body && document.body.appendChild(el)
}
loadScript('https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js', function () {
    console.log(111111)
})
loadScript('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js', function () {
    console.log(111111)
})

cookie管理

var cookie = {
    get: function (sKey) {
        return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
    },
    set: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) {
        if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; }
        var sExpires = "";
        if (vEnd) {
            switch (vEnd.constructor) {
                case Number:
                    sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd;
                    break;
                case String:
                    sExpires = "; expires=" + vEnd;
                    break;
                case Date:
                    sExpires = "; expires=" + vEnd.toUTCString();
                    break;
            }
        }
        document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : "");
        return true;
    },
    remove: function (sKey, sPath, sDomain) {
        if (!sKey || !this.has(sKey)) { return false; }
        document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "");
        return true;
    },
    has: function (sKey) {
        return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") + "\\s*\\=")).test(document.cookie);
    },
    keys: function () {
        var arr = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/);
        arr.forEach((k, i) => (arr[i] = decodeURIComponent(k)))
        return arr;
    }
}

实现call、apply、bind方法

Function.prototype._call = function () {
    var args = [].slice.call(arguments)
    var ctx = args.shift() || window
    ctx.fn = this
    var result = ctx.fn(...args)
    delete ctx.fn
    return result
}
Function.prototype._apply = function () {
    var ctx = arguments[0] || window
    var args = arguments[1]
    ctx.fn = this
    args = Array.isArray(args) ? args : []
    var result = ctx.fn(...args)
    delete ctx.fn
    return result
}
Function.prototype._bind = function (otherThis) {
    if (typeof this !== 'function') return
    var baseArgs = [].slice.call(arguments, 1),
        ctx = this,
        Temp = function () { },
        F = function () {
            baseArgs = baseArgs.concat(arguments)
            return ctx.apply(
                Temp.prototype.isPrototypeOf(this) ? this : otherThis, baseArgs
            )
        }
    if (this.prototype) Temp.prototype = this.prototype
    F.prototype = new Temp()
    return F
}

requestAnimFrame、cancelAnimFrame兼容性方法

window.requestAnimFrame = (function () {
    return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        function (callback) {
        window.setTimeout(callback, 1000 / 60)
    }
})()
window.cancelAnimFrame = (function () {
    return window.cancelAnimationFrame ||
        window.webkitCancelAnimationFrame ||
        window.mozCancelAnimationFrame ||
        function (id) {
        window.clearTimeout(id)
    }
})()

jsonp方法

function jsonp(url) {
    var el = document.createElement('script')
    el.src = url
    document.body && document.body.appendChild(el);
}
// eg:
function callback(data) { console.log(data) }
jsonp('http://127.0.0.1:5757/user/getinfo?id=1&callback=callback')

判断有值

/**
 * 判断有值
 * @param {Any} v
 * @returns {Boolean} 是true,否false
 */
function isValue (v) {
  return !(v === undefined || v === 'undefined' || v === null || v === 'null')
}

获取精确的数据类型

/**
 * 获取精确的数据类型
 * typeof Symbol() -> symbol
   toString(1): [object Object] this始终指向Object,需要调用call方法
   Symbol(): [object Symbol]
   document: [object HTMLDocument]
   document.getElementsByTagName('body'): [object HTMLCollection]
 * @param {Any} v
 * @returns {String} string/number/boolean/object...
 */
function toType (v) {
  return Object.prototype.toString.call(v).replace(/\[|\]/g, '').split(/\s/)[1].toLocaleLowerCase()
}

判断数字

/**
 * 判断数字
 * isNaN 非数字,返回true
 * toString.call(NaN) : number
 * typeof NaN : number
 * @param {Any} v
 * @returns {Boolean} 是true,否false
 */
function isNumber (v) {
  return !isNaN(v)
}

判断Promise

/**
 * 判断Promise
 * @param {Object} v
 * @returns {Boolean} 是true,否false
 */
function isPromise (v) {
  return v && typeof v === 'object' && typeof v.then === 'function'
}

判断dom节点

/**
 * 判断dom节点
 * @param {Any} v
 * @returns {Boolean} 是true,否false
 */
function isDom (v) {
  typeof HTMLElement === 'undefined'
  ? v && typeof v === 'object' && v.nodeType !== undefined && v.nodeType !== null
  : v instanceof HTMLElement
}

判断为空

/**
 * 判断为空
 * @param {String|Array|Object} v
 * @returns {Boolean} 是true,否false
 */
function isEmpty (v) {
  if (Array.isArray(v) || typeof v === 'string') {
    return v.length === 0
  }
  if (v instanceof Map || v instanceof Set) {
    return v.size === 0
  }
  if (v && typeof v === 'object') {
    return Object.keys(v).length === 0
  }
  return false
}

随机字符串


/**
 * 随机字符串
 * @param {Number} range
 * @returns {String}
 */
const CHARACTER = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
const CHARACTER_LENGTH = CHARACTER.length
function anystr (range) {
    range = range || 8
    const r = []
    for (let i = 0; i < range; i++) {
        r.push(CHARACTER.charAt(
            Math.floor(Math.random() * CHARACTER_LENGTH)
        ))
    }
    return r.join('')
}

模板字符串替换

/**
 * 模板字符串替换
 * @param {String} s
 * @param {Array|Object} args
 * @returns {String}
 */
function format(s, args) {
    if (!s) return null
    if (!args) return s
    if (typeof s !== 'string') return s
    if (Array.isArray(args)) {
        args.forEach((v, i) => {
            s = s.replace(new RegExp("({[" + i + "]})", "g"), v)
        })
    } else if (typeof args === 'object') {
        Object.keys(args).forEach(k => {
            s = s.replace(new RegExp("({" + k + "})", "g"), args[k])
        })
    }
    return s
}
var eg = format("我是{0},今年{1}了", ['Tom', 15])
var eg2 = format("我是{name},今年{age}了", { name: 'Tom', age: 15 })
var eg3 = format("{ \"a\": \"{name}\", \"b\": {age} }", { name: 'Tom', age: 15 })

单词首字母大写

/**
 * 单词首字母大写
 * @param {String} v
 * @returns {String}
 */
function capitalize (v) {
  return String(v).toLowerCase().replace(/( |^)[a-z]/g, function (s) {
    return s.toUpperCase()
  })
}

中文Unicode编码、解码

const unicode = {
    encode(v) {
        if (!v) return null
        v = String(v)
        const r = []
        for (let i = 0, length = v.length; i < length; i++) {
            // charCodeAt() 方法返回0到65535之间的整数,表示给定索引处的UTF-16代码单元
            // slice(-4): 编码后字符不满足4位的用0填充,并截取最后四位
            r.push(
                ('00' + v.charCodeAt(i).toString(16)).slice(-4)
            )
        }
        return '\\u' + r.join('\\u')
    },
    decode(v) {
        return v ? unescape(v.replace(/\\/g, '%')) : null
    }
}

生成url请求参数字符串、解析url请求参数字符串为对象

const urlParams = {
    queryString(o) {
        if (!o || typeof o !== 'object') return ''
        let v
        return Object.keys(o).map(k => {
            v = o[k]
            if (v === undefined) v = null
            if (v && typeof v === 'object') v = JSON.stringify(v)
            return `${k}=${v}`
        }).join('&')
    },
    get(url, name) {
        if (url && typeof url !== 'string') return ''
        url = decodeURIComponent(url || location.search).replace(/^\?/, '')
        if (!url) return ''
        var o = JSON.parse('{"' +
                           url.replace(/^\?/, '')
                           .replace(/"/g, '\\"')
                           .replace(/&/g, '","')
                           .replace(/=/g, '":"')
                           + '"}')
        return name ? typeof o[name] === undefined ? '' : o[name] : o
    }
}

浅拷贝对象

/**
 * 浅拷贝对象
 * @param {Arguments}
 * @returns {Object}
 * @eg objectMerge({},{},...)
 */
function objectMerge() {
    const args = [].slice.call(arguments)
    const target = Object(args.shift())
    args.forEach(o => {
        if (!o || typeof o !== 'object') return
        Object.keys(o).forEach(k => {
            if (typeof o[k] === undefined) return
            target[k] = o[k]
        })
    })
    return target
}

object格式化,粗暴的克隆方式

/**
 * 对象格式化
 * @param {Object} o
 * @return {Object}
 */
function objectFormat (o) {
  return JSON.parse(JSON.stringify(o || {}))
}

深度克隆对象

/**
 * 深度克隆对象
 * @param {arguments}
 * @returns {Object}
 * @eg deepClone({},{},...)
 */
function deepClone() {
    const args = [].slice.call(arguments)
    const target = Object(args.shift())
    args.forEach((items) => {
        if (!items || typeof items !== 'object') return
        Object.keys(items).forEach((key) => {
            if (key === '__proto__') return // 防止对象原型被污染,防止永无休止的循环
            const row = items[key]
            if (row === undefined) return
            if (Array.isArray(row)) {
                if (!Array.isArray(target[key])) {
                    target[key] = row
                    return
                }
                row.forEach((col, index) => {
                    if (typeof col == 'object') {
                        target[key][index] = deepClone(target[key][index], col)
                    } else {
                        target[key][index] = col
                    }
                })
            } else if (typeof row == 'object') {
                target[key] = deepClone(target[key], row)
            } else {
                target[key] = row
            }
        })
    })
    return target
}

会话缓存、本地储存

const storage = {
    session(name, v) {
        if (v === undefined || v === null) {
            return sessionStorage.getItem(name)
        } else {
            if (typeof v === 'object') v = JSON.stringify(v)
            sessionStorage.setItem(name, v)
            return true
        }
    },
    local(name, v) {
        if (v === undefined || v === null) {
            return localStorage.getItem(name)
        } else {
            if (typeof v === 'object') v = JSON.stringify(v)
            localStorage.setItem(name, v)
            return true
        }
    }
}

倒计时

class Countdown {
    timer = 0;
    stepper = 0;
    longtime = 0;
    delay = 0;
    fn;

    constructor(longtime, delay, callback) {
      this.longtime = longtime || 120;
      this.delay = delay || 1000;
      this.fn = callback || function () { };
    }
    countdown() {
      const that = this;
      const { fn, stepper, timer, longtime, delay } = this;
      fn(that.stepper);
      that.timer = setInterval(function () {
        if (longtime <= stepper) {
          return clearInterval(timer);
        }
        that.stepper++;
        fn(that.stepper);
      }, delay);
    }
    cancel() {
      clearInterval(this.timer);
    }
  }

驼峰相互转换

// 下划线转换驼峰
function toHump(name) {
  return name.replace(/\_(\w)/g, function (all, letter) {
    return letter.toUpperCase();
  });
}

// 驼峰转换下划线
function toLine(name) {
  return name.replace(/([A-Z])/g, "_$1").toLowerCase();
}

posted on 2021-06-16 10:27  我是何平  阅读(42)  评论(0编辑  收藏  举报