前端常见需要手写的代码

实现 call/apply/bind

//call 和 apply 只是参数传递方式不同。

//apply 传递的是个参数数组
Function.prototype.myApply = function(context,args){
    let fn = this;
    let key = Symbol();
    context[key] = fn;
    let res = context[key](...args);
    delete context[key];
    return res;
}

function a (...x){console.log(this.a,x)}

a.myApply({a:"myapply"},[1,2,3])
a.apply({a:"myapply"},[1,2,3])

//call 传递的是参数列表
Function.prototype.myCall = function(context,...args){
    let fn = this;
    let key = Symbol();
    context[key] = fn;
    let res = context[key](...args);
    delete context[key];
    return res;
}

function a (...x){console.log(this.a,x)}

a.myCall({a:"myCall"},111,22)

a.call({a:"myCall"},111,22)

//bind 返回一个绑定了this的函数
Function.prototype.myBind = function(context,...bindArgs){
    let fn = this;
    let key = Symbol();
    context[key] = fn;
    return function(...args){
        let finalArgs = [...bindArgs,...args];
        let res = context[key](...finalArgs);
        delete context[key];
        return res;
    }
}
b = a.myBind({a:"myBind"},1,2)
b(3,4)

b = a.bind({a:"myBind"},1,2)
b(3,4)

实现 new

new操作符做了如下几件事:

  1. 新建一个对象
  2. 把对象关联到构造函数的prototype属性
  3. 以新建对象为this执行构造函数
  4. 判断构造函数的返回值,对象就返回对象,不是就返回新建对象
function myNew(fn,...args){
	let obj = Object.create(null);
  obj.__proto__ = fn.prototype;
  let res = fn.apply(obj,args);
  return typeof res === "object"?res:obj;
}

function a (word) {
    this.temp = "myNew"
    this.word = word
}
a.prototype.myNew = ()=>{console.log("myNew")}

myNew(a,"hello")

实现 节流防抖

节流防抖目的都是同一个,防止事件触发太频繁。区别在于 防抖:不停触发事件,事件最终只响应一次。在间隔时间后,再次触发才会再次响应。 节流:不停触发事件,事件会按照设定的间隔时间,间隔响应。

//防抖
function debounce(fn,wait){
	let timer;
  return function(...args){
		if(timer)clearTimeout(timer);
		timer = setTimeout(()=>{
			fn(...args);
		},wait);
	}
}

//节流 这个不太精确
function throttle(fn,wait){
	let time = 0;
  return function(...args){
		if(Date.now() - time > wait){
			fn(...args);
      time = Date.now();
		}
	}
}

a = function(){console.log("onscroll")}
b = debounce(a,100)
c = throttle(a,1000)
window.onscroll = a;
//滚动试试


window.onscroll = b;
//滚动试试


window.onscroll = c;
//滚动试试

实现深拷贝

function deepclone(obj,cache=[]){
	if(obj === null || typeof obj !== "object"){
  	return obj;
  }
  
  const has = cache.filter(cache=>cache.orignal === obj)[0];
  if(has){
  	return has.copy;
  }
  
  const copy = Array.isArray(obj)?[]:{};
  
  cache.push({
    orignal: obj,
    copy
  });

  Object.keys(obj).forEach(key => {
    copy[key] = deepClone(obj[key], cache);
  });
  return copy;
}

实现Promise(todo)

做这个之前,最好浏览一遍promise的规范链接

实现promise的构造函数。

promise 主要有三个状态:

pending/fulfilled/rejected

且状态只能由pending->fulfilled 或者 pending->rejected

promise初始化 状态为pending。

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

const resolvePromise = (res,resolve,reject)=>{
    if(res && typeof res === 'object' ||  typeof res === 'function' ){
        let then = res.then;
        if(typeof then === 'function'){
            then.call(res,(r)=>{
                resolvePromise(r,resolve,reject);
            },(r)=>{
                reject(r);
            })
        }else{
            reject(x);
        }
    }else{
        resolve(res);
    }
};

function XPromise(fn) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCB = [];
    this.onRejectedCB = [];
    let that = this;
    function resolve(value) {
        setTimeout(() => {
            if (that.status === PENDING) {
                that.status = FULFILLED;
                that.value = value;
                that.onFulfilledCB.forEach(cb => cb(that.value));
            };
        },0)
    }
    function reject(reason) {
        setTimeout(() => {
            if (that.status === PENDING) {
                that.status = REJECTED;
                that.reason = reason;
                that.onRejectedCB.forEach(cb => cb(reason));
            }
        },0)
    }

    try {
        fn(resolve, reject)
    } catch (e) {
        reject(e)
    }


};

XPromise.prototype.then = function (onFulfill, onReject) {

    onFulfill = typeof onFulfill === 'function' ? onFulfill : r=>r;
    onReject = typeof onReject === 'function' ? onReject : r=>{throw(r)};
    const that = this;
    return new XPromise((resolve,reject)=>{
        if (that.status === PENDING){
            that.onFulfilledCB.push((value)=>{
                setTimeout(()=>{
                    try {
                        const res = onFulfill(value);
                        resolvePromise(res,resolve,reject);
                    } catch (error) {
                        reject(error);
                    }
                },0);
            });
            that.onRejectedCB.push((reason)=>{
                setTimeout(()=>{
                    try {
                        const res = onReject(reason);
                        resolvePromise(res,resolve,reject);
                    } catch (error) {
                        reject(error);
                    }
                },0);
            });
        }else if (that.status === FULFILLED){
            setTimeout(()=>{
                try {
                    const res = onFulfill(that.value);
                    // 根据res来执行后续
                    resolvePromise(res,resolve,reject);
                } catch (error) {
                    reject(error);
                }
            },0);
        }else if (that.status === REJECTED){
            setTimeout(()=>{
                try {
                    const res = onReject(that.reason);
                    // 根据res来执行后续
                    resolvePromise(res,resolve,reject);
                } catch (error) {
                    reject(error);
                }
            },0);
        }
    });
};


const p = new XPromise((resolve,reject)=>{
    setTimeout(()=>reject("test"),1000);
});

p2 = p.then(v=>{console.log(v);return 'sdsds'}).then(v=>console.log(v),(r)=>console.log(r));

将一个数组奇数在前偶数在后

function test(array){
    array.sort(function(a,b){
        if(a%2 == 0 && b%2!=0){
            return 1;
        }else{
            return -1;
        }
    })
    console.log(array);
}

打印一个三角形

const getStr = (i, n) => {
  let str = "";

  let emptyStrLength = n - i;
  let notEmptyStrLen = 1+(2*(i-1));

  let loop = 0;
  while (loop < emptyStrLength) {
    str += " ";
    loop++;
  }
  loop = 0;
  while (loop < notEmptyStrLen) {
    str += '*';
    loop++;
  }
  loop = 0;
  while (loop < emptyStrLength) {
    str += " ";
    loop++;
  }
  return str;
};

const trig = (n) => {
  let i = 1;
  while (i <= n) {
    console.log(getStr(i, n));
    i++;
  }
};

trig(6);

WorkerMan

class WorkerMan{
    tasks = [];
    sleep = (time)=>new Promise((r)=>setTimeout(r,time * 1000)); 
    taskName(name){
        this.name = name;
        return this;
    }
    do(task){
        this.tasks.push({ type : "normal", txt : `> do ${task}`});
        return this;
    }
    wait(num){
        this.tasks.push({ type : "wait", num });
        return this;
    }
    waitFirst(num){
        this.tasks.unshift({ type : "wait", num });
        return this;
    }

    async execute(){
        console.log(`${this.name} 开始做任务了`);
        for(let i = 0; i<this.tasks.length;i++){
            const task = this.tasks[i];
            const { type } = task;
            if(type === "normal"){
                console.log(task.txt);
            }else if(type === "wait"){
                console.log(`等待${task.num}`);
                await this.sleep(task.num);
            }
        }
        this.tasks = [];
    }
}

const man = new WorkerMan();
man.taskName("zack").do("敲代码").wait(1).do("拿外卖").waitFirst(2).execute();
man.taskName("zack").do("敲代码").wait(1).do("拿外卖").execute();


使用 Discussions 讨论 Github 上编辑