美文网首页
面试秘籍之手写系列

面试秘籍之手写系列

作者: River_何 | 来源:发表于2021-12-16 09:05 被阅读0次

    一、手写call函数

    Function.prototype.myCall = function (context, ...args) {
        if (typeof this !== 'function') {
            throw new TypeError('not a function');
        }
        if (context === undefined || context === null) {
            // 如果传入的上下文对象是undefined或null的话,则直接使用window对象作为上下文对象
            context = window;
        } else {
            context = Object(context);
        }
        var specialPrototype = Symbol('specialPrototype'); // 给上下文对象添加的临时属性
        context[specialPrototype] = this; // 将this函数添加到上下文对象上
        var result = context[specialPrototype](...args); // 调用上下文对象上的方法获取执行结果
        delete context[specialPrototype]; // 删除上下文对象上添加的临时属性
        return result; // 返回函数执行结果
    }
    

    二、手写bind函数

    Function.prototype.myBind = function (thisArg) {
        if (typeof this !== 'function') {
            throw new TypeError('not a function');
        }
        var that = this;
        var args = Array.prototype.slice.call(arguments, 1);
    
        var fn = function () {}
    
        var fnBound = function () {
            const _this = this instanceof fn ? this : thisArg;
            const newArgs = args.concat(Array.prototype.slice.call(arguments));
            return that.apply(_this, newArgs);
        }
    
        if (this.prototype) {
            fn.prototype = this.prototype;
        }
        fnBound.prototype = new fn();
        fnBound.prototype.constructor = this;
        return fnBound;
    };
    

    三、手写实现new功能的函数

    function myNew(fn, ...args) {
        // 创建一个空的对象,将实例化对象的原型指向构造函数的原型对象
        const instance = Object.create(fn.prototype);
        // 将构造函数的this指向实例化对象
        const res = fn.apply(instance, args);
        // 判断返回值,如果函数返回值为基本数据类型时, 则new出的对象依然是创建出的对象
        return res instanceof Object ? res : instance;
    }
    

    四、手写reduce函数

    Array.prototype.myReduce = function (fn, initialValue) {
        // 判断调用对象是否为数组
        if (Object.prototype.toString.call(this) !== '[object Array]') {
            throw new TypeError('not a array');
        }
        // 判断调用数组是否为空数组
        if (this.length === 0) {
            throw new TypeError('empty array');
        }
        // 判断传入的第一个参数是否为函数
        if (typeof fn !== 'function') {
            throw new TypeError(`${fn} is not a function`);
        }
    
        // 回调函数参数初始化
        var sourceArray = this;
        var result,  currentValue, currentIndex;
        if (initialValue !== undefined) {
            result = initialValue;
            currentIndex = 0;
        } else {
            result = sourceArray[0];
            currentIndex = 1;
        }
    
        // 开始循环
        while (currentIndex < sourceArray.length) {
            if (Object.prototype.hasOwnProperty.call(sourceArray, currentIndex)) {
                 currentValue = sourceArray[currentIndex];
                 result = fn(result, currentValue, currentIndex, sourceArray);
            }
            currentIndex++;
        }
    
        // 返回结果
        return result;
    }
    

    五、手写防抖函数

    function debounce(fn, delay=300) {
        if (typeof fn !== 'function') {
            throw new TypeError(`${fn} is not a function`);
        }
        let timer = null;
        return (...args) => {
             if (timer) {
                 clearTimeout(timer);
             }
             timer = setTimeout(() => {
                 fn.apply(this, args);
            }, delay);
        }
    }
    

    六、手写节流函数

    function throttle(fn, duration=500) {
        if (typeof fn !== 'function') {
            throw new TypeError(`${fn} is not a function`);
        }
        let lastTime = new Date().getTime();le
        return (...args) => {
             const now = new Date().getTime();
             if (now - lastTime >= duration) {
                 fn.apply(this, args);
                 lastTime = now;
             }
        }
    }
    

    七、手写Promise类 (实现了Promise/A+ 的大部分规范)

    // 定义promise的三个状态值
    var promiseStatus = {
        PENDING: "pending",
        FULFILLED: "fulfilled",
        REJECTED: "rejected",
    }
    
    function MyPromise(task) {
        if(typeof task !== "function") {
            throw new TypeError(`${task} is not a function`);
        }
        this.status = promiseStatus.PENDING; // 设置初始状态
        this.value = undefined;
        this.thenCallback = undefined;
        this.catchCallback = undefined;
        var _this = this; // 缓存this对象
    
        var resolve = function(value) {
            if (_this.status === promiseStatus.PENDING) {
                _this.status = promiseStatus.FULFILLED;
                _this.value = value;
                if(value instanceof MyPromise) {
                    value.then(function(res) {
                        if (_this.thenCallback) {
                            _this.thenCallback(res);
                        } else if (_this.catchCallback) {
                            _this.catchCallback(res);
                        }
                    });
                } else {
                    // 这里使用setTimeout来模拟异步任务,实际promise是微任务,回调函数会放在微任务队列中
                    setTimeout(function() {
                        if (_this.thenCallback) {
                            _this.thenCallback(_this.value);
                        } else if (_this.catchCallback) {
                            _this.catchCallback(_this.value);
                        }
                    });
                }
            }
        }
                    
        var reject = function(errValue) {
            if (_this.status === promiseStatus.PENDING) {
                _this.status = promiseStatus.REJECTED;
                _this.value = errValue;
                // 这里使用setTimeout来模拟异步任务,实际promise是微任务,回调函数会放在微任务队列中
                setTimeout(function() {
                    if (_this.catchCallback) {
                        _this.catchCallback(_this.value);
                    } else if (_this.thenCallback) {
                        _this.thenCallback(_this.value);
                    }
                });
            }
        }
    
        try {
            task(resolve, reject);
        } catch(err) {
            reject(err);
        }
    }
    
    MyPromise.prototype.then = function(onFulfilledCallback, onRejectedCallback) {
        var _this = this;
        // 返回promise对象,保证链式调用
        return new MyPromise(function(resolve, reject) {
            if (typeof onFulfilledCallback === "function") {
                _this.thenCallback = function(value) {
                    /**
                     * 因为在使用链式调用的时候可能第一个调用的不是then
                     * 所以我们在做检测时会借助then来将catch的信息向下传递 
                     * 所以我们检测到触发thenCallback的Promise对象的状态是rejected时
                     * 我们就继续调用下一个Promise对象的reject
                     */
                    if (_this.status === promiseStatus.REJECTED) {
                        reject(value);
                    } else {
                        // 用户传入的方法执行时都要用try包裹
                        try {
                            var res = onFulfilledCallback(value);
                            if(res instanceof MyPromise && res.status === promiseStatus.REJECTED) {
                                res.catch(function(errValue) {
                                    reject(errValue);
                                });
                            } else {
                                resolve(res);
                            }
                        } catch(err) {
                            reject(err);
                        }
                    }
                };
            }
            if (typeof onRejectedCallback === "function") {
                _this.catchCallback = function(errValue) {
                    /**
                     * 因为在使用链式调用的时候可能第一个调用的不是catch
                     * 所以我们在做检测时会借助catch来将then的信息向下传递
                     * 所以我们检测到触发catchCallback的Promise对象的状态是fulfilled时
                     * 我们就继续调用下一个Promise对象的resolve
                     */
                    if (_this.status === promiseStatus.FULFILLED) {
                        resolve(errValue);
                    } else {
                        // 用户传入的方法执行时都要用try包裹
                        try {
                            var res = onRejectedCallback(errValue);
                            if(res instanceof MyPromise && res.status === promiseStatus.REJECTED) {
                                res.catch(function(errValue) {
                                    reject(errValue);
                                });
                            } else {
                                resolve(res);
                            }
                        } catch(err) {
                            reject(err);
                        }
                    }
                }
            }
        });
    }
    
    MyPromise.prototype.catch = function(onRejectedCallback) {
        return this.then(null, onRejectedCallback);
    }
    
    MyPromise.prototype.finally = function (onFinallyCallback) {
        return this.then(function (res) {
            onFinallyCallback();
            return res;
        }, function(err) {
            onFinallyCallback();
            throw new Error(err);
        });
    }
    
    MyPromise.resolve = function(value) {
        return new MyPromise(function(resolve, reject) {
            resolve(value);
        });
    }
    
    MyPromise.reject = function(errValue) {
        return new MyPromise(function(resolve, reject) {
            reject(errValue);
        });
    }
    
    MyPromise.all = function (promiseArr) {
        var resArr = [];
        return new MyPromise(function(resolve, reject) {
            promiseArr.forEach(function(item, index) {
                item.then(function(res) {
                    resArr[index] = res;
                    var allResolve = promiseArr.every(function(_item) {
                        return _item.status === promiseStatus.FULFILLED;
                    })
                    if (allResolve) {
                        resolve(resArr);
                    }
                }).catch(function(err) {
                    reject(err);
                })
            });
        });
    }
    
    MyPromise.race = function (promiseArr) {
        return new MyPromise(function(resolve, reject) {
            promiseArr.forEach(function(item, index) {
                item.then(function(res) {
                    resolve(res);
                }).catch(function(err) {
                    reject(err);
                });
            });
        });
    }
    
    MyPromise.allSettled = function (promiseArr) {
        var resAll = [];
        return new MyPromise(function (resolve, reject) {
            promiseArr.forEach(function (item, index) {
                item.then(function (res) {
                    const obj = {
                        status: promiseStatus.FULFILLED,
                        value: res,
                    }
                    resArr[index] = obj;
                    var allResolve = promiseArr.every(function(_item) {
                        return _item.status !== promiseStatus.PENDING;
                    });
                    if (allResolve) {
                        resolve(resArr);
                    }
                }).catch(function (err) {
                    const obj = {
                        status: promiseStatus.REJECTED,
                        value: err,
                    }
                    resArr[index] = obj;
                    var allResolve = promiseArr.every(function (_item) {
                        return _item.status !== promiseStatus.PENDING;
                    });
                    if (allResolve) {
                        resolve(resArr);
                    }
                });
            })
        });
    }
    

    八、手写XMLHttpRequest发送请求

    function request(method, url, params){
        // 初始化实例
        let xhr;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        }else {
            xhr = new ActiveXObject("microsoft.XMLHTTP");
        }
        method = method ? method.toUpperCase() : 'GET';
        if (method === 'POST') {
            xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        }
        xhr.open(method , url, true);
    
        xhr.onreadystatechange = function () {
            // 只有readyState === 4 和 status === 200,才会正常返回数据
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                     // 通过JSON.parse()把test.json里面的数据转换为json对象
                     console.log(JSON.parse(xhr.responseText))
                } else {
                     console.log('其他情况')
                }
            }
        }
    
        xhr.sent(method === 'GET' ? null : JSON.stringify(params));
    }
    

    九、手写深拷贝deepClone函数

    function deepClone (value, map = new WeakMap()) {
        let newValue = value;
        if (value && typeof obj === 'object') {
            // 使用map防止循环引用,检查map中有没有,如果有被记录则直接返回
            const oldValue = map.get(value);
            if ( oldeValue ) {
                return oldValue;
            }
            // 如果没有被记录,则创建新的对象
            newValue = value.constructor == Array ? [] : {};
            // 记录该引用
            map.set(value, newValue);
            for (let key in value) {
                const item = value[key];
                newValue[key]=item && typeof item === 'object'  ? arguments.callee(item, map) : item;
            }     
        }
        return newValue;
    }
    

    十、手写一个比较完美的继承

    function inheritPrototype(subType, superType){
        var protoType = Object.create(superType.prototype);    //创建对象
        protoType.constructor = subType;   //增强对象
        subType.prototype = protoType;   //指定对象
    }
    
    function SuperType(name){
        this.name = name;
        this.colors = ["red", "blue", "green"];
    }
    SuperType.prototype.sayName = function(){
        console.log('name', this.name);
    }
    
    function SubType(name, age){
        SuperType.call(this, name);
        this.age = age;
    }
    
    inheritPrototype(SubType, SuperType)
    SubType.prototype.sayAge = function(){
        console.log('age', this.age);
    }
    
    var instance = new SubType("Bob", 18);
    instance.sayName();
    instance.sayAge();
    

    更多个人文章

    1. 两个跨域页面进行跳转传参的终极方案
    2. 深入理解Event Loop的运行机制
    3. hashHistory和browserHistory的区别
    4. 面试秘籍之排序算法

    相关文章

      网友评论

          本文标题:面试秘籍之手写系列

          本文链接:https://www.haomeiwen.com/subject/fhxsfrtx.html