防抖和节流
防抖
function debounce(fn,delay) {
let timer = null;
return (...args)=>{
clearTimeout(timer);
timer = setTimeout(() => {
//改正this,event
fn.apply(this,args)
}, delay);
}
}
节流
//时间戳
const throttle = (fn, delay) => {
let startTime = Date.now();
return (...args)=>{
let curTime = Date.now();
if(curTime-startTime>delay){
fn.apply(this,args);
startTime = curTime;
}
}
}
//定时器
const throttle = (fn, delay) => {
let timer = null;
return (...args)=>{
if(!timer){
timer=setTimeout(()=>{
fn.apply(this,args);
timer=null
},delay)
}
}
}
图片懒加载
我们会把没有出现在视窗内的图片,用一张占位符图片来代替。
<img src='./loading.png' data-src='./真正的图片地址'>
const images = document.querySelectorAll("img");
const observer = new IntersectionObserver((images, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {//出现在视口内
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
}
});
});
images.forEach((image) => {
observer.observe(image);
});
数组
快排
const quickSort = (arr) => {
if(arr.length<2) return arr;
let mid = Math.floor(arr.length/2);
let val = arr.splice(mid,1);
let left=[],right=[];
for(let i of arr){
if(i<val){
left.push(i);
}else{
right.push(i);
}
};
return quickSort(left).concat(val).concat(quickSort(right))
}
数组扁平化
const flatten = (arr) => {
return arr.reduce((pre,cur)=>{
return pre.concat(Array.isArray(cur)?flatten(cur):cur)
},[])
}
数组去重
const unique = arr => [...new Set(arr)]
object的键名必须是string类型或者symbol,map则都可
function unique(arr){
return arr.filter((item,index,arr)=>{
return arr.indexOf(item)==index
})
}
reduce
//不能使用箭头函数
Array.prototype.myreduce = function(fn, initial){
const arr = this;
let total = initial || arr[0];
for (let index = initial ? 0 : 1; index < arr.length; index++) {
total = fn(total, arr[index], index, arr);
};
return total;
};
forEach
遍历需要.call
Array.prototype._forEach = function(fn, thisArg){
const arr = this;
for(let i = 0;i < arr.length;i++){
fn.call(thisArg,arr[i],i,arr);
}
};
map
Array.prototype._map = function(fn, thisArg){
const arr = this;
let res = [];
for(let i = 0;i < arr.length;i++){
res[i]=fn.call(thisArg,arr[i],i,arr);
};
return res;
};
filter
Array.prototype._Filter = function (fn, thisArg) {
if (typeof fn !== 'function') {
throw new Error(`${fn} 不是一个函数`)
}
const arr = this
const filterArr = [] // 没有符合条件的返回空数组
for (let i = 0; i < arr.length; i++) {
const res = fn.call(thisArg, arr[i], i, arr)
if (res) {
filterArr.push(arr[i])
}
}
return filterArr
}
函数
call
Function.prototype.myCall = function(context,...args){
context = context || window;//没有输入就是window
let fn = Symbol('fn');//确保fn唯一性,防止属性覆盖
context[fn] = this;//绑定this
const res = context[fn](...args);
delete context[fn];//删除fn防止污染
return res;//调用函数
}
apply
Function.prototype.myApply=function(context,args){//这里不一样
context = context || window;
let fn = Symbol('fn');
context[fn] = this;
let res = context[fn](...args);
delete context[fn];
return res;
}
bind
返回一个新的函数,但是不会立即执行该函数
Function.prototype._bind = function (context, ...args) {
if (typeof this !== 'function') {
throw new Error("Type Error");
}
// 保存this的值
const _this = this;
return function F() {
// 考虑new的情况
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(context, [...args, ...arguments])
}
}
函数柯里化
function add(a, b, c) {
return a + b + c
}
function curry(fn) {
let judge = (...args) => {
if(args.length == fn.length) return fn(...args);
return (...arg)=>judge(...args,...arg)
};
return judge
}
let addCurry = curry(add)
const res1 = addCurry(1, 2)(3)
const res2 = addCurry(1)(2)(3)
instanceOf
const _instanceOf = (left, right) => {
let proto = left.__proto__;
let prototype = right.prototype;
while (true) {
if (proto == null) return false;
if (proto === prototype) return true;
proto = proto.__proto__;
}
}
发布与订阅
class Observer{
constructor(){
this.message = {}
}
$on(type,callback){
if(!this.message[type]){
this.message[type] = [callback];
}else{
this.message[type].push(callback)
}
}
$off(type,callback){
if(!this.message[type]) return;
if(!callback){
this.message[type] = undefined;
return;
};
this.message[type] = this.message[type].filter(item=>item !== callback);
};
$emit(type){
if(!this.message[type]) return;
this.message[type].forEach(item => {
item()
});
}
}
深拷贝和浅拷贝
浅拷贝
- Object.assign()
- 展开运算符...
- Array.prototype.slice()
深拷贝
-
JSON.parse(JSON.stringify(demo))有缺点:会忽略
undefined/Symbol/function,不能处理正则、new Date(),循环引用对象 -
手撕
function myclone(target,hash = new WeakMap()) {
if(typeof(target)!=='object'||target==null){
return target;
};
if(hash.has(target)) return hash.get(target);
let res = Array.isArray(target)?[]:{}
hash.set(target,res)
for(let key in target){
res[key]=myclone(target[key],hash)
};
return res
}
promise
promise.resolve和reject
Promise.resolve(value)可以将任何值转成值为value&状态是resolve的Promise,但如果传入的Value是promise,则会直接返回。
Promise._resolve = (value)=>{
if(value instanceof Promise){
return value
};
return new Promise(resolve=>resolve(value))
};
Promise.reject(value)也会实例化一个reject状态的promise对象,但是与resolve不同的是,如果给reject传入一个promise对象,这个对象会成为新的Promise的值。
Promise._reject = (value)=>{
return new Promise((resolve,reject)=>reject(value))
};
promise.all
接收一组异步任务,并行执行异步任务,并且在所有异步操作执行完后才执行回调。使用场景:素材较多的应用,打开网页时,预先加载需要用到的各种资源,所有的都加载完成后在进行页面的初始化。Promise.all().then结果中数组的顺序和接收到数组顺序一致。
- 传入的所有 Promsie 都是 resolve,则返回由他们的值组成的,状态为 fulfilled 的新 Promise;
- 只要有一个 Promise 是 rejected,则返回 rejected 状态的新 Promsie,且它的值是第一个 rejected 的 Promise 的值;
- 只要有一个 Promise 是 pending,则返回一个 pending 状态的新 Promise;
Promise._all=(promises)=>{
//返回一个promise的变量
return new Promise((resolve, reject) => {
// 该方法的参数需为一个可迭代对象
if (promises == null || typeof promises[Symbol.iterator] !== 'function') {
throw new error(`${promises} is not a iterable`)
}
//声明变量
let count = 0;
let arr = [];
//遍历
promises.forEach((item,index)=>{
Promise.resolve(item).then((res)=>{
count++;
arr[index] = res;
if(count===promises.length) resolve(arr)
}).catch(reject)
})
})
}
promise.race
接收一组异步任务,并行执行异步任务,只保留第一个执行完成的异步操作结果,其他方法仍在执行,不过结果会被抛弃。all和race传入的数组中如果有抛出异常的任务,只有最先抛出的错误会被(.then的第二个参数或者catch)捕获,但并不会影响数组中其他的异步任务的执行。
Promise._race=(promises)=>{
return new Promise((resolve,reject)=>{
Promise.forEach((item)=>{
Promise.resolve(item).then(res=>{
resolve(res)
}).catch(reject)
})
})
}
promise.allSettled
所有的promise状态都变化了,则返回一个状态是fulfilled的promise,值是一个数组(按照输入顺序)。如果有一个是pending的promise,则返回一个状态是pending的实例。
Promise.MyAllSettled = function (promises) {
let arr = [],count = 0;
return new Promise((resolve, reject) => {
const processResult = (res, index, status) => {
arr[index] = { status: status, val: res }
count += 1
if (count === promises.length) resolve(arr)
}
promises.forEach((item, i) => {
Promise.resolve(item).then(res => {
processResult(res, i, 'fulfilled')
}, err => {
processResult(err, i, 'rejected')
})
})
})
}
列表转树
let arr = [
{ id: 1, name: '部门1', pid: 0 },
{ id: 2, name: '部门2', pid: 1 },
{ id: 3, name: '部门3', pid: 1 },
{ id: 4, name: '部门4', pid: 3 },
{ id: 5, name: '部门5', pid: 4 },
{ id: 6, name: '部门6', pid: 0 },
]
function get_tree(arr) {
const list = []
arr.forEach(element => {
const chiildren_arr = arr.filter(ele => {
return element.id === ele.pid
})
if (chiildren_arr.length > 0) {
element.chiildren = chiildren_arr
}
if (element.pid === 0) {
list.push(element)
}
});
树转列表
const data = [
{
id: 1,
text: '节点1',
parentId: 0,
children: [
{
id: 2,
text: '节点1_1',
parentId: 1
}
]
}
]
function treeToList(data) {
let res = [];
const dfs = (tree) => {
tree.forEach((item) => {
if (item.children) {
dfs(item.children);
delete item.children;
}
res.push(item);
});
};
dfs(data);
return res;
}