(function () {
let class2type = {},
toString = class2type.toString,
hasOwn = class2type.hasOwnProperty,
fnToString = hasOwn.toString,
ObjectFunctionString = fnToString.call(Object),
getProto = Object.getPrototypeOf
// 检测数据类型的通用方法
const toType = function toType(obj) {
if (obj == null) return obj + ""
return typeof obj === "object" || typeof obj === "function" ?
/^\[object (\w+)\]$/g.exec(toString.call(obj))[1].toLowerCase() :
typeof obj
}
// 检测是否为函数
const isFunction = function isFunction(obj) {
// 排除部分浏览器中
// + typeof document.createElement("object")==="function"
// + typeof document.getElementsByTagName("div")==="function"
return typeof obj === "function" && typeof obj.nodeType !== "number" &&
typeof obj.item !== "function"
}
// 检测是否为window对象
const isWindow = function isWindow(obj) {
return obj != null && obj === obj.window
}
// 检测是否为数组或者类数组
const isArrayLike = function isArrayLike(obj) {
let length = !!obj && "length" in obj && obj.length,
type = toType(obj)
if (isFunction(obj) || isWindow(obj)) return false
return type === "array" || length === 0 ||
typeof length === "number" && length > 0 && (length - 1) in obj
}
// 检测是否为纯粹的对象「标准普通对象 或者 直属类是Object」
const isPlainObject = function isPlainObject(obj) {
let proto, Ctor
if (!obj || toString.call(obj) !== "[object Object]") return false
proto = getProto(obj)
if (!proto) return true
Ctor = hasOwn.call(proto, "constructor") && proto.constructor
return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString
}
// 检测是否为空对象
const isEmptyObject = function isEmptyObject(obj) {
if (obj == null) return false
if (typeof obj !== "object") return false
let keys = Object.keys(obj)
keys = keys.concat(Object.getOwnPropertySymbols(obj))
return keys.length === 0
}
// 检测是不是有效数字
const isNumeric = function isNumeric(obj) {
let type = toType(obj)
return (type === "number" || type === "string") && !isNaN(obj)
}
// 函数防抖
const clearTimer = function clearTimer(timer) {
if (timer) clearTimeout(timer)
return null
}
const debounce = function debounce(func, wait, immediate) {
if (typeof func !== 'function') throw new TypeError('func is not a function~')
if (typeof wait === 'boolean') immediate = wait
if (typeof wait !== 'number') wait = 300
if (typeof immediate !== "boolean") immediate = false
let timer = null
return function operate(...params) {
let now = !timer && immediate,
result
timer = clearTimer(timer)
timer = setTimeout(() => {
if (!immediate) func.call(this, ...params)
timer = clearTimer(timer)
}, wait)
if (now) result = func.call(this, ...params)
return result
}
}
// 函数节流
const throttle = function throttle(func, wait) {
if (typeof func !== 'function') throw new TypeError('func is not a function~')
if (typeof wait !== 'number') wait = 300
let timer = null,
previous = 0
return function operate(...params) {
let now = +new Date(),
remaining = wait - (now - previous),
result
if (remaining <= 0) {
result = func.call(this, ...params)
previous = +new Date()
timer = clearTimer(timer)
} else if (!timer) {
timer = setTimeout(() => {
func.call(this, ...params)
previous = +new Date()
timer = clearTimer(timer)
}, remaining)
}
return result
}
}
// 迭代数组/类数组/对象
const each = function each(obj, callback) {
let length,
i = 0,
keys
if (isArrayLike(obj)) {
length = obj.length
for (
if (callback.call(obj[i], obj[i], i) === false) break
}
} else {
keys = Object.keys(obj)
if (typeof Symbol !== "undefined") keys.concat(Object.getOwnPropertySymbols(obj))
for (i = 0
let key = keys[i],
item = obj[key]
if (callback.call(item, item, key) === false) break
}
}
return obj
}
// 实现数组/对象的深浅拷贝
const clone = function clone() {
// 处理传递的实参 target:克隆的目标 deep:控制深浅克隆
let target = arguments[0],
len = arguments.length,
deep = false,
treated = arguments[len - 1]
if (typeof target === "boolean" && len >= 2) {
deep = target
target = arguments[1]
}
// 防止套娃操作
if (!Array.isArray(treated) || !treated.treated) {
treated = []
treated.treated = true
}
if (treated.includes(target)) return target
treated.push(target)
// 检测类型
let type = toType(target),
isArray = Array.isArray(target),
isObject = isPlainObject(target)
// 特殊值的克隆「含:原始值、特殊对象...」
if (target == null) return target
let ctor = target.constructor
if (/^(regexp|date)$/i.test(type)) return new ctor(target)
if (/^(error)$/i.test(type)) return new ctor(target.message)
if (isFunction(target)) return function proxy(...params) {
return target.call(this, ...params)
}
if (!isObject && !isArray) return target
// 数组和对象的克隆
let result = new ctor()
each(target, (copy, key) => {
// copy:要拷贝的属性值 key:属性名
if (deep) {
// 深拷贝
result[key] = clone(deep, copy, treated)
return
}
// 浅拷贝
result[key] = copy
})
return result
}
// 数组和对象的深浅合并
const merge = function merge() {
let options,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false,
treated = arguments[length - 1]
// 第一次执行merge,最后一项是用来替换TARGET的,不是用来记录谁处理过、谁没处理过的,所以我们要为其赋值一个新数组;等到后期每次递归,我们都会在最末尾把存放哪些处理过的数组传递过来,此时最后一项这个数组才不是用来替换TARGET的!! => treated.treated有这个属性的数组是专门存放哪些处理过的
Array.isArray(treated) && treated.treated ? length-- : (treated = [], treated.treated = true)
// 如果第一个值是布尔,要把这个值给DEEP,让TARGET存储的第二个传递的参数(也就是第一个对象),也就是要被替换的对象
if (typeof target === "boolean") {
deep = target
target = arguments[i] || {}
i++
}
// 确保TARGET是个对象
if (typeof target !== "object" && !isFunction(target)) target = {}
// 循环除第一个传递的对象外,剩下的每个传递的对象
for (
options = arguments[i]
if (options == null) continue
// 之前已经处理过这个对象的,就没必要在处理了;没处理过的,加入到treated处理列表中!
if (treated.includes(options)) return options
treated.push(options)
// 循环这个对象中的每一项,用每一项的值替换TARGET中对应项的值
each(options, function (copy, name) {
let copyIsArray = Array.isArray(copy),
copyIsObject = isPlainObject(copy),
src = target[name],
clone = src
// 如果某一项的值是纯粹对象或者数组,并且DEEP是TURE,我们开启深度合并
if (deep && copy && (copyIsArray || copyIsObject)) {
if (copyIsArray && !Array.isArray(clone)) clone = []
if (copyIsObject && !isPlainObject(clone)) clone = {}
target[name] = merge(deep, clone, copy, treated)
} else if (copy !== undefined) {
target[name] = copy
}
})
}
return target
}
let utils = {
debounce,
throttle,
toType,
isFunction,
isWindow,
isArrayLike,
isPlainObject,
isEmptyObject,
isNumeric,
clone,
merge,
each
}
/* 导出模块 */
if (typeof module === "object" && typeof module.exports === "object") module.exports = utils
if (typeof window !== 'undefined') window.utils = utils
})()