常用工具函数
常用工具函数
选择器
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();
}