尚硅谷 https://www.bilibili.com/video/BV1Cy4y117vt?p=7
function call(Fn, obj, ...args) {
if (obj === undefined || obj === null) {
obj = globalThis // 全局对象
}
obj.temp = Fn
let result = obj.temp(...args)
delete obj.temp // 删除temp方法
return result
}
function apply(Fn, obj, args) {
if (obj === undefined || obj === null) {
obj = globalThis // 全局对象
}
obj.temp = Fn
let result = obj.temp(...args)
delete obj.temp
return result
}
function bind(Fn, obj, ...args) {
return function(...arg2) {
return call(Fn, obj, ...args, ...args2)
}
}
function debounce(fn, delay) {
var timer = null
return function() {
if (timer) {
clearTimeout(timer)
}
let _this = this
let _arguments = arguments
timer = setTimeout(function() {
fn.apply(_this, _arguments)
}, delay)
}
}
function throttle(fn, interval) {
var start = 0
return function() {
let _this = this
let _arguments = arguments
let now = new Date().getTime()
if (now - start > interval) {
fn.apply(_this, _arguments)
lastTime = newTime
}
};
}
function map(arr, fn) {
let result = []
for(let i=0; i<arr.length; i++) {
result.push(fn(arr[i], i)) // 传入的i是下标
}
return result
}
function reduce(arr, fn, initValue) {
let result = initValue
for(let i=0; i<arr.length; i++) {
result = fn(result, arr[i])
}
return result
}
function filter(arr, fn) {
let result = []
for(let i=0; i<arr.length; i++) {
let res = fn(arr[i], i) // 传入的i是下标
if (res) {
result.push(arr[i])
}
}
return result
}
function find(arr, fn) {
for(let i=0; i<arr.length; i++) {
let res = fn(arr[i], i) // 传入的i是下标
if (res) {
return arr[i]
}
}
return undefined
}
function find(arr, fn) {
for(let i=0; i<arr.length; i++) {
let res = fn(arr[i], i) // 传入的i是下标
if (res) {
return i
}
}
return -1
}
every():如果数组中的每个元素都满足测试函数,则返回true ,否则返回false。some():如果数组中至少有一个元素满足测试函数,则返回true,否则返回false。
function every(arr, fn) {
for(let i=0; i<arr.length; i++) {
if(!fn(arr[i], i)) { // 传入的i是下标
return false
}
}
return true
}
function some() {
for(let i=0; i<arr.length; i++) {
if(fn(arr[i], i)) { // 传入的i是下标
return true
}
}
return false
}
// 1.forEach()和indexOf():本质是双重遍历,效率低
function unique(arr) {
let result = []
arr.forEach(i => {
if(result.indexOf(i) === -1) {
result.push(i)
}
})
return result
}
// 2.forEach() + 对象容器:只需一重遍历,效率高
function unique2(arr) {
let result = []
let obj = {} // 用于快速判断元素是否已经存在
arr.forEach(i => {
if (obj[i] === undefined) {
obj[i] = true
result.push(i)
}
})
return result
}
// 3.利用ES6语法 from + Set:编码简洁
function unique3(arr) {
let set = new Set(arr)
let array = [...set]
return array
// 简洁:return [...new Set(arr)]
}
function concat(arr, ...args) {
let result = [...arr]
args.forEach(item => {
if(Array.isArray(item)) {
result.push(...item)
} else {
result.push(item)
}
})
return result
}
function slice(arr, start, end) {
// 优化
if (arr.length === 0) {
return []
}
start = start || 0
if (start >= arr.length) {
return []
}
end = end || arr.length
if (end < start) {
end = arr.length
}
// 本体
let result = []
for(let i=0; i<arr.length; i++) {
if (i >= start && i < end) {
result.push(arr[i])
}
}
return result
}
// 将[1,2,[3,4,[5,6]],7]变为[1,2,3,4,5,6,7]
function flatten1(arr) {
let result = []
arr.forEach(item => {
if(Array.isArray(item)) {
result.concat(flatten(item)) // 递归
} else {
result = result.concat(item)
}
})
return result
}
function flatten2(arr) {
let result = [...arr]
while(result.some(item => Array.isArray(item))) {
result = [].concat(...result)
}
return result
}
// [1,2,3,4,5,6] =chunk(arr, 4)=> [[1,2,3,4],[5,6]]
function chunk(arr, size=1) {
let result = []
let temp = []
arr.forEach(item => {
if(temp.length === 0) {
result.push(temp)
}
temp.push(item)
if(temp.length === size) {
temp = []
}
})
return result
}
// difference([1,3,5,7],[5,8]) ==> [1,3,7]
function difference(arr1, arr2=[]) {
// 优化
if(arr1.length === 0) {
return []
}
ir(arr2.length === 0) {
return arr1.slice()
}
// 本体
let result = arr1.filter(item => !arr2.includes(item))
return result
}
// pull([1,3,5,7],2,7,3,7) ==> 原数组变为[1,5],返回值为[3,3,7]
function pull(arr, ...args) {
let result = [] // 保存删除的元素
for(let i=0; i<arr.length; i++) {
if(args.includes(arr[i])) {
result.push(arr[i])
arr.splice(i, 1)
i--
}
}
return result
}
// pullAll([1,3,5,3,7],[2,7,3,7]) ==> 数组变为[1,5],返回值为[3,3,7]
function pullAll(arr, values) {
retur pull(arr, ...values)
}
// drop(array,count) 得到当前数组过滤掉左边count个元素后的数组 drop([1,3,5,7],2) ==> [5,7]
function drop(arr, size) {
// return arr.filter((value, index) => {
// return index >= size
// })
return arr.filter((value, index) => index>=size)
}
// dropRight(array,count) 得到当前数组过滤掉右边count个元素后的数组 drop([1,3,5,7],2) ==> [1,3]
function dropRight(arr, size) {
// return arr.filter((value, index) => {
// return index < arr.length - size
// })
return arr.filter((value, index) => index<arr.length-size)
}
// let obj = newInstance(Person, '张三', 18) 相当于new关键字的功能
function newInstance(Fn, ...args) {
// 1.创建一个新对象
let obj = {}
// 2.修改函数内部this指向新对象 并执行
let result = Fn.call(obj, ...args)
// 修改新对象的原型对象
obj.__proto__ = Fn.prototype
// 3.返回新对象
return result instanceof Object ? result : obj
}
// 用于判断obj是否是Type类型的实例
function instanceOf(obj, Fn) {
// 获取函数的显式原型
let prototype = Fn.prototype
// 获取obj的隐式原型对象
let proto = obj.__proto__
// 遍历原型链
while(proto) {
// 检测原型对象是否相等
if(prototype === proto) {
return true
}
// 如果不等于
proto = proto.__proto__
}
return false
}
// 有重名的属性,则合并而不是覆盖
function mergeObject(...objs) {
// 声明一个空对象
let result = {}
// 遍历所有的参数对象
objs.forEach(obj => {
// 获取当前对象所有的属性
Object.keys(obj).forEach(key => {
// 检测 result 中是否存在 key 属性
if(result.hasOwnProperty(key)) {
result[key] = [].concat(result[key], obj[key])
} else {
// 如果没有,则直接写入
result[key] = obj[key]
}
})
})
return result
}
function clone1(target) {
if(typeof target === 'object' && target !== null) {
if(Array.isArray(target) {
return [...target]
} else {
return {...target}
}
} else {
return target
}
}
function clone2(target) {
if(typeof target === 'object' && target !== null) {
let result = Array.isArray(target) ? [] : {}
for(let key in target) {
if(target.hasOwnProperty(key)) {
result[key] = target[key]
}
}
return result
} else {
return target
}
}
// 问题1:函数属性会丢失
// 问题2:循环引用会出错
function deepClone1(target) {
let str = JSON.stringfy(target)
let data = JSON.parse(str)
return data
}
// 解决问题1:函数属性会丢失
function deepClone2(target) {
if(typeof target === 'object' && target !== null) {
let result = Array.isArray(target) ? [] : {}
for(let key in obj) {
// 检测该属性是否为对象本身的属性(不能拷贝原型对象的属性)
if(target.hasOwnProperty(key)) {
// 拷贝
result[key] = deepClone2(target[key]) // 递归
}
}
return result
} else {
return target
}
}
// 解决问题2:循环引用会出错
function deepClone3(target, map=new Map()) {
if(typeof target === 'object' && target !== null) {
// 克隆数据之前,进行判断,数据之前是否克隆过
let cache = map.get(target) // map用于判断是否已经克隆过
if(cache) {
return cache
}
let result = Array.isArray(target) ? [] : {}
// 将新的结果存入到容器中
map.set(target, result)
for(let key in obj) {
// 检测该属性是否为对象本身的属性(不能拷贝原型对象的属性)
if(target.hasOwnProperty(key)) {
// 拷贝
result[key] = deepClone3(target[key], map) // 递归
}
}
return result
} else {
return target
}
}
// 优化遍历性能
function deepClone4(target, map=new Map()) {
if(typeof target === 'object' && target !== null) {
// 克隆数据之前,进行判断,数据之前是否克隆过
let cache = map.get(target) // map用于判断是否已经克隆过
if(cache) {
return cache
}
let isArray = Array.isArray(target)
let result = isArray ? [] : {}
// 将新的结果存入到容器中
map.set(target, result)
if(isArray) {
target.forEach((item, index) => {
result[index] = deepClone4(item, map)
})
} else {
// 如果是对象,获取所有的键名,再forEach遍历
Object.keys(target).forEach(keys => {
result[key] = deepClone4(target[key], map)
})
}
return result
} else {
return target
}
}
function reverseString(str) {
let arr = [...str] // 将字符串转为数组
arr.reverse() // 反转数组
let s = arr.join('') // 将数组拼接成字符串
return s
}
function palindrome(str) {
return reverseString(str) === str
}
function truncate(str, length) {
return str.slice(0, length) + '...'
}
function addEventListener(el, type, fn, selector) {
if(typeof el === 'string') {
el = document.querySelector(el)
}
// 事件绑定
// 若没有传递子元素的选择器,则给 el 元素绑定事件
if(!selector) {
el.addEventListener(type, fn)
} else {
el.addEventListener(type, function(e) {
// 获取点击的目标事件源
let target = e.target
// 判断选择器与目标元素是否相符合
if(target.matches(selector)) {
// 若符合,则调用回调
fn.call(target, e)
}
})
}
}
let eventBus = {
// 保存类型与回调的容器
callbacks: {
login: [fn, fn2]
}
}
// 绑定事件
eventBus.on = function(type, callback) {
// 回调
if(this.callbacks[type]) {
// 如果 callbacks 属性中存在该类型事件
this.callbacks[type].push(callback)
} else {
// 如果 callbacks 属性中不存在该类型事件
this.callbacks[type] = [callback]
}
}
// 触发事件
event.emit = function(type, data) {
if(this.callbacks[type] && this.callbacks[type].length) {
// 遍历数组
this.callbacks[type].forEach(callback => {
callback(data)
})
}
}
// 解绑事件
eventBus.off = function(eventName) {
// 若传入了eventName事件类型
if(eventName) {
// 只删除eventName对应的事件回调
delete this.callbacks[eventName]
} else {
this.callbacks = {}
}
}
let PubSub = {
id: 1, //订阅唯一id
callbacks: {} // 频道与回调保存容器
}
// 订阅频道
PubSub.subscribe = function(channel, callback) {
// 创建唯一的编号
let token = "token_" + this.id++
// pay token_1
if(this.callbacks[channel]) {
this.callbacks[channel][token] = callback
} else {
this.callbacks[channel] = {
[token]: callback
}
}
// 返回频道订阅的id
return token
}
// 发布消息
PubSub.publish = function(channel, data) {
// 获取当前频道中所有的回调
if(this.callbacks[channel]) {
Object.values(this.callbacks[channel]).forEach(callback => {
callback(data)
})
}
}
// 取消订阅
/*
1.没有传值,flag 为 undefined
2.传入token字符串
3.msgName字符串
*/
PubSub.publish = function(flag) {
// 如果flag为undefined 则清空所有订阅
if(flag === undefined) {
this.callbacks = {}
} else if(typeof flag === 'string') {
// 判断是否为 token_ 开头
if(flag.indexOf('token_') === 0) {
// 如果是 标明是一个订阅id
let callbackObj = Object.values(this.callbacks).find(obj => obj.hasOwnProperty(flag))
if(callbackObj) {
delete callbackObj[flag]
}
} else {
// 表明是一个频道的名称
delete this.callbacks[flag]
}
}
}
/* 函数封装 */
function axios({method, url, params, data}) {
// 转换为大写
method = method.toUpperCase()
return new Promise((resolve, reject) => {
// 1.创建对象
const xhr = new XMLHttpRequest()
// 2.初始化
// 处理 params 对象
let str = ''
for(let k in params) {
str += `${k}=${params[k]}&`
}
str = str.slice(0, -1)
xhr.open(method, url+'?'+str)
// 3.发送
if(method === 'POST' || method === 'PUT' ||| method === 'DELETE') {
// Content-type mime类型设置
xhr.setRequestHeader('Content-type', 'application/json')
//设置请求体
xhr.send(JSON.stringfy(data))
} else {
xhr.send()
}
// 设置响应结果的类型为 JSON
xhr.responseType = 'json'
// 4.处理结果
xhr.onreadystatechange = function() {
//
if(xhr.readyState === 4) {
// 判断响应状态吗
if(xhr.status >= 200 && xhr.status < 300) {
// 成功的状态
resolve({
status: xhr.status,
message: xhr.statusText,
body: xhr,response
})
} else {
// 失败的状态
reject(new Error('请求失败,失败的状态码为' + xhr.status))
}
}
}
xhr.send()
})
}
/* 方法封装 */
axios.get = function(url, options) {
// 发送 AJAX 请求 GET
return axios(Object.assign(options, {method:'GET', url: url}))
}
axios.post = function(url, options) {
// 发送 AJAX 请求 GET
return axios(Object.assign(options, {method:'POST', url: url}))
}
axios.delete = function(url, options) {
// 发送 AJAX 请求 GET
return axios(Object.assign(options, {method:'DELETE', url: url}))
}