写这篇主要是想回顾一下之前手写的一些JS方法,巩固下JS,也做个记录;有些方法在我之前博客也有单独写过,比如手写Promise、基于发布订阅模式的简单JS事件、深拷贝、Vue2响应式原理等等,在这里也算是总结一下吧
之前的博客:js深拷贝
function deepClone(source) {
if(typeof source !== 'object') return source
const target = source.constructor === Array ? [] : {}
for(let key in source) {
if(source.hasOwnProperty(key)) {
if(source[key] && typeof source[key] === 'object') {
target[key] = deepClone(source[key])
} else {
target[key] = source[key]
}
}
}
return target
}
之前博客:手写一个基于发布订阅模式的简单js事件
class EventBus {
constructor() {
this.arrays = {}
}
// 订阅
$on(type, fn) {
this.arrays[type] = this.arrays[type] || []
this.arrays[type].push(fn)
}
// 发布
$emit(type, ...args) {
if(!this.arrays[type]) return
this.arrays[type].forEach(callback => {
callback(...args)
})
}
// 解绑
$off(type, fn) {
const args = Array.from(arguments)
if(args.length === 0) {
this.arrays = {}
} else if(args.length === 1) {
if(!this.arrays[type]) return
this.arrays[type] = []
} else if(args.length === 2) {
if(!this.arrays[type]) return
for(let i = this.arrays[type].length - 1; i >= 0; i--) {
if(this.arrays[type][i] === fn) {
this.arrays[type].splice(i, 1)
}
}
}
}
}
class Observerd {
constructor() {
this.observerList = []
}
addObserver(observer) {
this.observerList.push(observer)
}
notify() {
this.observerList.forEach(observer => {
observer.update()
})
}
}
class Observer {
constructor(doSome) {
this.doSome = doSome
}
update() {
console.log(this.doSome)
}
}
const obj1 = new Observer('观察者1')
const obj2 = new Observer('观察者2')
const objd = new Observerd()
objd.addObserver(obj1)
objd.addObserver(obj2)
objd.notify()
之前博客:手写Promise
class MyPromise {
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(fn) {
this.status = MyPromise.PENDING
this.result = null
this.resolveCallbacks = []
this.rejectCallbacks = []
try {
fn(this.resolve.bind(this), this.reject.bind(this))
} catch(e) {
this.reject(e)
}
}
resolve(data) {
setTimeout(() => {
if(this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED
this.result = data
if(this.resolveCallbacks.length) {
this.resolveCallbacks.forEach(callback => {
callback(data)
})
}
}
})
}
reject(err) {
setTimeout(() => {
if(this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED
this.result = err
if(this.rejectCallbacks.length) {
this.rejectCallbacks.forEach(callback => {
callback(err)
})
}
}
})
}
then(ONFULFILLED, ONREJECTED) {
return new MyPromise((resolve, reject) => {
ONFULFILLED = typeof ONFULFILLED === 'function' ? ONFULFILLED : () => {}
ONREJECTED = typeof ONREJECTED === 'function' ? ONREJECTED : () => {}
if(this.status === MyPromise.PENDING) {
this.resolveCallbacks.push(ONFULFILLED)
this.rejectCallbacks.push(ONREJECTED)
}
if(this.status === MyPromise.FULFILLED) {
setTimeout(() => {
ONFULFILLED(this.result)
})
}
if(this.status === MyPromise.REJECTED) {
setTimeout(() => {
ONREJECTED(this.result)
})
}
})
}
}
之前博客: Vue2响应式原理
const data = {
name: 'wft',
info: {
age: 12
},
list: [1,2,3]
}
// 重写数组方法
const oldArrProto = Array.prototype
const newArrProto = Object.create(oldArrProto)
const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'reverse']
methods.forEach(method => {
newArrProto[method] = function() {
oldArrProto[method].call(this, ...arguments)
console.log('更新视图操作----->>>')
}
})
function observer(target) {
if(typeof target !== 'object' || target === null) {
return target
}
if(target.constructor === Array) {
target.__proto__ = newArrProto
}
for(let key in target) {
if(target.hasOwnProperty(key)) {
defineReactive(target, key, target[key])
}
}
}
function defineReactive(target, key, value) {
if(typeof value === 'object') {
observer(value)
}
Object.defineProperty(target, key, {
get() {
return value
},
set(newVal) {
if(typeof newVal === 'object') {
observer(newVal)
}
if(newVal !== value) {
value = newVal
console.log('更新视图操作----->>>')
}
}
})
}
之前博客: js树形结构,根据里层id找出它所属的每层父级集合
class FindArrsById {
constructor(id, tree) {
this.id = id
this.flatArr = this.flatTreeAndSetLevel.call(this, tree)
this.parentAreas = this.getParentAreas.call(this, this.id, this.flatArr)
this.getParentValuesByKey.bind(this)
}
flatTreeAndSetLevel(tree, level = 0) {
const list = []
tree.forEach(item => {
const o = JSON.parse(JSON.stringify(item))
if(o.children) delete o.children
o.level = level
list.push(o)
if(item.children && item.children.length) {
list.push(...this.flatTreeAndSetLevel(item.children, level + 1))
}
})
return list
}
getParentAreas(pid, list) {
const target = []
let o = list.find(item => item.id == pid) || {}
if(JSON.stringify(o) != '{}') target.push(o)
if(o.parentId) target.push(...this.getParentAreas(o.parentId, list))
return target
}
getParentValuesByKey(key) {
return this.parentAreas.map(item => item[key]).reverse()
}
}
function numberToCurrencyNo(value) {
if (!value) return '0'
if (value === '--') return '--'
value = value - 0
// 将数值截取,保留0位小数
value = value.toFixed(0)
// 获取整数部分
const intPart = Math.trunc(value)
// 整数部分处理,增加,
const intPartFormat = intPart.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
// 预定义小数部分
let floatPart = ''
// 将数值截取为小数部分和整数部分
const valueArray = value.toString().split('.')
if (valueArray.length === 2) { // 有小数部分
floatPart = valueArray[1].toString() // 取得小数部分
return intPartFormat + '.' + floatPart
}
return intPartFormat + floatPart
}
console.log(numberToCurrencyNo(23423456)) //23,423,456
之前博客:js数组去重
export function noRepeatArr(list) {
if (list.every(item => typeof item !== 'object')) {
return [...new Set(list)]
}
return [...new Set(list.map(item => JSON.stringify(item)))].map(item => JSON.parse(item))
}
function myTrim(str) {
if(typeof str !== 'string') return ''
if(str.split('').every(str => str === ' ')) return ''
let start = 0
let end = str.length - 1
for(let i = 0; i < str.length; i++) {
if(str[i] !== ' ') {
start = i
break
}
}
for(let i = str.length - 1; i >= 0; i--) {
if(str[i] !== ' ') {
end = i
break
}
}
return str.slice(start, end + 1)
}
uuid应该后端来生成
function uuid() {
const temp_url = URL.createObjectURL(new Blob())
const uuid = temp_url.toString()
URL.revokeObjectURL(temp_url) //释放这个url
return uuid.substring(uuid.lastIndexOf('/') + 1)
}
// 1. ios
// 2. android
// 3. 其他
function getOSType() {
let u = navigator.userAgent, app = navigator.appVersion;
let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1;
let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
if (isIOS) {
return 1;
}
if (isAndroid) {
return 2;
}
return 3;
}
// 手机号脱敏
function hideMobile(mobile) {
return mobile.replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2")
}
console.log(hideMobile('17812345678')) //178****5678
补充一下
有参数的情况下
function getObjType(obj) {
var toString = Object.prototype.toString
var map = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regExp',
'[object Undefined]': 'undefined',
'[object Null]': 'null',
'[object Object]': 'object'
}
if (obj instanceof Element) {
return 'element'
}
return map[toString.call(obj)]
}