中高级前端必须掌握的手写代码

465 阅读7分钟

事件订阅发布

class EventEmitter {
    constructor() {
        this._events = {}
    }
    on(evtName, handler) {
        if (!this._events) this._events = {}
        if (this._events[evtName]) {
            this._events[evtName].push(handler)
        } else {
            this._events[evtName] = [handler]
        }
    }

    emit(evtName, data) {
        if (!this._events) this._events = {}
        if (this._events[evtName]) {
            this._events[evtName].forEach(fn => fn(data))
        }
    }

    off(evtName, handler) {
        if (!this._events) this._events = {}
        if (this._events[evtName]) {
            this._events[evtName] = this._events[evtName].filter(fn => fn !== handler &&fn.l!==handler)
        }
    }
//只触发一次,就卸载
    once(evtName, handler) {
        const once=(data)=>{
           handler(data)
           this.off(evtName,once)
        }
        once.l=handler
        this.on(evtName,once)

    }
}
//设置最大监听值
class EventEmitter {
    constructor() {
        this._events = {}
        this.maxListeners=maxListeners||Infinity
    }
    on(evtName, handler) {
        if (!this._events) this._events = {}
        ifthis.maxListener!+=Infinity&&this.events[event].length>=this.maxListeners){
        console.warn(`当前事件${event}超过最大监听数`)
        }
        if (this._events[evtName]) {
            this._events[evtName].push(handler)
        } else {
            this._events[evtName] = [handler]
        }
    }

    emit(evtName, data) {
        if (!this._events) this._events = {}
        if (this._events[evtName]) {
            this._events[evtName].forEach(fn => fn(data))
        }
    }

    off(evtName, handler) {
        if (!this._events) this._events = {}
        if (this._events[evtName]) {
            this._events[evtName] = this._events[evtName].filter(fn => fn !== handler &&fn.l!==handler)
        }
    }
//只触发一次,就卸载
    once(evtName, handler) {
        const once=(data)=>{
           handler(data)
           this.off(evtName,once)
        }
        once.l=handler
        this.on(evtName,once)

    }
}

函数柯里化

记录每次调用时传入的参数,并且和函数的参数个数进行比较,如果不满足个数就返回新函数,如果传入的个数和参数一致,执行原来的函数。

function curring(fn){
 const inner=(...args)=>{
 	return args.length>=fn.length?fn(...args):(...userArgs)=>inner(...args,...userArgs);
 }
 return inner();
}
实现连续add函数
function Add() {
    const nums = [...arguments];
    function AddPro() {
        nums.push(...arguments);
    return AddPro;
    }
    AddPro.sumOf = () => {
        return nums.reduce((a, b) => a + b);
    }
    return AddPro;
}

compose

function compose(...fn){
	if(!fn.length)return v=>v;
	if(fn.length===1)return fn[0];
	return fn.reduce((acc,cur)=>(...args)=>acc(cur(...args)))
}

Promise

乞丐版


const PENDING="PENDING";
const FULFILLED="FULFILLED";
const REJECTED="REJECTED";

class Promise{
 constructor(executor){
 	this.status=PENDING;
 	this.value=undefined;
 	this.reason=undefined;
 	this.onResolvedCallbacks=[];
 	this.onRejectedCallbacks=[];
 	const resolve=(value)=>{
 		if(this.status===PENDING){
 			this.value=value;
 			this.status=FULFILLED;
 			this.onResolvedCallbacks.forEach(fn=>fn())
 		}

 	}
 	const reject=(reason)=>{
 		if(this.status===PENDING){
 			this.reason=reason;
 			this.status=REJECTED;
 			this.onRejectedCallbacks.forEach(fn=>fn())
 		}
 	}
 	try{
      executor(resolve,reject)
 	}catch(e){
      reject(e)
 	}
 }
 then(onFulfilled,onRejected){
   if(this.status===PENDING){
   	this.onResolvedCallbacks.push(

   	()=>{
   		onFulfilled(this.value)
   	})
   this.onRejectedCallbacks.push(

   	()=>{
   			onRejected(this.reason)
   	})
   }
   if(this.status===FULFILLED){
   	onFulfilled(this.value)
   }
   if(this.status===REJECTED){
   	onRejected(this.reason)
   }
 }
}

promiseA+版

实现思路

  • 普通的promise
  • promise 执行器传入异步代码 callbacks队列保存事件
  • promise.then 执行也可能异常 所以都包上try catch
  • then链式调用返回promise2 为了拿到primose2 加上setTimeout
  • 处理resolve(x) x可能是promise
  • resolvePromise
  1. 返回的x=promise2 reject(new TypeError('error'));
  2. 返回的是promise
  3. 返回的不是promise
  4. called 兼容别人写的promise 保证状态只改变一次
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';


// 利用x的值来判断是调用promise2的resolve还是reject
function resolvePromise(promise2, x, resolve, reject) {
    // 核心流程
    if (promise2 === x) {
        return reject(new TypeError('错误'))
    }
    // 我可能写的promise 要和别人的promise兼容,考虑不是自己写的promise情况
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') { // 有可能是promise 
        // 别人的promise可能调用成功后 还能调用失败~~~  确保了别人promise符合规范
        let called = false;
        try { // 有可能then方法是通过defineProperty来实现的 取值时可能会发生异常
            let then = x.then;
            if (typeof then === 'function') {
                // 这里我就认为你是promise了  x.then 这样写会触发getter可能会发生异常  
                then.call(x, y => {
                    if (called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject); // 直到解析他不是promise位置
                }, r => { // reason
                    if (called) return;
                    called = true;
                    reject(r);
                });
            } else { // {}  {then:{}}
                resolve(x); // 常量
            }
        } catch (e) {
            if (called) return;
            called = true;
            reject(e);
        }
    } else {
        resolve(x); // 说明返回的是一个普通值 直接将他放到promise2.resolve中
    }
}

class Promise {
    constructor(executor) {
        this.status = PENDING; // promise默认的状态
        this.value = undefined; // 成功的原因
        this.reason = undefined; // 失败的原因
        this.onResolvedCallbacks = []; // 存放成功的回调方法
        this.onRejectedCallbacks = []; // 存放失败的回调方法
        const resolve = (value) => { // 成功resolve函数
            if(value instanceof Promise){
                return value.then(resolve,reject)
            }
            if (this.status === PENDING) {
                this.value = value;
                this.status = FULFILLED; // 修改状态
                // 发布
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        const reject = (reason) => { // 失败的reject函数
            if (this.status === PENDING) {
                this.reason = reason;
                this.status = REJECTED // 修改状态
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    // then中的参数是可选的
    then(onFulfilled, onRejected) { // onFulfilled, onRejected
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; }
        // 用于实现链式调用
        let promise2 = new Promise((resolve, reject) => {
            // 订阅模式
            if (this.status == FULFILLED) { // 成功调用成功方法
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.value);
                        // 此x 可能是一个promise, 如果是promise需要看一下这个promise是成功还是失败 .then ,如果成功则把成功的结果 调用promise2的resolve传递进去,如果失败则同理

                        // 总结 x的值 决定是调用promise2的 resolve还是reject,如果是promise则取他的状态,如果是普通值则直接调用resolve
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            }
            if (this.status === REJECTED) { // 失败调用失败方法
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);

            }
            if (this.status == PENDING) { // 代码是异步调用resolve或者reject的
                this.onResolvedCallbacks.push(() => { // 切片编程 AOP
                    setTimeout(() => {
                        try {
                            // todo...
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);

                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            // todo...
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
            }
        })
        return promise2
    }
    static resolve(value){
        return new Promise((resolve,reject)=>{
            resolve(value);
        })
    }
    static reject(value){
        return new Promise((resolve,reject)=>{
            reject(value);
        })
    }
    catch(errorFn){
        return this.then(null,errorFn)
    }
}
// npm install promises-aplus-tests -g


// 延迟对象 帮我们减少一次套用 : 针对目前来说 应用不是很广泛
Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise((resolve,reject)=>{
        dfd.resolve= resolve;
        dfd.reject = reject;
    }); 
    return dfd;
}

Promise.all

Promise.all = function(promises) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises)) return reject(new Error("传入参数必须是数组"))
        let result = [];
        let times = 0;
        const processSuccess = (index,val)=>{
            result[index] = val;
            if(++times === promises.length){
                resolve(result)
            }
        }
        for (let i = 0; i < promises.length; i++) { // 并发 多个请求一起执行的
            let p = promises[i]
            if(p && typeof p.then === 'function'){
                p.then((data)=>{
                    processSuccess(i,data)
                },reject); // 如果其中某一个promise失败了 直接执行失败即可
            }else{
                processSuccess(i,p)
            }
        }
    });
}
function myPromiseAll(promises) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises)) return reject(new Error("传入参数必须是数组"))
        let res = [],
            count = 0,
            len = promises.length;
        for (let i = 0; i < len; i++) {
            Promise.resolve(promises[i]).then(val => {
                count++;
                res[i] = val;
                if (count === len) {
                    resolve(res)
                }
            }).catch(e => reject(e))
        }
    })
}

function myPromiseAll(promises) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(promises)) return reject(new Error("传入参数必须是数组"))
        let res = [],
            count = 0,
            len = promises.length;
        const processSuccess = (index, val) => {
            res[index] = val
            if (++count == len) {
                resolve(res)
            }
        }
        for (let i = 0; i < len; i++) {
            let p = promises[i]
            Promise.resolve(p).then(val => processSuccess(i, val), reject)
        }

    })
}

Promise.race

Promise.race=function(promises){
	return new Promise((resolve,reject)=>{
		for(let i=0;i<promises.length;i++){
			let p=promises[i]
			if(p && typeof p.then === 'function'){
			p.then(resolve,reject)

		}else{
			resolve(p)
		}
		}
	})
}

可以abort的promise

function warp(p1){
	let p=new Promise((resolve,reject)=>{abort=reject})
	let p2=Promise.race([p,p1])
	p2.abort=abort;
	reject p2;
}

Promise.finally

Promise.finally=function(cb){
	return this.then((data)=>{
			return Promise.resolve(cb()).then(()=>data)
		},()=>{
			return Promise.resolve(cb()).then(()=>throw err)
		}		)
}

promiseify

function promiseify(fn) {
	return function(...args) {
		return new Promise((resolve, reject) => {
			fn(...args, (err, data) => {
				if (err) return reject(err)
				resolve(data)
			}
		})
	}
}

function promiseifyAll(obj){
	let o={};
	for(key in obj){
		if(typeof obj[key]==='function'){
			o[key+'Promise']=promiseify(o[key])
		}
	}
	return o;
}

promise实现并发限制

function limitLoad(urls, handler, limit) {
    const sequence = [].concat(urls);
    let promises = [];
    promises = sequence.splice(0, limit).map((url, index) => {
        return handler(url).then(() => {
            return index
        })
    });
    let p = Promise.race(promises);
    for (let i = 0; i < sequence.length; i++) {
        p = p.then((res) => {
            promises[res] = handler(sequence[i]).then(() => {
                return res;
            })
            return Promise.race(promises)
        })
    }
}

promise实现串行

function executePromises(createPromises) {
  var result = Promise.resolve();
  createPromises.forEach(function (createPromise) {
    result = result.then(createPromise);
  });
  return result;
}
function createPromise() {
  return new Promise((resolve)=>{
      doSomthing()
      resolve()
  });
}

const promiseChain=promiseList.reduce((acc,cur)=>{
   return acc.then(cur)
},Promise.resolve())

sleep

function sleep(time){
   	return new Promise((resolve,reject)=>{
   		settimeout(()=>{resolve()},time)
   	})
   }

按顺序输出queue

class Queue{
   constructor(){
       this.queue = [];
   }

   task(delay, callback){
       this.queue.push({
           delay,
           callback
       });
       return this;
   }

   async start(){
       for(let i=0;i<this.queue.length;i++){
           await new Promise((resolve)=>{
               setTimeout(resolve, this.queue[i].delay)
           }).then(()=>{
               this.queue[i].callback();
           })
       }
   }
}

防抖和节流

debounce

function debounce(func, wait, immediate) {
	let timeout, result;
	let debounced = function() {
		if (timeout) {
			clearTimeout(timeout);
		}
		if (immediate) {
			let callNow = !timeout;
+			if (callNow) result = func.apply(this, arguments);

		}

		timeout = setTimeout(() => {
			func.apply(this, arguments);
+			timeout = null;
		}, wait);
		return result;
	}
	debounced.cancel = function() {
		clearTimeout(timeout);
		timeout = null;
	};
	return debounced;
}

throttle

  1. 先写一个普通的throttle
  2. now-previous>wait func执行 previous=now
  3. 加上trailing 最后一次也触发 (默认触发)
  4. later setTimeout 清除定时器
  5. 再加上leading==false 延迟第一次点击吧,不要马上触发
  6. 让第一次走setimeout的逻辑 previous = now
  7. later那里判断leading==false 0
function(func, wait, options) {
	let timeout, context, args, result;
	let previous = 0;
	if (!options) options = {};
	let later = function() {
+		previous = options.leading === false ? 0 : Date.now();
		result = func.apply(context, args);
                if (!timeout) context = args = null;
	};

	let throttled = function() {
		context = this;
		args = arguments;
		let now = Date.now();
+		if (!previous && options.leading === false) previous = now;
		let remaining = wait - (now - previous);
		if (remaining <= 0) {
			if (timeout) {
				clearTimeout(timeout);
				timeout = null;
			}
			previous = now;
			result = func.apply(context, args);
+		} else if (!timeout && options.trailing !== false) {
			timeout = setTimeout(later, remaining);
		}
		return result;
	};

	throttled.cancel = function() {
		clearTimeout(timeout);
		 timeout = context = args = null;
	};

	return throttled;
};

co

function co(it) {
	return new Promise((resolve, reject) => {
		function next(data) {
			let {
				value,
				done
			} = it.next(data);
			if (done) {
				resolve(value)
			} else {
				Promise.resolve(value).then(next, reject)
			}
		}
		next();

	})
}

链表


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

}
class LinkedList {
	constructor() {
		this.head = null;
		this.size = 0;
	}

	_node(index) {
		if (index < 0 || index > this.size) throw new Error('越界');
		let current = this.head;
		for (let i = 0; i < index; i++) {
			 current = current.next
		}
		return current
	}
	add(index, element) {
		if (arguments.length === 1) {
			element = index
			index = this.size
		}
		if (index < 0 || index > this.size) throw new Error('越界');
		if (index === 0) {
			let head = this.head;
			this.head = new Node(element, head)
		} else {
			let prevNode = this._node(index - 1);
			prevNode.next = new Node(element, prevNode.next)
		}
		this.size++;
	}
	remove(index) {
		if (index < 0 || index > this.size) throw new Error('越界');
		if (index === 0) {
			this.head = this.head.next;
		}else{
			let prevNode=this._node(index-1)
			prevNode.next=prevNode.next.next
		}
		this.size--;
	}

}

反转链表

//解法一:递归
function reverse(head) {
	if (head == null || head.next == null) return head;
	let newHead = reverse(head.next);
	head.next.next = head;
	head.next = null;
	return newHead;
	}
//解法二:创建一个新链表
function reverse(head) {
	if (head == null || head.next == null) return head;
	let newHead = nullwhile (head !== null) {
		let temp = head.next;
		head.next = newHead;
		newHead = head;
		head = temp;
	}
	return newHead;
}


class Node {
    constructor(element, parent) {
        this.element = element;
        this.parent = parent;
        this.left = null;
        this.right = null;
    }
}
class Tree {
    constructor() {
        this.root = null;
    }
    add(element) {
        if (this.root === null) {
           return  this.root = new Node(element);
        }
        // 可以用递归,用循环就可以了
        let currentNode = this.root; // 更新当前节点
        let parent;
        let compare;
        while (currentNode) {
            compare = currentNode.element < element;
            parent = currentNode; // 遍历前先记录节点
            if (compare) { //  作比较 更新节点
                // 接着以右边的为根节点
                currentNode = currentNode.right
            } else {
                currentNode = currentNode.left // 插入8的时候 右边没有人了
            }
        }
        // compare; // 放左还是放右边
        // parent; // 放到谁的身上
        let node = new Node(element, parent)
        if (compare) {
            parent.right = node
        } else {
            parent.left = node
        }
      
    }
}

前序遍历

preorderTraversal() {
	const traversal = (node) => {
	if (node === null) return
        console.log(node.element); // 先访问根节点
        traversal(node.left); // 在访问左子树
        traversal(node.right);// 在访问右子树
    }
    traversal(this.root);
} 

中序遍历

inorderTraversal() {
    const traversal = (node) => {
        if (node === null) return
        traversal(node.left);
        console.log(node.element);
        traversal(node.right);
    }
    traversal(this.root);
}

后序遍历

postorderTraversal() {
    const traversal = (node) => {
    if (node === null) return
        traversal(node.left);
        traversal(node.right);
        console.log(node.element);
    }
    traversal(this.root);
}

层序遍历

levelOrderTraversal() {
    if (this.root == null) return;
    let stack = [this.root];
    let currentNode = null;
    let index = 0;
    while (currentNode = stack[index++]) {
        console.log(currentNode.element); 
        if (currentNode.left) {
            stack.push(currentNode.left);
        }
        if (currentNode.right) {
            stack.push(currentNode.right);
        }
    }
}

反转二叉树

invertTree(){
    if (this.root == null) return;
    let stack = [this.root];
    let currentNode = null;
    let index = 0;
    while (currentNode = stack[index++]) {
        let tmp = currentNode.left;
        currentNode.left = currentNode.right;
        currentNode.right = tmp
        if (currentNode.left) {
            stack.push(currentNode.left);
        }
        if (currentNode.right) {
            stack.push(currentNode.right);
        }
    }
    return this.root;
}

列表转树

[
    {
        id: 1,
        text: '节点1',
        parentId: 0 //这里用0表示为顶级节点
    },
    {
        id: 2,
        text: '节点1_1',
        parentId: 1 //通过这个字段来确定子父级
    }
    ...
]

转成
[
    {
        id: 1,
        text: '节点1',
        parentId: 0,
        children: [
            {
                id:2,
                text: '节点1_1',
                parentId:1
            }
        ]
    }
]

function listToTree(data) {
  let temp = {};
  let treeData = [];
  for (let i = 0; i < data.length; i++) {
    temp[data[i].id] = data[i];
  }
  for (let i in temp) {
    if (+temp[i].parentId != 0) {
      if (!temp[temp[i].parentId].children) {
        temp[temp[i].parentId].children = [];
      }
      temp[temp[i].parentId].children.push(temp[i]);
    } else {
      treeData.push(temp[i]);
    }
  }
  return treeData;
}

深拷贝

function copy(obj, hash = new WeakMap()) {
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);
    if (typeof obj !== "object" || obj === null) return obj;
    if (hash.get(obj)) return hash.get(obj);
    let res = obj instanceof Array ? [] : {};
    hash.set(obj, res);
    for (const [k, v] of Object.entries(obj)) {
        res[k] = copy(v, hash);
    }
    return res;
}
 
 function clone(target, map = new Map()) {
    if (typeof target === 'object') {
        let cloneTarget = Array.isArray(target) ? [] : {};
        if (map.get(target)) {
            return target;
        }
        map.set(target, cloneTarget);
        for (const key in target) {
            cloneTarget[key] = clone(target[key], map);
        }
        return cloneTarget;
    } else {
        return target;
    }
};

深度比较

function isEqual(obj1, obj2) {
    if (!isObject(obj1) || !isObject(obj2)) {
        // 值类型(注意,参与 equal 的一般不会是函数)
        return obj1 === obj2
    }
    if (obj1 === obj2) {
        return true
    }
    // 两个都是对象或数组,而且不相等
    // 1. 先取出 obj1 和 obj2 的 keys ,比较个数
    const obj1Keys = Object.keys(obj1)
    const obj2Keys = Object.keys(obj2)
    if (obj1Keys.length !== obj2Keys.length) {
        return false
    }
    // 2. 以 obj1 为基准,和 obj2 一次递归比较
    for (let key in obj1) {
        // 比较当前 key 的 val —— 递归!!!
        const res = isEqual(obj1[key], obj2[key])
        if (!res) {
            return false
        }
    }
    // 3. 全相等
    return true
}

Lazyman

  1. 实现一个LazyMan,可以按照以下方式调用: LazyMan(“Hank”)输出: Hi! This is Hank!  
  2. LazyMan(“Hank”).sleep(10).eat(“dinner”)输出 Hi! This is Hank! //等待10秒.. Wake up after 10 Eat dinner~  
  3. LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输出 Hi This is Hank! Eat dinner~ Eat supper~  
  4. LazyMan(“Hank”).sleepFirst(5).eat(“supper”)输出 //等待5秒 Wake up after 5 Hi This is Hank! Eat supper
    class Lazy {
        constructor(name) {
            this.sleepFirstTime = 0;
            this.promise = Promise.resolve().then(
                () => this.sleepFirstTime && this._sleep(this.sleepFirstTime)
            ).then(() => {
                console.log(`Hi! This is ${name}!`);
            });
        }
        sleepFirst(time) {
            this.sleepFirstTime = time;
            return this;
        }
        eat(food) {
            this.promise = this.promise.then(() => {
                console.log(`Eat ${food}~`);
            });
            return this;
        }
        sleep(time) {
            this.promise = this.promise.then(() => this._sleep(time));
            return this;
        }
        _sleep(time) {
            return new Promise((next) => {
                setTimeout(() => {
                    console.log(`Wake up after ${time}`);
                    next();
                }, time);
            });
        }
    }

    function LazyMan(name) {
        return new Lazy(name);
    }

call/bind/apply

!function (proto) {
    function getContext(context) {
        context = context || window;
        let type = typeof context;
        if (['number', 'string', 'boolean', 'null'].includes(type)) {
            context=Object(context)
        }
        return context;
    }
    function call(context, ...args) {
        context = getContext(context);
        let symbol=Symbol('fn')
        context[symbol] = this;
        let result = context[symbol](...args);
        delete context[symbol];
        return result;
    }
    function apply(context, args) {
        context = getContext(context);
        let symbol=Symbol('fn')
        context[symbol] = this;
        let result = context[symbol](...args);
        delete context[symbol];
        return result;
    }

    function bind(context, ...bindArgs) {
        return (...args) => this.call(context, ...bindArgs, ...args);
    }
    proto.call = call;
    proto.apply = apply;
    proto.bind = bind;
}(Function.prototype)

Object.create

Object.create = function(obj) {
	function Fn() {};
	Fn.prototype = obj;
	return new Fn();
}

new

function New(fn, ...args) {
	let instance = Object.create(fn.prototype);
	let result = fn.call(instance, ...args);
	return typeof result === "object" ? result : instance;
}

instanceof

function myInstanceof(left, right) {
    let proto = Object.getPrototypeOf(left);
    while(true) {
        if(proto == null) return false;
        if(proto == right.prototype) return true;
        proto = Object.getPrototypeof(proto);
    }
}

二分查找

function bsearch(A, x) {
	let l = 0,
		r = A.length - 1,
		guess;
	while (l <= r) {
		guess = Math.floor((l + r) / 2)
		if (A[guess] === x) return guess
		else if (A[guess] > x) r = guess - 1
		else l = guess + 1
	}
	return -1
}

数组去重

//1.双层for循环 NaN没有去掉
function uniq(arr) {
    for (let i = 0; i < arr.length; i++) {
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[i] === arr[j]) {
                arr.splice(j, 1);
                j--;
            }
        }
    }
    return arr;
}
//2.先排序后比较 NaN没有去掉,对象去重复失效
function uniq(arr) {
    arr.sort();
    for (let i = 0; i < arr.length - 1; i++) {
        arr[i] === arr[i + 1] && arr.splice(i + 1, 1) && i--;
    }
    return arr;
} 
//3.indexof  NaN被去掉了
function uniq(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (arr.indexOf(arr[i]) === i) {
            res.push(arr[i]);
        }
    }
    return res;
}
function uniq2(arr) {
    return arr.filter((item, index)=> {
        return arr.indexOf(item) === index
    })
}
//4.reduce
function uniq6(arr){
	return arr.reduce((acc,cur)=>{
		if(!acc.includes(cur)){
			acc.push(cur)
		}
		return acc
	},[])
}
let  uniq2 = arr.sort().reduce((acc, current) => {
    if(acc.length === 0 || acc[acc.length-1] !== cur) {
        acc.push(cur);
    }
    return acc;
}, []);
//5.includes 可以去重NaN
function uniq(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (!res.includes(arr[i])) {
            res.push(arr[i]);
        }
    }
    return res;
}
//6.Map 可以去重NaN
function uniq(arr) {
    let map = new Map();
    for (let i = 0; i < arr.length; i++) {
        !map.has(arr[i]) && map.set(arr[i], true);
    }
    return [...map.keys()];
}
//7.set 可以去重NaN
function uniq(arr) {
    return [...new Set(arr)];
}

数组扁平化

// 1.reduce
function flatten(arr) {
      return arr.reduce((result, item) => {
          return result.concat(Array.isArray(item) ? flatten(item) : item); 
        }, []);
     };
function flat(arr, num = 1) {
  return num > 0
    ? arr.reduce(
        (pre, cur) =>
          pre.concat(Array.isArray(cur) ? flat(cur, num - 1) : cur),
        []
      )
    : arr.slice();
}
// 2.map
function flatten2(arr) {
    let res = [];
    arr.map(item => {
        if(Array.isArray(item)) {
            res = res.concat(flatten2(item));
        } else {
            res.push(item);
        }
    });
    return res;
}
//3.while ...
function flatten(arr){
  while (arr.some(item => Array.isArray(item))){
    arr = [].concat(...arr);
  }
  return arr;
}
//4.stack
function flat(arr) {
  const result = []; 
  const stack = [].concat(arr);  // 将数组元素拷贝至栈,直接赋值会改变原数组
  //如果栈不为空,则循环遍历
  while (stack.length !== 0) {
    const val = stack.pop(); 
    if (Array.isArray(val)) {
      stack.push(...val); //如果是数组再次入栈,并且展开了一层
    } else {
      result.unshift(val); //如果不是数组就将其取出来放入结果数组中
    }
  }
  return result;
}
//5.jion split 只用于数字
let flatten1 = a.join(',').split(',').map(Number)
let flatten2 = JSON.stringify(a).replace(/\[|\]/g, '').split(',').map(Number);

对象扁平化

function objectFlat(obj = {}) {
  const res = {}
  function flat(item, preKey = '') {
    Object.entries(item).forEach(([key, val]) => {
      const newKey = preKey ? `${preKey}.${key}` : key
      if (val && typeof val === 'object') {
        flat(val, newKey)
      } else {
        res[newKey] = val
      }
    })
  }
  flat(obj)
  return res
}

反转字符串

var reverseString = function(s) {
    const n = s.length;
    for (let left = 0, right = n - 1; left < right; ++left, --right) {
        [s[left], s[right]] = [s[right], s[left]];
    }
};

JSON.Stringify

function jsonStringify(obj) {
    let type = typeof obj;
    if (type !== "object") {
        if (/string|undefined|function/.test(type)) {
            obj = '"' + obj + '"';
        }
        return String(obj);
    } else {
        let json = []
        let arr = Array.isArray(obj)
        for (let k in obj) {
            let v = obj[k];
            let type = typeof v;
            if (/string|undefined|function/.test(type)) {
                v = '"' + v + '"';
            } else if (type === "object") {
                v = jsonStringify(v);
            }
            json.push((arr ? "" : '"' + k + '":') + String(v));
        }
        return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
    }
}

数组乱序

//Fisher–Yates Shuffle洗牌算法  
//先从数组末尾开始,选取最后一个元素,与数组中随机一个位置的元素交换位置
//然后在已经排好的最后一个元素以外的位置中,随机产生一个位置,让该位置元素与倒数第二个元素进行交换
function shuffle(arr) {
  let len = arr.length;
  while (len) {
    let i = (Math.random() * len--) >> 0; 
    let temp = arr[len];
    arr[len] = arr[i];
    arr[i] = temp;
  }
  return arr;
}
//用洗牌算法随机取数
function getRandomArrElement(array, count) {
    let arr = array.slice(0), 
        i = arr.length, 
        min = i - count, 
        temp, 
        index;
    while (i > min) {
        index = Math.floor((i--) * Math.random());
        temp = arr[index];
        arr[index] = arr[i];
        arr[i] = temp;
    }
    return arr.slice(min);
}
//在范围内生成随机数
function setRangeRandom(min, max) { 
        let n = max - min;
        if (n == 0) {
            return max
        } else if (n < 0) {
            [max, min] = [min, max];
            n = Math.abs(n);
        }
        return  Math.random()* n+min;
    }

JSON.parse

//法一
(new Function('return ' + jsonStr))();
//法二
function jsonParse(opt) {
    return eval('(' + opt + ')');
}

寄生组合继承

function Super() {}
function Sub() {
    Super.call(this)
}
Sub.prototype = new Super();
Sub.constructor = Sub;

实现中划线与驼峰的互相转换

function camelize(str) {
    return (str + '').replace(/-\D/g, function(match) {
        return match.charAt(1).toUpperCase()
    })
}
function hyphenate(str) {
    return (str + '').replace(/[A-Z]/g, function(match) {
        return '-' + match.toLowerCase();
    })
}

实现ajax

function ajax({url, methods, headers}) {
    return new Promise((resolve, reject) => {
        let xhr =  XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
        xhr.open(methods,url);
        for(let key in headers) {
            let value = headers[key]
            xhr.setRequestHeader(key, headers[key]);
        }
        xhr.onreadystatechange = () => {
            if(xhr.readyState === 4) {
                if(xhr.status === 200 || xhr.status === 304) {
                    resolve(xhr.response);
                } else {
                    reject(new Error(xhr.responseText))
                }
            }
        }
         xhr.send();
    })
}

实现最大并发数

function asyncPool(poolLimit, array, iteratorFn) {
    let i = 0;
    const ret = [];
    const executing = [];
    const enqueue = function () {
        // 边界处理,array为空数组
        if (i === array.length) {
            return Promise.resolve();
        }
        // 每调一次enqueue,初始化一个promise
        const item = array[i++];
        const p = Promise.resolve().then(() => iteratorFn(item, array));
        // 放入promises数组
        ret.push(p);
        // promise执行完毕,从executing数组中删除
        const e = p.then(() => executing.splice(executing.indexOf(e), 1));
        // 插入executing数字,表示正在执行的promise
        executing.push(e);
        // 使用Promise.rece,每当executing数组中promise数量低于poolLimit,就实例化新的promise并执行
        let r = Promise.resolve();
        if (executing.length >= poolLimit) {
            r = Promise.race(executing);
        }
        // 递归,直到遍历完array
        return r.then(() => enqueue());
    };
    return enqueue().then(() => Promise.all(ret));
}

function sendRequest(urls, num, callback) {
    (function request(res) {
        urls.length ? Promise.all(urls.splice(0, num).map(url => fetch(url))).then(r => request(res.concat(r))) : callback(res);
    })([]);
}

vue2响应式


class Dep {
  static stack = []
  static target = null
  deps = null
  
  constructor() {
    this.deps = new Set()
  }

  depend() {
    if (Dep.target) {
      this.deps.add(Dep.target)
    }
  }

  notify() {
    this.deps.forEach(w => w.update())
  }

  static pushTarget(t) {
    if (this.target) {
      this.stack.push(this.target)
    }
    this.target = t
  }

  static popTarget() {
    this.target = this.stack.pop()
  }
}

// reactive
function reactive(o) {
  if (o && typeof o === 'object') {
    Object.keys(o).forEach(k => {
      defineReactive(o, k, o[k])
    })
  }
  return o
}

function defineReactive(obj, k, val) {
  let dep = new Dep()
  Object.defineProperty(obj, k, {
    get() {
      dep.depend()
      return val
    },
    set(newVal) {
      val = newVal
      dep.notify()
    }
  })
  if (val && typeof val === 'object') {
    reactive(val)
  }
}

// watcher
class Watcher {
  constructor(effect) {
    this.effect = effect
    this.update()
  }

  update() {
    Dep.pushTarget(this)
    this.value = this.effect()
    Dep.popTarget()
    return this.value
  }
}

vue3响应式

/**
 * Vue3 响应式原理
 *
 */

// 判断是不是对象
function isObject(val) {
  return typeof val === "object" && val !== null;
}
function hasOwn(target, key) {
  return target.hasOwnProperty(key);
}
// WeakMap: 弱引用映射表
// 原对象 : 代理过的对象
let toProxy = new WeakMap();
// 代理过的对象:原对象
let toRaw = new WeakMap();
// 响应式核心方法
function reactive(target) {
  // 创建响应式对象
  return createReactiveObject(target);
}
function createReactiveObject(target) {
  // 如果当前不是对象,直接返回即可
  if (!isObject(target)) {
    return target;
  }
  // 如果已经代理过了,就直接返回代理过的结果
  let proxy = toProxy.get(target);
  if (proxy) {
    return proxy;
  }
  // 防止代理过的对象再次被代理
  if (toRaw.has(target)) {
    return target;
  }
  let baseHandler = {
    get(target, key, receiver) {
      // Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与proxy handlers的方法相同。
      let res = Reflect.get(target, key, receiver);
      // 收集依赖/订阅 把当前的key和effect做映射关系
      track(target, key);
      // 在get取值的时候才去判断该值是否是一个对象,如果是则递归(这里相比于Vue2中的默认递归,其实是一种优化)
      return isObject(res) ? reactive(res) : res;
    },
    set(target, key, value, receiver) {
      // 这里需要区分是新增属性还是修改属性
      let hasKey = hasOwn(target, key);
      let oldVal = target[key];
      let res = Reflect.set(target, key, value, receiver);
      if (!hasKey) {
        console.log("新增属性");
        trigger(target, "add", key);
      } else if (oldVal !== value) {
        console.log("修改属性");
        trigger(target, "set", key);
      }
      return res;
    },
    deleteProperty(target, key) {
      let res = Reflect.deleteProperty(target, key);
      return res;
    },
  };
  let observed = new Proxy(target, baseHandler);
  toProxy.set(target, observed);
  toRaw.set(observed, target);
  return observed;
}

// 栈 先进后出 {name:[effect]}
let activeEffectStacks = [];
let targetsMap = new WeakMap();
// 如果target中的key发生变化了,就执行数组里的方法
function track(target, key) {
  // 拿出栈顶函数
  let effect = activeEffectStacks[activeEffectStacks.length - 1];
  if (effect) {
    // 获取target对应依赖表
    let depsMap = targetsMap.get(target);
    if (!depsMap) {
      targetsMap.set(target, (depsMap = new Map()));
    }
    // 获取key对应的响应函数集
    let deps = depsMap.get(key);
    // 动态创建依赖关系
    if (!deps) {
      depsMap.set(key, (deps = new Set()));
    }
    if (!deps.has(effect)) {
      deps.add(effect);
    }
  }
}
function trigger(target, type, key) {
  let depsMap = targetsMap.get(target);
  if (depsMap) {
    let deps = depsMap.get(key);
    if (deps) {
      // 将当前key对应的effect依次执行
      deps.forEach((effect) => {
        effect();
      });
    }
  }
}
// 响应式 副作用
function effect(fn) {
  const rxEffect = function () {
    try {
      // 捕获异常
      // 运行fn并将effect保存起来
      activeEffectStacks.push(rxEffect);
      return fn();
    } finally {
      activeEffectStacks.pop();
    }
  };
  // 默认应该先执行一次
  rxEffect();
  // 返回响应函数
  return rxEffect;
}

setTimeout实现setInterval

function timerFun(){
//todo...
let timer=setTimeout(()=>{
     timerFun();
     clearTimeout(timer)
  },1000);
}

requestAnimationFrame实现setInterval

// setInterval实现
function setInterval(callback, interval) {
    let timer
    const now = Date.now
    let startTime = now()
    let endTime = startTime
    const loop = () => {
        timer = window.requestAnimationFrame(loop)
        endTime = now()
        if (endTime - startTime >= interval) {
            startTime = endTime = now()
            callback(timer)
        }
    }
    timer = window.requestAnimationFrame(loop)
    return timer
}

let a = 0
setInterval(timer => {
    console.log(a)
    a++
    if (a === 3) window.cancelAnimationFrame(timer)
}, 1000)
// 0
// 1
// 2

requestAnimationFrame实现setTimeout

// setTimeout 实现
function setTimeout(callback, interval) {
    let timer
    const now = Date.now
    let startTime = now()
    let endTime = startTime
    const loop = () => {
        timer = window.requestAnimationFrame(loop)
        endTime = now()
        if (endTime - startTime >= interval) {
            callback(timer)
            window.cancelAnimationFrame(timer)
        }
    }
    timer = window.requestAnimationFrame(loop)
    return timer
}

let a = 0
setTimeout(timer => {
    console.log(a)
    a++
}, 1000)
// 0

大数相加

function add(a ,b){
   let maxLength = Math.max(a.length, b.length);
   //用0去补齐长度
   a = a.padStart(maxLength , 0);//"0009007199254740991"
   b = b.padStart(maxLength , 0);//"1234567899999999999"
   //定义加法过程中需要用到的变量
   let t = 0;
   let f = 0;   //"进位"
   let sum = "";
   for(let i=maxLength-1 ; i>=0 ; i--){
      t = parseInt(a[i]) + parseInt(b[i]) + f;
      f = Math.floor(t/10);
      sum = t%10 + sum;
   }
   if(f==1){
      sum = "1"+ sum;
   }
   return sum;
}

LRU


var LRUCache = function(capacity) {
    this.capacity = capacity;
    this.map = new Map();
};


LRUCache.prototype.get = function(key) {
    if(this.map.has(key)){
        let temp=this.map.get(key)
         this.map.delete(key);
         this.map.set(key, temp);
         return temp
    }else{
        return -1
    }
};

LRUCache.prototype.put = function(key, value) {
    if(this.map.has(key)){
        this.map.delete(key);
    }
    this.map.set(key,value);
    if(this.map.size > this.capacity){
        this.map.delete(this.map.keys().next().value);
    }
};

版本号

//排序
arr.sort((a, b) => {
  let i = 0;
  const arr1 = a.split(".");
  const arr2 = b.split(".");

  while (true) {
    const s1 = arr1[i];
    const s2 = arr2[i];
    i++;
    if (s1 === undefined || s2 === undefined) {
      return arr2.length - arr1.length;
    }

    if (s1 === s2) continue;

    return s2 - s1;
  }
});
//比大小
var compareVersion = function(version1, version2) {
 let arr1=version1.split("."),arr2=version2.split('.');
 let maxLen=Math.max(arr1.length,arr2.length);
for(let i=0;i<maxLen;i++){
    arr1[i]=arr1[i]===undefined?0:parseInt(arr1[i]);
    arr2[i]=arr2[i]===undefined?0:parseInt(arr2[i]);
    if(arr1[i]>arr2[i]){return 1}else if(arr1[i]<arr2[i])return -1;
}
return 0;
};

trim

String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g, '')
}

使用css绘制几何图形

//三角形
.triangle {
            width: 0;
            height: 0;
            border-width: 50px;
            border-style: solid;
            border-color: #8D0EEE transparent transparent transparent;
        }
//菱形
 .diamond {
            width: 100px;
            height: 100px;
            transform: rotate(45deg);
            background: #0CCEf2;
        }
//扇形
.sector{
          border-radius:80px 0 0;
          width: 80px;
          height: 80px;
          background: #666;
}

css绘制曲线

.ball{
  height: 100px;
  width: 100px;
  border-radius: 50%;
  position: absolute;
  bottom: 40px;
  left: 40px;
  background: greenyellow;
}

//定义动画的流程
.run_top_right {
  display: block;
  animation: run-right-right 3s 0.4s 1 linear, run-right-top 3s 0.4s 1 cubic-bezier(.66,.1,1,.41);
  animation-fill-mode: forwards;
}

//向上走的动画初始及结尾值
@keyframes run-right-top {
  0% {
    bottom: 40px;
  }

  100% {
    bottom: 800px;
  }
}
//向上右的动画初始及结尾值
@keyframes run-right-right {
  0% {
    left: 40px;
    transform: scale(0.7);
  }

  100% {
    left: 600px;
    transform: scale(0.45);
  }
}

左右固定中间自适应布局

//优缺点 
//假设高度未知 float absolute grid不能用了
//兼容性
//浮动
脱离文档流
兼容性好
.left{
	float: left;
	width: 300px;
}
.right{
	float: right;
	width: 300px;
}
.center{

}
//绝对定位
脱离文档流
.left{
	left: 0;
	width: 300px
	position: absolute;
}
.right{
	right: 0;
	width: 300px;
	position: absolute;
}
.center{
	left: 300px;
	right: 300px;
	position: absolute;
}
//flex
ie8不支持
父容器{
	display: flex;
}
.right,.left{
	width: 300px;
}
.center{
	flex: 1;
}

//tablecell
兼容性好
两边会同时增高
父容器{
	display: table;
	width: 100%
}
.left{
    display: table-cell;
	width: 300px
}
.right{
    display: table-cell;
	width: 300px;
}
.center{
    display: table-cell;
}
//网格布局
代码简洁
父容器{
	display: grid;
	width: 100%;
	grid-template-rows: 100px;
	grid-template-columns: 300px auto 300px;
}

水平垂直居中

.parent {
    display: table-cell;
    vertical-align: middle;

}

.son {
    margin: 0 auto;
}

.parent2 {
    position: relative;
}

.son2 {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.parent3 {
    position: relative;
}

.son3 {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    margin: auto;
}

.parent4 {
    display: flex;
    justify-content: center;
    align-items: center;
}

.son4 {}

.parent5 {
    display: flex;
    justify-content: center;
}

.son5 {
    align-self: center;
}

.parent6 {
    display: grid;
}

.son6 {
    align-self: center;
    justify-self: center;
}
.parent7 {
    display: grid;
}

.son7{
margin:auto;
}