javascript 中常见手写题

155 阅读3分钟

深拷贝(包含Symbol, 环)

JSON.parse/JSON.stringify

   ##缺点: 不能解决属性为函数,undefined, 循环引用的情况

递归实现 (该方法容易爆栈)

    function deepClone (source, hash=new WeakMap()) {
        // 第一步:判断是否为对象
        if (!isObject(source)) return source;
        // 第二步:判断是否为环
        if (hash.has(source)) return hash.get(source);
        // 以上都不满足
        let target = Array.isArray(source) ? [...source] : {...source};
        hash.set(source, target);

        Reflect.ownKeys(target).forEach(key => {
            if (isObject(source[key])) {
                target[key] = deepClone(source[key], hash);
            } else {
                target[key] = source[key];
            }
        })
        return target;
    }

广度优先遍历实现

    function cloneDeep(x) { 
        const root = {}; // 栈 
        const loopList = [{ 
                parent: root, 
                key: undefined, 
                data: x, 
            }]; 
        while(loopList.length) { 
            // 广度优先 
            const node = loopList.pop(); 
            const parent = node.parent; 
            const key = node.key; 
            const data = node.data; 
            // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素 
            let res = parent; 
            if (typeof key !== 'undefined') {
                res = parent[key] = {}; 
            } 
            for(let k in data) { 
                if (data.hasOwnProperty(k)) {
                    if (typeof data[k] === 'object') { 
                        // 下一次循环 
                        loopList.push({ parent: res, key: k, data: data[k], }); 
                    } else { 
                        res[k] = data[k]; 
                    } 
                } 
            } 
        } 
        return root; 
    }

防抖

    function debounce(fn, delay) {
        let timer = null;
        return function() {
            if(timer) {
                clearTimeout(timer);
            }
            timer = setTimeout(function () {
                fn.apply(this, arguments);
            }, delay)
        }
    }
    
    
    立即执行版本:
    
    function debounce(fn, delay, immediate) {
    
        let timer;
        
        return function(){
        
            clearTimeout(timer);
            
            if(immediate){
            
                const callNow = !timer;
                
                timer = setTimeout(function(){
                    timer = null;
                }, delay)
                
                if(callNow) {
                
                    fn.apply(this, arguments) 
                    
                };
                
            } else {
            
                timer = setTimeout(function(){
                    fn.apply(this, arguments)
                }, delay)
            
            }
        }
        
    }
    
    适用于:resize,搜索,点击按钮,DOM元素的拖拽等

节流

1. 时间戳
function throttle (fn, delay) {
    let pre = Date.now();
    return  function() {
        let current = Date.now();
        if(current - pre >= delay) {
            fn.apply(this, arguments);
            pre = Date.now();
        }
    }
}

2. 时间戳 + 定时器
function throttle(fn, delay) {
    let start = Date.now();
    let timer = null;
    return funtion () {
        let current = Date.now();
        let remaining = delay - (current - start);
        cleartTimeout(timer);
        if(remaining <= 0) {
            fn.apply(this, arguments);
            start = Date.now();
        } else {
            timer = setTimeout(fn, remaining)
        }
    }
}

适用于:scroll事件,

reduce

    用法:reduce(callback, initValue);
    
    Array.prototype.my_reduce = (callback, ...args) => {
        let start = 0;
        let pre;
        
        if(args.length > 0) {
            pre = args[0]
        } else {
            pre = this[0];
            start = 1;
        }
        for (let i = start; i < this.length; i++) {
            pre = callback(pre, this[i], i, this);
        }
        return pre;
    }

数组扁平化

        let ary = [1, [2, 3, [4, 5, [6, 6, 7, 8, [10]]]]];
        // 方法一:
        function a0(ary) {
            let str = ary.join(',');
            let newAry = str.split(',');
            let ret = newAry.map(item => {
                return parseInt(item)
            });
            return ret;
        }

        console.log(a0(ary))

        // 方法二:
        function a1(ary) {
            let res = [];
            for (let index = 0; index < ary.length; index++) {
                if (Array.isArray(ary[index])) {
                    res = res.concat(a1(ary[index]));
                } else {
                    res.push(ary[index]);
                }
            }
            return res;
        }

        console.log(a1(ary))

        // 方法三:
        function a2(ary) {
            let res = ary.reduce((pre, item) => {
                if (Array.isArray(item)) {
                    return pre.concat(a2(item));
                } else {
                    return pre.concat(item);
                }
            }, [])
            return res;
        }

        console.log(a2(ary))

        // 方法四:
        function a3(ary) {
            while (ary.some(item => Array.isArray(item))) {
                ary = [].concat(...ary);
            }
            return ary;
        }

        console.log(a3(ary))

        // 方法五:
        console.log(ary.flat(40))

数组去重

        let ary = [1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 66, 7, 8, 8, 8, 7, 5, 3];

        // 方法一:
        console.log([...new Set(ary)]);

        // 方法二:
        function a1(ary) {
            let res = [];
            for (let i = 0; i < ary.length; i++) {
                if (!res.includes(ary[i])) {
                    res.push(ary[i])
                }
            }
            return res;
        }
        console.log(a1(ary))

        // 方法三:
        function a2(ary) {
            let res = [];
            let obj = {};
            for (let i = 0; i < ary.length; i++) {
                if (obj[ary[i]]) {
                    obj[ary[i]] += 1;
                }else {
                    obj[ary[i]] = 1;
                    res.push(ary[i]);
                }
            }
            return res;
        }
        console.log(a2(ary))

        // 方法四:
        function a3(ary) {
            let res = [];
            for (let i = 0; i < ary.length; i++) {
                if (res.indexOf(ary[i]) < 0) {
                    res.push(ary[i])
                }
            }
            return res;
        }
        console.log(a3(ary))

        // 方法五:
        function a4(ary) {
            let res = [];
            res = ary.filter((item, index) => {
                return ary.indexOf(item) === index;
            })
            return res;
        }

        console.log(a4(ary))

new

    new 操作符所做的事情
    1.  创建一个空的简单JavaScript对象(即`{}`);
    2.  链接该对象(即设置该对象的构造函数)到另一个对象 ;
    3.  将步骤1新创建的对象作为`this`的上下文 ;
    4.  如果该函数没有返回对象,则返回`this`
    function create(){
        let Con = [].shift.call(arguments);
        let obj = Object.create(Con.prototype);
        let res = Con.apply(obj, arguments);
        return res instanceof Object? res : obj;
    }

instanceof

    function my_instanceof = function(L, R) {
        let Lv = L.__proto__;
        let Rv = R.prototype;
        while(true) {
            if(Lv === Rv) {
                return true;
            }
            if( Lv === null ) {
                return false;
            }
            Lv = Lv.__proto__;
        }
    }
    

快排

        function qucklySort(ary) {
            if (ary.length < 2) return ary;
            let middleIndex = parseInt(ary.length / 2);
            let middleValue = ary.splice(middleIndex, 1)[0];
            let left = [];
            let right = [];
            for (let index = 0; index < ary.length; index++) {
                if (ary[index] > middleValue) {
                    right.push(ary[index]);
                } else {
                    left.push(ary[index])
                }
            }
            return [...qucklySort(left), middleValue, ...qucklySort(right)];
        }
        
        
        优化:
        
        function qucklySort2(ary, left, right) {
            if (left < right) {
                let pos = left - 1;
                for (let index = left; index <= right; index++) {
                    let posV = ary[right];
                    if (ary[index] <= posV) {
                        pos++;
                        let temp = ary[pos]
                        ary[pos] = ary[index]
                        ary[index] = temp
                    }
                }
                qucklySort2(ary, left, pos - 1);
                qucklySort2(ary, pos + 1, right);
            }
            return ary;
        }

冒泡

        function bubbleSort(ary) {
            for (let index = 0; index < ary.length; index++) {
                let flag = true;
                for (let k = 0; k < ary.length - index - 1; k++) {
                    if (ary[k] > ary[k + 1]) {
                        flag = false;
                        [ary[k], ary[k + 1]] = [ary[k + 1], ary[k]]
                    }
                }
                if (flag) {
                    return;
                }
            }
            return ary;
        }

forEach

    arr.forEach(cabllback);
    
    Array.prototype.my_forEach = function(callback) {
        for(let index = 0; index < this.length; index++) {
            callback(this[index], index, this);
        }
    }

map


    map(callback)
    
    Array.prototype.my_map = funtion (callback) {
        let res = [];
        
        for(let index = 0; index < this.length; index++) {
            res.push(callback(this[index], index, this));
        }
        
        return res;
    }

二分查找


        let ary = [1, 3, 3121, 6, 0, 31, 4, 2, 12, 53, 7, 434, 8, 9];

        function binary(ary, target) {
            let len = ary.length;
            if (len === 0) return -1;
            ary.sort((a, b) => a - b);
            let low = 0;
            let high = len - 1;
            while (low <= high) {
                let middleIndex = Math.floor((high + low) / 2);
                if (ary[middleIndex] === target) {
                    return "存在";
                } else if (ary[middleIndex] < target) {
                    low = middleIndex + 1;
                } else if (ary[middleIndex] > target) {
                    high = middleIndex - 1
                } else {
                    return '不存在';
                }
            }
        }

Object.create

    function create(o){
        let F = function(){};
        F.prototype = o;
        let obj = new F();
        return obj;
    }

call,apply,bind

call

Function.prototype.my_call = function(obj, ...args) {
    obj.fn = this;
    let res = obj.fn(...args);
    delete obj.fn;
    return res;
}

apply

Function.prototype.my_apply = function(obj, ary) {
    obj.fn = this;
    let res;
    if(ary && ary.length > 0) {
        res = obj.fn(...ary);
    } else {
        res = obj.fn()
    }
    delete obj.fn;
    return res;
}

bind

Function.prototype.my_bind = function(obj, ...args) {
    obj.fn = this;
    return function (){
        const newArr = args.concat(...arguments);
        let res = obj.fn(...newArr);
        delete obj.fn;
        return res;
    }
}

promise

promise.finally

    finally(callback) {
        return this.then(
            value => Promise.resolve(callback()).then(() => value),
            reason => Promise.resolve(callback()).then(() => {throw reason })
        )
    }

promise.race

    race(promiseArr) {
        return new Promise((resolve, reject) => {
            for(let item of promiseArr) {
                Promise.resolvue(item).then(
                    value => resolve(value),
                    err => reject(err)
                )
            }
        })
    }

promise.all

all(promiseArr) {
    let index = 0;
    let res = [];
    return new Promise((resolve, reject) => {
        promiseArr.forEach((item, index) => {
            Promise.resolve(item).then(
                val => {
                    index++;
                    res[index] = val;
                    if(index === promiseArr.length) {
                        reslove(res);
                    }
                },
                err => {
                    reject(err)
                }
            )
        })
    })
}

柯里化

    add(1)(2)(3)(4)(5).toStrng();//有待优化,怎么能去掉toString().
    
    function add(){
        let args = Array.prototype.slice.call(arguments);
        let _innerAdd = function(){
            args.push(...arguments);
            return _innerAdd;
        }
        
        _innerAdd.toString = function(){
            return args.reduce((a,b) => a+b);
        }
        return _innerAdd;
    }
    

compose函数

    
    function compose(...args1) {
        return function (...args2) {
            if(args1.length === 0) {
                return args2;
            }
            if(args1.length === 1) {
                return args1[0](...args2);
            }
            return args1.reduce(function(pre, next) => {
                return Array.isArray(pre) ? next(...pre) : next(pre);
            }, args2)
        }
    }
    
    
    
    const fn1 = x => x+10; 
    const fn2 = x => x-10; 
    const fn3 = x => x*10; 
    const fn4 = x => x/10;
    
    let res = compose(fn1,fn2,fn3,fn4)(20); 
    console.log(res);

filter

    filter(callback);
    
    Array.prototype.my_filter = function(callback) {
        let res = [];
        for(let index = 0; index< this.length; index++) {
            callback(this[index], index, this) && res.push(this[index]);
        }
        return res;
    }

LRU(最近最少使用)

        let LRUcache = function(capacity) {
            let cache = new Map();
            this.capacity = capacity;
        }
        LRUcache.prototype.get = function(key) {
            if (this.cache.has(key)) {
                let temp = this.cache.get(key);
                this.cache.delete(key);
                this.cache.set(key, temp);
                return temp
            }
            return -1;
        }
        LRUcache.prototype.set = function(key, value) {
            if (this.cache.has(key)) {
                this.cache.delete(key);
            } else if (this.cache.size >= this.capacity) {
                this.cache.delete(this.cache.keys().next().value);
            }
            this.cache.set(key, value);
        }

sleep函数

function sleep(time) {
    return new Promise((resolve) => setTimeout(resolve, time));
}

归并排序

使用setTimeout模拟setInterval

    let timer = null;
    
    function interval(fn ,wait) {
    
        let interv = function(){
            fn.call(null);
            timer = setTimeout(interv, wait);
        }
        
        timer = setTimeout(interv, wait)
        
    }

rem的实现原理

    function setRem () {
        let doc = document.documentElement;
        
        let width = doc.getBoundingClientRect().width;
        
        ler rem = width / 7.5;
        
        doc.style.fontSize = rem;
    }
    
    window.addEventLinstener('resize', setRem());
    

将金额转换为千分位写法

     function money2String(money) {
          let moneyStr = money.toString();
          let moneyAry = moneyStr.split('');
          let res = [];
          while (moneyAry.length > 3) {
            res.unshift(moneyAry.splice(moneyAry.length - 3, 3))
          }
          res.unshift(moneyAry);

          let s = res.map((item) => {
            return item.join('')
          })
          return s.join(',');
    }
    

数组find

数组from

数组findIndex

数组some

数组every