手写题

153 阅读6分钟

实现洋葱模型 redux.compose

// newFn = compose(fn1, fn2 ,fn3, fn4, fn5)
// newFn(...args) <=> fn1(fn2(fn3(...fnk(...args))))

// fn1 = a => a + 1
// fn2 = a => a + 10
// fn3 = a => a + 100
// fn4 = a => a + 1000
// fn5 = (a,b,c,d,e,f) => a + b + c + d + e  + f
// newFn = compose(fn1, fn2, fn3, fn4, fn5)

// newFn(1,2,3,4,5,6)  == 1132                 // 等价于下面
// fn1(fn2(fn3(fn4(fn5(1,2,3,4,5)))))  == 1132      // true
function compose(...funcs) {
    return function proxy(...args) {
        let len = funcs.length;
        if (len === 0) {
            return args;
        } else if (len === 1) {
            return funcs[0](...args);
        } else {
            return funcs.reverse().reduce((prev, cur) => {
                return typeof prev === "function" ? cur(prev(...args)) : cur(prev);
            });
        }
    };
}

重写new


function _new(Ctor,...params){
    // 创建一个新的实例对象
    let obj={}
    // 新的实例对象的__proto__关联到构造函数的prototype
    obj.__proto__ = Ctor.prototype;
    // 改变构造函数的this指向,让其指向新创建的实例对象
    let result = Ctor.call(obj,...params)
    //如果返回的是空或者基本对象,则return实例本身,否则return原有结果
    if(typeof result == "object" || typeof result == "function") {
        return result
    } else {
        return obj
    }
}

防抖

主体思路:在当前点击完成后,我们等wait这么长的时间,看是否还会触发第二次,如果没有触发第二次,属于非频繁操作,我们直接执行想要执行的函数func;如果触发了第二次,则以前的不算了,从当前这次再开始等待
function debounce(func, wait = 300, immediate = false) {
    let timer = null;
    return function annoymous(...params) {
        let now = immediate && !timer;
        //每次点击都把之前设置的定时器清除
        clearTimeout(timer);
        //重新设置定时器监听wait时间内是否触发第二次
        //this->当前元素
        timer = setTimeout(() => {
            //手动让其回归到初始状态
            timer = null;
            //wait这么久的等待中,没有触发第二次
            !immediate ? func.call(this, ...params) : null;
        }, wait);
        //如果当前是立即执行
        now ? func.call(this, ...params) : null
    }
}
function sayHi() {
    console.log("防抖成功");
}
var inp = document.getElementById("inp");
inp.addEventListener("input", debounce(sayHi,300));

节流

function throttle(func,wait=300){
    let timer = null,
        previous = 0;//记录上一次操作时间
    return function annoymous(...params){
        let now = new Date(),
            remaining=wait - (now - previous);//记录还差多久达到我们一次触发的频率
        if(remaining<=0){
            timer = null;
            //两次操作的间隔已经超过wait了
            window.clearTimeout(timer)
            previous = now;
            func.call(this,...parems)
        } else if(!timer) {
            //两次操作时间的间隔不符合触发频率
            timer = setTimeout(()=>{
                timer = null;
                previous = new Date();
                func.call(this,...params)
            },remaining)
        }
    }
}
function sayHi(e) {
    console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener("resize", throttle(sayHi,3000));

重写call、apply

Function.prototype.call = function call(context, ...params) {
    //fn.call=>call中的this是fn
    //this->fn 当前执行的函数
    //context->obj 需要改变的this
    //params->[10,20]需要函数传递的实参信息
    if (context == null) {
        context = window;
    }
    if (typeof context !== "object"||typeof context !=='function') {
        context = Object(context);
    }
    let key = Symbol("key");
    context[key] = this;
    //this要执行的函数
    let result = context[key](...params);
    delete context[key];
    return result;
};
let a = "1";
function fn() {
    console.log(this);
}
fn()

重写bind

事件触发/定时器到时间执行对应方法
原理:闭包、柯里化
Function.prototype.bind = function bind(context,...params){
    //this->fn最后要执行的函数
    //context->obj 最后要改变的this
    //params->[10,20] 最后要传递的参数
    let that = this
    return function proxy(...args){
    匿名函数中的this是当前元素
        params = params.concat(args)
        return that.call(context,...params);
    }
}
function foo(name) {
    this.name = name;
}
let obj = {};
//上下文 功能  done
let bar = foo.bind(obj);
bar("jack");
console.log(obj.name); //'jack'
// 参数 功能   done
let tar = foo.bind(obj, "rose");
tar();
console.log(obj.name); //'rose'
// new 功能   error
let alice = new bar("alice");
console.log(obj.name); //alice   obj name should be 'jack'
console.log(alice.name); //undefined, alice name should be 'alice'

重写instanceof

function instance_of(obj, constructor) {
    let proto = obj.__proto__,
        prototype = constructor.prototype;
    while (true) {
        if (proto === null) return false;
        //找到Object.prototype.__proto__都没有相等的,则证明不是当前累的实例
        if (proto === prototype) return true;
        //找到对象的原型链包含类的原型,则证明对象是类的一个实例
        proto = proto.__proto__;
        //一级级查找
    }
}
console.log(instance_of([],Array))
console.log(instance_of([],RegExp))

类的继承

组合继承

function Parent(){
    this.x =100;
}
Parent.prototype.getX = function getX(){
    return this.x;
}
function Child(){
    Parent.call(this);
    this.y = 200;
};
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.getY = function getY(){
    return this.y;
}
let c1 = new Child;
console.log(c1);

题干
function Parent() {
    this.x = 100;
}
Parent.prototype.getX = function getX() {
    return this.x;
};
function Child() {
    this.y = 100;
}
Child.prototype.getY = function getY() {
    return this.y;
};
let c1 = new Child();
console.log(c1);

es6继承

class Parent {
    constructor() {
        this.x = 100;
    }
    getX() {
        return this.x
    }
}
class Child extends Parent {
    constructor() {
        super();
        this.y = 200;
    }
    getY() {
        return this.y;
    }
}
let c1 = new Child;
console.log(c1);

题干
class Parent {
    constructor() {
        this.x = 100;
    }
    getX() {
        return this.x;
    }
}
class Child extends Parent {
    constructor() {
        super();
        this.y = 200;
    }
    getY() {
        return this.y;
    }
}
let c1 = new Child();
console.log(c1);

手写flatten

function flatten(arr) {
    return arr.reduce((prev, item) => {
        return prev.concat(Array.isArray(item) ? flatten(item) : item);
    }, []);
}
let arr = [1, 2, 3,[4, [5, 6]]];
console.log(flatten(arr));

手写Object.create

新建一个空的构造函数 F ,然后让 F.prototype 指向 obj,最后返回 F 的实例
const myCreate = function (obj) {
    function F() {};
    F.prototype = obj;
    return new F()
}

手写深拷贝

function _cloneDeep(obj) {
    // 传递进来的如果不是对象,则无需处理,直接返回原始的值即可(一般Symbol和Function也不会进行处理的)
    if (obj === null) return null;
    if (typeof obj !== "object") return obj;
    // 过滤掉特殊的对象(正则对象或者日期对象):直接使用原始值创建当前类的一个新的实例即可,这样克隆后的是新的实例,但是值和之前一样
    if (obj instanceof RegExp) return new RegExp(obj);
    if (obj instanceof Date) return new Date(obj);
    // 如果传递的是数组或者对象,我们需要创建一个新的数组或者对象,用来存储原始的数据
    // obj.constructor 获取当前值的构造器(Array/Object)
    let cloneObj = new obj.constructor();
    for (let key in obj) {
        // 循环原始数据中的每一项,把每一项赋值给新的对象
        if (!obj.hasOwnProperty(key)) break;
        cloneObj[key] = _cloneDeep(obj[key]);
    }
    return cloneObj;
}
let obj = { a: { b: { c: 2 } } };
let newObj = _cloneDeep(obj);
newObj.a.b.c = 33;
console.log(obj, newObj);

手写深比较

function _assignDeep(obj1, obj2) {
    // 先把OBJ1中的每一项深度克隆一份赋值给新的对象
    let obj = _cloneDeep(obj1);
    // 再拿OBJ2替换OBJ中的每一项
    for (let key in obj2) {
        if (!obj2.hasOwnProperty(key)) break;
        let v2 = obj2[key],
            v1 = obj[key];
        // 如果OBJ2遍历的当前项是个对象,并且对应的OBJ这项也是一个对象,此时不能直接替换,需要把两个对象重新合并一下,合并后的最新结果赋值给新对象中的这一项
        if (typeof v1 === "object" && typeof v2 === "object") {
            obj[key] = _assignDeep(v1, v2);
            continue;
        }
        obj[key] = v2;
    }
    return obj;
}
let obj1 = { a: 2, b: { c: 2 } };
let obj2 = { a: 1, b: { c: 1 } };
console.log(_assignDeep(obj2, obj1));

发布订阅

class EventEmitter {
    _events = {};
    on() {
        //订阅
        let [eventName, cb, ...argus] = arguments;
        if (!this._events[eventName]) {
            this._events[eventName] = [];
        }
        this._events[eventName].push(cb);
    }
    emit() {
        //发布
        let [eventName, ...args] = arguments;
        this._events[eventName].map((cb) => {
            cb(args);
        });
    }
    off() {
        //取消订阅
        let [eventName, cb, ...argus] = arguments;
        let index = this._events[eventName].indexOf(cb);
        this._events[eventName].splice(index, 1);
    }
    once() {
        let [eventName, cb] = arguments;
        let fn = (argus) => {
            cb && cb(...argus);
            this.off(eventName, fn);
        };
        this.on(eventName, fn);
    }
}
let eventEmitter = new EventEmitter();

实现lazyMan

class LazyMan {
    constructor(str) {
        console.log(str);
        this.task = [];
        setTimeout(() => {
            this.next();
        });
    }
    next() {
        if (this.task.length === 0) return;
        let nextTask = this.task.shift();
        nextTask();
    }
    eat(sth) {
        let _this = this;
        let fn = () => {
            console.log(`eat ${sth} ~~~`);
            _this.next();
        };
        this.task.push(fn);
        return this;
    }
    sleep(time) {
        let _this = this;
        let fn = () => {
            setTimeout(() => {
                console.log(`timeout ${time} s`);
                _this.next();
            }, time);
        };
        this.task.push(fn);
        return this;
    }
}
let lazyMan = (str) => {
    return new LazyMan(str);
};
lazyMan("lazyMan ~~").sleep(1000).eat("breakfirst").sleep(1000).eat("lunch");

手写promise

(function () {
    // 自定义一个Promise类,实现内置Promise的重写(PromiseAplus)
    // https://promisesaplus.com/
    function Promise(executor) {
        // 必须保证executor得是一个函数
        if (typeof executor !== "function") {
            throw new TypeError("Promise resolver " + executor + " is not a function");
        }
        // self:存储的是promise实例
        var self = this;
        self.PromiseState = "pending";
        self.PromiseResult = undefined;
        self.onFulfilledCallbacks = [];
        self.onRejectedCallbacks = [];
        // 执行resolve/reject都是修改当前实例的状态和结果
        //   + 状态一但被更改后,则不能再次修改状态
        var run = function run(state, result) {
            if (self.PromiseState !== "pending") return;
            self.PromiseState = state;
            self.PromiseResult = result;
            // 执行resolve/rejec的时候,立即更改状态信息,但是不会立即通知方法执行(异步效果)
            setTimeout(function () {
                var arr = state === "fulfilled" ? self.onFulfilledCallbacks : self.onRejectedCallbacks;
                for (var i = 0; i < arr.length; i++) {
                    let itemFunc = arr[i];
                    if (typeof itemFunc === "function") {
                        itemFunc(self.PromiseResult);
                    }
                }
            });
        };
        var resolve = function resolve(value) {
            run("fulfilled", value);
        };
        var reject = function reject(reason) {
            run("rejected", reason);
        };
        // 立即执行executor函数:如果函数执行报错了,则promise状态也要改为失败态
        try {
            executor(resolve, reject);
        } catch (err) {
            reject(err);
        }
    }
    // Promise的原型
    Promise.prototype = {
        // 标记是否为自定义的PROMISE
        customize: true,
        constructor: Promise,
        then: function (onfulfilled, onrejected) {
            // 根据状态不同,执行不同的方法,处理不同的事情
            //   + 执行THEN的时候,哪怕此时已经知道状态了,也不是立即把onfulfilled/onrejected执行的,需要把函数的执行设置为异步操作:设置一个定时器,不设置等待的时间,则默认是浏览器最快的反应时间后执行,但是本身属于异步操作了
            //   + 执行THEN的时候,还不清楚实例的状态(例如:EXECUTOR函数中是一个异步操作的):此时我们应该先把基于THEN传入的方法onfulfilled/onrejected存储起来(后期可以做去重处理),在以后执行resolve/reject函数的时候,通知方法执行!
            var self = this;
            switch (self.PromiseState) {
                case "fulfilled":
                    setTimeout(function () {
                        onfulfilled(self.PromiseResult);
                    });
                    break;
                case "rejected":
                    setTimeout(function () {
                        onrejected(self.PromiseResult);
                    });
                    break;
                default:
                    self.onFulfilledCallbacks.push(onfulfilled);
                    self.onRejectedCallbacks.push(onrejected);
            }
        },
        catch: function () {},
    };
    window.Promise = Promise;
})();
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("OK");
        console.log(2);
    }, 1000);
});
p1.then(
    (value) => {
        console.log("成功", value);
    },
    (reason) => {
        console.log("失败", reason);
    }
);
p1.then(
    (value) => {
        console.log("成功", value);
    },
    (reason) => {
        console.log("失败", reason);
    }
);
p1.then(
    (value) => {
        console.log("成功", value);
    },
    (reason) => {
        console.log("失败", reason);
    }
);
console.log(1);

手写Promise.all
function all(args) {
    return new Promise((resolve, reject) => {
        let len = args.length;
        let num = 0;
        let res = [];
        function setRes(val, idx) {
            res[idx] = val;
            if (num === len) {
                resolve(res)
            }
        }
        args.map((el, idx) => {
            if (el && typeof el.then === 'function' && el instanceof Promise) {
                el.then(result => {
                    console.log(result)
                    num++;
                    setRes(result, idx)
                })
            } else {
                num++;
                setRes(el, idx)
            }
        })
    })
}

function promiseAll(promises) {
    if (!Array.isArray(promises)) {
        throw new Error("must be an array");
    }
    return new Promise((resolve, reject) => {
        let len = promises.length;
        let count = 0;
        let result = new Array(len);
        promises.forEach((item, index) => {
            Promise.resolve(item).then(
                (value) => {
                    result[index] = value;
                    count++;
                    if (count === len) {
                        resolve(result);
                    }
                },
                (err) => {
                    rejext(err);
                }
            );
        });
    });
}
let fn = new Promise((resolve) => {
    setTimeout(() => {
        resolve(1);
    }, 3000);
});
promiseAll([fn, 2, 3]).then((res) => {
    console.log(res);
});

手写Promise.race
function promiseRace(promises) {
    if (!Array.isArray(promises)) {
        throw new Error("must be an array");
    }
    return new Promise((resolve, reject) => {
        promises.forEach((p) => {
            Promise.resolve(p).then(
                (data) => {
                    resolve(data);
                },
                (err) => {
                    reject(err);
                }
            );
        });
    });
}
let fn = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(222);
    }, 1000);
});
promiseRace([fn, 2, 3])
    .then((res) => {
        console.log(res);
    })
    .catch((err) => {
        console.log(err);
    });

const now = ()=>{
  const [count,setCount] = useState(new Date())
  useEffect(()=>{
    timer = setTimeout(()=>{
      setCount(new Date())
    },1000)
    return ()=>{
      clearTimer(timer)
    }
  },[count])
}