阅读 122

前端开发中常用的utils方法

网站安全

xss   跨站脚本攻击

js脚本:注入脚本执行,可以获取你本地的cookie等。

防止:输入过滤、输出编码、cookie防盗(编码、与ip绑定)

csrf  跨站请求伪造

伪造成你的身份执行操作。

人机识别,验证码。

网络请求

一次限制n个fetch且保证最快速度

function limitPromise(callbacks, n){
    const len = callbacks.length;
    let count = 0;
    let result = [];

    return new Promise((resolve,reject)=>{
        try{
            while(count<n){
                fetch();
            }
            function fetch(){
                if(count>=len && result.length===n) return resolve(result);//全部执行完毕
                if(count<n){//执行fetch
                    count++;//记录已执行fetch次数
                    Promise(callbacks[count]()).then(res=>{ result.push(res); fetch(); return res; })
                }
            }
        } catch(err){
            reject(err);
        }
    })
}
复制代码

前端算法

案例一:输出一个树的镜像

queue存储   left/right互换

queue=[root];  
while(queue.length){ 
    r=queue.shift(); 
    r.left=r.right; 
    r.right=r.left;
    queue.push(r.right); 
    queue.push(r.left);
}
root
复制代码

案例二:判断一个树是否是对称树

递归

root===null return true;
compare(left,right){
    !left && !right true;
    !left || !right false;
    left.val!==right.val false;
    return compare(left.left,right.right) && compare(left.right,right.left)
}
compare(root.left,root.right);
复制代码

案例三:t1b t2是否节点重合

双指针,分别走完自己的路并回到对方的起点,终会在我们第一次相交的地方重逢

function getpose(t1, t2){
    const node1=t1;
    const node2=t2;
    while(node1 !== node2){
        if(node1===null || node2===null){return null;}
        node1=node1?node.next:t1;
        node2=node2?node2.next:t2;
    }
    return node1;
}
复制代码

bfs

案例

  <div id="roottag">    <div>      <span>1</span>      <span>2</span>      <div>        <span>3</span>        <span>4</span>      </div>    </div>    <div>      <span>5</span>      <span>6</span>    </div>  </div>

function bfs(root){
    const nodelist=[];
    let queue=[root];
    while(queue.length>0){
        const parent=queue.splice(0,1)[0];
        nodelist.push(parent);
        for(var i=0;i<parent.children.length;i++){
            queue.push(parent.children[i]);
        }
        //if(parent.children) queue=queue.concat(parent.children) children需要转成数组
    }
    return nodelist;
}
复制代码

背包问题

动态规划

要点:

1.确认最后一个问题

2.确认子问题

3.找到状态转移函数 

4.确认计算顺序

计算最大递增子序列的长度  eg: 输入:array[2,3,1,5,6]  输出:4

function maxLengthList(array){
    let dp=[0];
    for(let i=1;i<array.length;i++){
        dp[i]=1;
        for(let j=0;j<i;j++){
            array[i]>array[j] && (dp[i]=Math.max(dp[i] + dp[j]+1));
        }
    }
    return Math.max(...dp);
}
复制代码

回溯

要点:1.路径path  2.终止条件return出递归  3.可选择数组selection

1.从 i 开始尝试 path 路径,直到结尾并记录该路径(res.push([...path]))。

2.回到 i 继续开始其他路径

案例一:输入一个数组,返回其元素组成的所有可能的等长数组的集合

//返回输入array的元素可以组成的所有数组的集合
function backtrack(array){
    let res=[]; //最终返回的集合
    let temppath = []; //存放此次的路径
    function dfs(path){
        if(temppath.length===array.length){ return res.push([...temppath]); }
        //本次路径终止条件

        for(var i=0;i<array.length;i++){
            temppath.push(array[i]);//路径入队一个节点
            dfs(temppath);//从这个节点开始继续走
            temppath.pop();//路径清空
        }
    }
    dfs([]);
    return res;
}
复制代码

案例二:输入一个数组,返回其子集

function sonarray(array) {
    const res = [];
    const path = [];
    function dfs(path, start) {
        if(path.length===array.length) return;//子集小于父集      
        res.push([...path]);//走过的节点都加上      
        for (var i = start; i < array.length; i++) {
            //从当前节点下一个开始        
            if(path.indexOf(array[i])!==-1) continue;        
            path.push(array[i]);        
            dfs(path, i);        
            path.pop();      
        }    
    }    
    dfs(path,0);    
    return res;  
}
复制代码

案例三:输入一个数组,找到数组中元素和为target的组合

function cacula(array, target) {    
    const res = [];    
    const path = [];//路径保存    
    if(target>sum(array)) return 'empty';    
    function sum(array) { 
        if (array.length === 0) return 0; 
        return array.reduce((cur, next) => { return cur + next }) 
    }    //计算总和    
    function deps(path, start) {      
        //终止条件:找到target返回  大于target返回    
        if (sum(path) === target) { res.push([...path]); return; }      
        if (sum(path) > target) return pathTotal = 0;      
        for (var i = start; i < array.length; i++) {        
            path.push(array[i]);        
            console.log(1);        
            deps(path, i + 1);        
            path.pop();      
        }    
    }    
    deps([], 0);    
    return res;  
}
复制代码

反转链表

案例一:将一个链表反转

双指针法

pre cur  {next=cur.next cur.next=pre   pre=cur   cur=cur.next}
cur
复制代码

案例二:截取倒数第K位链表到表尾

双指针法

slow;  fast;   for(i++<k) {fast=fast.next}   while(fase) {slow=slow.next;fase=fase.next;}
slow
复制代码

案例三:合并两个递增链表,合并后不完全递增

head = new nodelist; h=head; 
while(l1 && l2){ l1.val<l2.val head.next=l1 l1=l1.next  head=head.next};
if(l1/l2) head.next=l1/l2;
h
复制代码

单调栈

1.从栈顶到栈底单调递增或递减

2.将不符合单调性得出栈

//直接将任意数组进栈
//实现递增单调栈
function monstack(array){
    const stack = [];
    const len = array.length;
    for(var i=0;i<len;i++){
        //while(stack.length && array[i] > stack[stack.length-1]){ //递增栈
        while(stack.length && array[i] < stack[stack.length-1]){//递减栈
            stack.pop();
            continue;
        }
        stack.push(array[i]);
    }
    return stack;
}
复制代码

冒泡排序

1、i和i+1....len-1依次做对比,放到指定顺序的位置然后继续下两个

2、直到没有需要交换位置的时候停止

function bublesort(array){
    const len = array.length;
    for(var i=0;i<len;i++){
        for(var j=i+1;j<len;j++){
            let cache = 0;
            if(array[i]>array[j]){
                cache = array[i];
                array[i] = array[j];
                array[j] = cache;
            }
        }
    }
    return array;
}
复制代码

快速排序

1.选择一个中间值,从数组中取出该中间值key

2.遍历剩下的数组,将比其大的放到right,比其小的放到right

3.再依次对左边执行快排和右边执行快排。return left.concat([key],right)

function quiksort(array){
    if(array.length<=1) return array;//终止条件
    const index = Math.floor((array.length-1)/2);//基准值index
    const key = array.splice(index,1)[0];//原数组中删除基准值
    let left=[],right = [];
    for(var i=0;i<array.length;i++){
        const item = array[i];
        if(item<=key){left.push(item);}if(item>key){right.push(item);}
    }
    return quiksort(left).concat([key],quiksort(right));
}
复制代码

插入排序

1.将current位与左侧(current-1...0)排序好的做比较,将current放入比他小的元素后面

2.current从1开始

3.如果没有比它小的则插入头部

function insertsort(array){
    const len = array.length;
    for(var i=1;i<len;i++){//从1开始
        const temp = array[i];
        array.splice(i,1);
        for(var j=i-1;j>=0;j--){
            if(array[j]<temp){ array.splice(j+1,0,temp);break;}
            if(j===0){array.splice(0,0,temp);}//找不到比他小的直接插入第一位
        }
    }
    return array;
}
复制代码

二分查找

1.使用以上排序为数组排序。建议quiksort

2.search与array[middle]比较如果search>array[middle]则比较search与middle右侧

3.直到查到或者遍历完成

//key为查找的值
//array为已排序的数组或 array = quiksort(array)
//使用双指针的方法
function binarysearch(key,array){
    const len = array.length;
    let left = 0,right = len-1;
    while(left<=right){
        const middle=Math.floor((left+right)/2);
        if(key===array[middle]){return middle;}
        if(key<array[middle]){right=middle-1;}
        if(key>array[middle]){left=middle+1;}
    }
    return false;
}
复制代码

类型判断

whatType = function(arg){
    //null undefined直接返回对应的string
    if(arg === null || arg === undefined){
        return String(arg);
    }
    //object需要分类   array object
    if(typeof arg === 'object'){
        const name = Object.prototype.toString.call(arg).slice(7); //'[object Array]'
        if(name.includes('Array')){ return 'array'; }
    }
    //其他类型直接使用typeof返回
    return typeof arg;
}
复制代码

深拷贝

要点:

1.如果为基础类型直接返回

2.应用类型分为:array、object需要兼容

const cloneLoop = function(source) {
    let res;
    if(typeof source !== 'object'){ return source; }
    if(typeof source === 'object' && source !== null){
        res = Array.isArray(source) ? [] : {}; //区分当数组 对象
        for(var k in source){//iterator遍历
            if(source.hasOwnProperty(k)){ //排除原型链上的属性
                //子元素为引用类型时,子元素深拷贝
                if(typeof source[k] === 'object'){ res[k] = cloneLoop(source[k]); } 
                else{ res[k] = source[k]; } //终止条件,直接赋值
            }
        }
    }
    return res; //返回本轮深拷贝结果
}
复制代码

防抖节流

要点:

一段时间内只执行一次;每隔一段时间执行一次;都需要存储上一次执行的状态

//防抖 一段时间内只执行一次
const tt = function(fn, delay){
    let timer=null;
    return function(){
        clearTimeout(timer);
        timer=setTimeout(fn,delay);
    }
}

//节流 每隔一段时间执行一次
const ll = function(fn, delay){
    let start=new Date().getTime();
    return function(){
        const end = new Date().getTime();
        if(end-start>delay){
            setTimeout(fn,delay);
            start=end;
        }
    }
}
复制代码

js数据结构

js数据结构之堆栈

1.先入后出

2.操作都在栈顶

3.出栈后的元素依然在栈中:找回删除后的数据

4.可以入栈、出栈、获取栈长度、查看栈顶元素、清空栈

class stack{
    top = 0; //栈顶
    state = [];//使用js的数组模拟栈
    //出栈-栈顶-1
    pop = function(){
        return this.top ?  this.state[--this.top] : 'empty';//出栈后栈顶-1
    }
    //入栈-栈顶+1 且栈顶+1存储新值    
    push = function(arg){
        const top = this.top;
        this.state[top] = arg;
        return ++this.top;
    }
    //返回栈顶的元素
    peek = function(){
        return this.state[this.top-1]; //栈顶在数组最后一位
    }
    //获取当前栈的长度
    getLength = function(){return this.top;}
    //清空栈-当前栈顶为0时
    clear = function(){this.state=[]; this.top=0; return true;}
}
//使用
const stacky = new stack();
stacky.push(1);//....
复制代码

案例1:

判断一个字符串是不是回文

const isRe = function(string){
    if(typeof string !== 'string') return '请输入字符串';
    const pre = new stack();
    for(var i=0 in string){ //假设为12321
        const len = Math.floor(string.length/2); //两段长度为2的字符串
        const middle = string.length%2 === 1 ? len : 0; //中间数index=2
        if(i<len){
            pre.push(string[i]);
        }else{
            if(middle===0 || Number(i)!==middle){//不是中间数/没有中间数
                if(pre.pop()!==string[i]) return false;
            }
        }
    }
    return true;
}
//注意:for in 返回为string
//使用
isRe('')//true
isRe('1')//false
isRe('121');//true
复制代码

案例2:实现数制间的相互转换

要点:

num%base 为当前位数值;Math.floor(num/base)为下一位总数:num=Math.floor(num/base)

const transform = function(num, base){
    const stacknum = new stack();//使用堆栈
    let data=''; //保存转换后的位数
    while(num!==0){//终止条件
        stacknum.push(num%base);
        num=Math.floor(num/base);
    }
    const len = stacknum.getLength();//位数长度
    for(let i=0;i<len;i++){
        data+=String(stacknum.pop());//从最高位抛出
    }
    return Number(data); //返回值转换成数值
}
//使用
ttransform(32,2) //100000
复制代码

js数据结构之列表

1、包含一系列属性和方法

2、基本操作类似数组可直接使用数组封装。笔者不做过多介绍了...

const list = ()=>{
    this.listSize = 0;  //初始化元素个数为0
    this.pos = 0;    //初始化位置为0
    this.dataStore = []; //初始化空数组来保存列表元素
    this.clear = clear;
    this.find = find;  //寻找元素
    this.toString = toString; //显示列表中的元素
    this.insert = insert;
    this.append = append;
    this.remove = remove;
    this.front = front;
    this.end = end;
    this.prev = prev;
    this.next = next;
    this.length = length; //列表中的元素总数
    this.currPos = currPos;
    this.moveTo = moveTo;
    this.getElement = getElement;
    this.contains = contains; //判断给定值是否在列表中
}
复制代码

js数据结构之链表

js链表与数组的区别:数组是需要一块连续的内存空间来存储,对内存的要求比较高。 而链表却相反,它并不需要一块连续的内存空间。链表是通过指针将一组零散的内存块串联在一起。

1.链表的最小单位是元素,一个元素保存其本身以及其下一个元素or其上一个元素

2.只保存下一个元素的链表是单向链表。同时存在上一个和下一个元素的链表为双向链表

3.链表初始化时: 头节点为null,  this.head=null; this.count=0; 可以增删改查.

class Node {
    constructor(element){
        this.element = element;
        this.next = undefined;
    }
}

class LinkedList {
    constructor(){
        this.head = null;
        this.count = 0;
    }
    //往链表尾部添加元素
    push(element){
        const nodes = new Node(element);
        let current=this.head;
        if(this.head===null){
            this.head = nodes;
        }else{
            while(current.next){
                current = this.head.next;
            }
            current.next = nodes;
        }
        this.count++;
    }
    removeAt(position){
        //[position-1].next=[position].next
        let current = this.head; //寻找position-1记为current
        if(position>this.count-1){return false;}
        if(position===0){this.head = this.head.next || null; return true; }
        for(let i=0;i<position-1;i++){
            //position前为空即position位不存在
            current = current.next;
        }
        current.next = current.next.next; //将position-1的next指向position的next
        return true;
    }
    remove(element){
        console.log('12');
        if(this.head.element === element) {
            this.head = this.head.next;
            return true;
        }
        let current = this.head;
        while(current.next){
            if(current.next.element===element){
                current.next=current.next.next; //将element的前一个的next指向element的next
                return true;
            }
            current=current.next;
        }
        return false;
    }
    insert(position, element){
        //0时直接将head替代,之前的head做head.next
        //将[position-1].next = element
        //且element.next = [position-1].next(记录position的next)
        element = new Node(element);
        let next;
        if(position===0){ 
            next = this.head; 
            this.head = element;
            this.head.next = next; 
            return true;
        }
        let current = this.head;
        for(let i=0; i < position-1; i++){
            if(current === undefined && i<position-1){ return false;}
            current=current.next;
        }
        next = current.next;
        current.next = element;
        element.next = next;
        this.count++;
        return true;
    }
}
复制代码

js数据结构之队列

1. 先进先出

2. 入队在队尾,出队在队头

class Queue {
    #data=[]; //私有数组变量存储队列内容
    enqueue(item){ //队尾插入
        this.#data.push(item);
        return true;
    }
    dequeue(){ //队首出队
        if(!this.#getLength()) return 'empty';
        this.#data.reverse().pop(); //shifit为O(n)避免
        this.#data.reverse(); //b反转
        return true;
    }
    //查看队首元素
    front(){   if(this.isEmpty()) return 'empty'; return this.#data[0];   }
    //查看队尾元素
    back(){   if(this.isEmpty()) return 'empty'; return this.#data[this.#getLength()-1];   }
    //清空队列    
    clear(){   this.#data = [];   }
    //查看队列内容
    toString(){ return this.#data.join()}
    //判断是否为空
    isEmpty(){ return !this.#data.length; }
    //私有变量内部使用函数,返回队列的长度
    #getLength(){
        return this.#data.length;
    }
}
复制代码

js数据结构之树

二叉树   满二叉树   完全二叉树

1.每个根节点只有两个子节点:root left right

2.节点之间有顺序:left<root<right

二叉树存储方式:

//链式存储
class Node {
    current: '',
    left: null,
    right: null
}
//顺序存储  使用数组 从i=1    123  完全二叉树使用此最省存储空间
root=i;
left=2*i;
right=2*i + 1;
复制代码

遍历:前 中 后

前序

1、root出发

2、root.left.left不存在时  再root.right

//前  root   left  right
const preOrder=(root)=>{
    let stack = [];
    r(root);
    return order;

    function r(root){
        if(root) return;
        stack.push(root.val);
        root.left && r(root.left); //遍历左子树
        root.right && r(root.right); //遍历右子树
    }
}
//中 left root right
const centerOrder=(root)=>{
    const stack=[];
    r(root);
    return stack;

    function r(root){
        if(root) return;
        root.left && r(root.left);
        stack.push(root.val);
        root.left && r(root.right);
    }
}
//后 left right root
const nextOrder=(root)=>{
    const stack=[];
    r(root);
    return stack;
    
    function r(root){
        if(!root) return;
        root.left && r(root.left);
        root.right && r(root.right);
        stack.push(root);
    }
}
//层次遍历  类似广度优先
function levelOrder() {
    if (this.root == null) return '';
    let a = [],
        left, right;
    a.push(this.root);

    // 节点入队,指针指向头部元素,如果它有left/right,入队。
    // 指针后移,继续同样步骤。。。直至指针跑到队列尾部后面去。。。
    for(let i = 0; i < a.length; i++) {     // 需要注意的是,数组 a 的长度是动态变化的(不停变长)
        left = a[i].left;
        if (left) a.push(left);

        right = a[i].right;
        if (right) a.push(right);
    }
    return a.map(item => item.val).join(',');
}
复制代码

dfs bfs

深度遍历:  广度遍历:

//BFS bread-first-search
function bfs(root){
    const stack=[]; //打印出树的所有节点信息
    const queue = [];
    queue=[root];
    while(queue.length>0)
        stack.push(queue.splice(0,1)[0]);
        root.children && queue=queue.concat(root.children);
    }
}
//DFS depth-first-search
//递归
function dfs(root){
    const nodelist=[];
    function r(root){
        if(!root) return;
        nodelist.push(root);
        for(leti=0;i<root.children.length;i++){
            r(root.children[i]);
        }
    }
    return nodeList;
}
//非递归
//栈--shifi成本太高了(O(n))
function dfs(root){
    const nodelist=[];
    const queue=[];
    queue=[root];
    while(queue.length>0){
        rootitem=queue.pop();//后出
        nodelist.push(rootitem);
        for(let i=rootitem.children.length-1;i>0;i--){//右侧-先入 保证左侧先执行
            queue.push(rootitem.children[i]);
        }
    }
}
复制代码

设计模式

观察者模式 and 发布订阅模式

要点:

1.观察者模式多个观察者对应一个被观察者,两者直接关联。被观察者提供绑定和触发功能,观察者使用其绑定功能

2.发布订阅中可以多对多,且引入一个中间数据管理者,两个对象只对数据管理中心做发布与订阅操作

观察者模式图:(来自知乎)

发布订阅模式图:(来自知乎)

//观察者模式
//多个观察者和一个被观察者
class Subject{
    msg=[];
    subscribe(callback){
        if(this.msg.includes(callback)) return false;
        this.msg.push(callback);
    }
    publish(args){
        if(this.msg.length===0) return;
        this.msg.map(handle => handle.apply(null, args))
    }
}
const subject = new Subject(); //被观察者
//两个观察者
const observer1 = subject.subscribe((...args)=>{console.log('观察者1号观察到变化:'+args);});
const observer2 = subject.subscribe((...args)=>{console.log('观察者2号观察到变化:'+args)});
subject.publish('hahaha'); //被观察者发布变化


//发布订阅模式
//实现一个观察者中心
//订阅者通过中心订阅事件
//数据源通过中心发布改变
//中心触发订阅者更新
class EventCenter{
    msg = {}; //储存所有观察信息
    emit(event,...args){
        //事件触发
        if(!this.msg[event]) return false;
        this.msg[event].forEach(item=>{ item.apply(null, args) });
    }
    on(event, callback){
        //事件注册
        if(!this.msg[event]){ this.msg[event] = [callback]; return true;}
        this.msg[event].push(callback);
    }
    remove(event, callback){
        //事件移除
        if(!this.msg[event]) return false;
        this.msg[event].filter(item=>item !== callback)
    }
    once(event,callback){
        //注册单次执行事件
        //单次执行即 执行后remove
        const wrapFunc = (...args) => {
            callback.apply(args);
            this.off(event, callback);
        }
        this.on(event, callback);
    }
}
const manerger = new EventCenter();//创建管理中心
const observer1 = (arg) => {console.log(111+arg)}
const observer2 = (arg) => {console.log(222+arg)}
//发布者
const publisher = (()=>{
    return {update1: ()=>{
        manerger.emit('update', observer1);
        manerger.emit('update', observer2);
    }}
})()
//订阅者
const subscriber=(()=>{
    return {update: ()={
        ob.on('update', observer1);
        ob.on('update', observer2);
    }}
})()
subscriber.update();//发布
publisher.update1('订阅被触发啦!');//订阅触发
复制代码

未完待续...

文章分类
前端
文章标签