EP28-zake搞Interview之JavaScript 2

117 阅读3分钟

1,手写题

参考文档:

  1. 前端进阶之必会的JavaScript技巧总结

1,函数柯里化

柯里化是高阶函数的一种特殊用法。接收函数作为参数的函数,都可以叫做高阶函数。 参考文档:

  1. 详解JS函数柯里化
    函数柯里化的目的是为了多参函数实现递归降解。其实现的核心是:
  2. 如何缓存每一次传入的参数。
  3. 传入的参数和目标函数的入参比较。 函数柯里化的好处:
  4. 参数复用
// 正常正则验证字符串 reg.test(txt)

// 函数封装后
function check(reg, txt) {
    return reg.test(txt)
}

check(/\d+/g, 'test')       //false
check(/[a-z]+/g, 'test')    //true

// Currying后
function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt)
    }
}

var hasNumber = curryingCheck(/\d+/g)
var hasLetter = curryingCheck(/[a-z]+/g)

hasNumber('test1')      // true
hasNumber('testtest')   // false
hasLetter('21212')      // false
  1. 提前确认
var on = function(element, event, handler) {
    if (document.addEventListener) {
        if (element && event && handler) {
            element.addEventListener(event, handler, false);
        }
    } else {
        if (element && event && handler) {
            element.attachEvent('on' + event, handler);
        }
    }
}

var on = (function() {
    if (document.addEventListener) {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.addEventListener(event, handler, false);
            }
        };
    } else {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.attachEvent('on' + event, handler);
            }
        };
    }
})();

//换一种写法可能比较好理解一点,上面就是把isSupport这个参数给先确定下来了
var on = function(isSupport, element, event, handler) {
    isSupport = isSupport || document.addEventListener;
    if (isSupport) {
        return element.addEventListener(event, handler, false);
    } else {
        return element.attachEvent('on' + event, handler);
    }
}
  1. 延迟执行
Function.prototype.bind = function (context) {
    var _this = this
    var args = Array.prototype.slice.call(arguments, 1)
 
    return function() {
        return _this.apply(context, args)
    }
}

1,柯里化通用式

const curry = (fn) => {
    let params = [];
    const next = (...args)=>{
        params = [...params, ...args];
        if(params.length < fn.length){
            return next;
        }else{
            return fn.apply(fn, params);
        }
    } 
    return next;
}

const sum = (a, b, c, d) => {
    return a + b + c + d;
}

const fn = curry(sum);
const res = fn(1)(2)(3);
console.log(res);

2,手写数组方法

1,map

Array.prototype.map = function (fn) {
    const result = [];
    for (let index = 0; index < this.length; index++) {
        if (!this.hasOwnProperty(index)) {
            continue;
        }  
        //递归调用
        result.push(fn(this[index], index, this))
    }
    return result;
    
}

const arr = [1, 2, 3];
const mapArr = arr.map(item => item * 2);
console.log(mapArr);

2,filter

Array.prototype.filter = function (fn) {
    const result = [];
    for (let index = 0; index < this.length; index++) {
        if (!this.hasOwnProperty(index)) {
            continue;
        }        
        fn(this[index], index, this) && result.push(this[index]);
    }
    return result;
    
}

const arr = [1, 2, 3];
const filterArr = arr.filter(item => item > 2);
console.log(filterArr);

3,reduce

Array.prototype.reduce = function(fn, initValue) {
    let result = initValue ? initValue : this[0];
    for(let i = initValue ? 1 : 0; i < this.length; i++){
        if(!this.hasOwnProperty(i)) continue;
        result = fn(result, this[i], i, this);
    }
    return result;
}

const arr1 = [1, 2, 3];
const reduceArr1 = arr1.reduce((a, b) => a * b, 2);
console.log(reduceArr1);

4,every

Array.prototype.every = function(fn) {
    let bool = true;
    for (let index = 0; index < this.length; index++) {
        if(!this.hasOwnProperty(index)){
            continue;
        }
        if(!fn(this[index], index, this)){
            bool = false;
        }
        return bool;
    }
}

const arr1 = [1, 2, 3, 5];
const reduceArr1 = arr1.every(item => item > 3);
console.log(reduceArr1);

5,some

Array.prototype.some = function(fn) {
    let bool = false;
    for (let index = 0; index < this.length; index++) {
        if(!this.hasOwnProperty(index)){
            continue;
        }
        if(fn(this[index], index, this)){
            bool = true;
            break;
        }
        return bool;
    }
}

const arr1 = [1, 2, 3, 5];
const reduceArr1 = arr1.some(item => item > 3);
console.log(reduceArr1);

6,find

Array.prototype.find = function(fn) {
    let findIndex = -1;
    for (let index = 0; index < this.length; index++) {
        if(!this.hasOwnProperty(index)){
            continue;
        }
        if(fn(this[index], index, this)){
            findIndex = index;
            break;
        }
    }
    return this[findIndex];

}

const arr1 = [1, 2, 3, 5];
const reduceArr1 = arr1.find(item => item > 3);
console.log(reduceArr1);

7, 数组扁平化

  1. 使用es6的flat方法,默认扁平一层,传入参数可以指定扁平几层,不改变原数组。
  1. 使用reduce方法
function reduceFlat(arr) {
    if(!Array.isArray(arr)) return;
    return arr.reduce((a, b) => a.concat(!Array.isArray(b) ? b : reduceFlat(b)), []);
}

arr1 = [1, [1, [1, 2]]];
let arr2 = reduceFlat(arr1);
console.log(arr2);
  1. 模拟栈实现数组拉平
function stackFlat(arr) {
    if(!Array.isArray(arr)) 
        return;
    const stackArr = [...arr];
    const flatArr = [];
    while(stackArr.length){
        let value = stackArr.shift();
        Array.isArray(value) ? stackArr.push(...value) : flatArr.push(value);
    }
    return flatArr;
}

let arr1 = [1, [2, [3]], 4];
let arr2 = stackFlat(arr1);
console.log(arr2);

3,图片懒加载

function lazyLoad(imgs) {
    let count = 0;
    const deleteImgs = [];
    const handle = ()=> {
        imgs.forEach((img, index) => {
            const react = img.getBoundingClientRect();
            if(react.top < window.innerHeight){
                img.src = dataset.src;
                count++;
                deleteImgs.push(index);
                if(count === img.length){
                    document.removeEventListener('scroll', lazyLoad);
                }
            }
        });
        imgs = imgs.filter((_, index) => {
            !deleteImgs.includes(index);
        })
    }
    return handle();
}

4,图片预加载

如果图片过大,可以预先将图片加载到缓存中,优化用户体验。

5,节流

1,实现1

const throttle = function (fn, waiting = 1000, options) {
    let preTime = new Date(0).getTime(),
        options = options || {
            firstTime: true,
            endTime: false
        },
        timer;
    let _throttle = function (...params) {
        let newTime = new Date().getTime();
        if (!options.firstTime) {
            if(timer) return;
            timer = setTimeout(() => {
                fn.apply(fn, args);
                timer = null;
            }, waiting);
        }else if(newTime - preTime > waiting){
            fn.apply(fn, args);
            preTime = newTime;
        }else if(options.endTime){
            timer = setTimeout(() => {
                fn.apply(fn, args);
                timer = null
            }, waiting);
        }
    };
    _throttle.cancel = () => {
        preTime = 0;
        clearTimeout(timer);
        timer = null;
    }

    return _throttle;
}

2,实现2

function throttle (f, wait) {
  let timer
  return (...args) => {
    if (timer) { return }
    timer = setTimeout(() => {
      f(...args)
      timer = null
    }, wait)
  }
}

需要节流的场景

  • scroll事件,每隔一秒计算一次位置信息。
  • input输入框实时搜索并展示下拉列表,

6,防抖

function debounce (f, wait) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      f(...args)
    }, wait)
  }
}

需要防抖的场景

  • 登录,发短信等按钮用户点击过多,为了避免发送过多请求
  • 文本编辑器实时保存,当无任何操作后一段时间自动保存

7,实现instanceof方法

function myInstanceOf(left, right) {
    if (typeof left !== 'object' || left == null) {
        return false;
    }
    
    let pro = Object.getPrototypeOf(left);
    while(true) {
        if (pro === null) {
            return false;
        }
        if (pro === right.prototype) {
            return true;
        }
        pro = Object.getPrototypeOf(pro);
    }
}

8,斐波那契数列

1,递归

function f(n) {
	if (n === 1 || n === 2) {
    	return 1;
    } else {
    	return f(n-1) + f(n-2);
    }
}

时间复杂度:O(2^n)
空间复杂度:O(n)
实现比较简洁,但是空间复杂度太大,容易导致栈溢出。

2,尾调用

function f(n, ac1=1, ac2=1) {
	if (n<=2) {
    	return ac2;
    } 
    return f(n-1, ac2, ac1+ac2);
}