前端面试知识点整理——常见手撕代码

前端面试知识点整理——常见手撕代码

文章目录

      • 一、排序
        • 1.冒泡排序
        • 2.选择排序
        • 3.插入排序
        • 4.快速排序
        • 5.推排序
        • 6.归并排序
      • 二、防抖和节流
        • 1.防抖
        • 2.节流
      • 三、函数柯里化
      • 四、清除前后空格
      • 五、数组拍平
      • 六、图片懒加载
      • 七、深拷贝
      • 八、bind、apply、call
        • 1.bind
        • 2.apply
        • 3.call
      • 九、发布-订阅模式
      • 十、promise
      • 十一、promise.all
      • 十二、promise.race
      • 十三、jsonp
      • 十四、new原理实现
      • 十五、instanceof原理实现
      • 十六、setTimeout实现setInterval

一、排序

1.冒泡排序

function bubbleSort(nums) {
    //每轮循环都从最后一个元素开始 比较并交换 一次循环会把最小的数顶到最上面
    for (let i = 0; i < nums.length - 1; i++) { //只是控制次数为n-1
        for (let j = nums.length - 1; j > i; j--) {
            if (nums[j] < nums[j - 1]) {
                //交换
                let temp = nums[j];
                nums[j] = nums[j - 1];
                nums[j - 1] = temp;
            }
        }
    }
}

//test
nums = [5, 1, 4, 2, 8];
bubbleSort(nums);
console.log(nums);

2.选择排序

function selectionSort(nums) {
    //和冒泡排序类似,但是并不在每次比较后交换 而是记录最小值(初识最小值为nums[i]) 最后再交换一次 
    //每次循环也是从最后开始 把最小元素放到最顶部
    for (let i = 0; i < nums.length - 1; i++) { //n -1循环
        let index = i;
        for (let j = nums.length - 1; j > i; j--) {
            if (nums[j] < nums[index]) {
                index = j;
            }
        }
        //交换
        let temp = nums[i];
        nums[i] = nums[index];
        nums[index] = temp;
    }
}

//test
nums = [5, 1, 4, 2, 8];
selectionSort(nums);
console.log(nums);

3.插入排序

function insertionSort(nums) {
    //插入排序 从第二个元素开始 把元素插入到合适的位置 每次比较(除了最后一次)都要交换
    for (let i = 1; i < nums.length; i++) {
        for (let j = i; j > 0; j--) {
            if (nums[j] < nums[j - 1]) {
                //交换
                let temp = nums[j];
                nums[j] = nums[j - 1];
                nums[j - 1] = temp;
            } else {
                break;
            }
        }
    }
}

//test
nums = [5, 1, 4, 2, 8];
insertionSort(nums);
console.log(nums);

4.快速排序

//快速排序最差O(n^2)最优O(nlogn)
function qsort(nums, l, r) {
    if (r <= l) return; //注意定义递归中止条件
    let pivot = nums[l]; //选择最左为轴值
    swap(nums, l, r); //把轴值与最右交换

    let i = l;
    for (let j = l; j < r; j++) {
        if (nums[j] < pivot) {
            swap(nums, i, j);
            i++;
        }
    }
    swap(nums, i, r); //此时i为轴值下标
    qsort(nums, l, i - 1);
    qsort(nums, i + 1, r);
}
function swap(nums, i, j) {
    let temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

//test
nums = [5, 1, 4, 2, 8, 11, 2, 3];
qsort(nums, 0, nums.length - 1);
console.log(nums);

5.推排序

//堆排序 建堆O(n) 删除n个O(nlogn)
function heapSort(nums) {
    if (nums.length <= 1) return nums;
    let res = [];
    let heapSize = nums.length;
    buildHeap(nums, heapSize);
    for (let i = 0; i < nums.length; i++) {
        res.push(nums[0]);
        swap(nums, 0, --heapSize);
        siftDown(nums, 0, heapSize);
    }
    return res;
}
//建堆 (最小堆)
function buildHeap(nums, heapSize) {
    for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
        siftDown(nums, i, heapSize);
    }
}
//siftDown 
function siftDown(nums, i, heapSize) {
    let smallest = i;
    let l = 2 * i + 1;
    let r = 2 * i + 2;
    if (l < heapSize && nums[l] < nums[smallest]) {
        smallest = l;
    }
    if (r < heapSize && nums[r] < nums[smallest]) {
        smallest = r;
    }
    if (smallest != i) {
        swap(nums, i, smallest);
        siftDown(nums, smallest, heapSize);
    }
}
function swap(nums, i, j) {
    let temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

//test
nums = [5, 1, 4, 2, 8, 11, 2, 3];
let res = heapSort(nums);
console.log(res);

6.归并排序

function mergeSort(nums) {
    if (nums.length <= 1) return nums;
    let middle = Math.floor(nums.length / 2);
    let arr1 = nums.slice(0, middle);
    let arr2 = nums.slice(middle);
    return merge(mergeSort(arr1), mergeSort(arr2));
}
function merge(arr1, arr2) {
    let res = [];
    while (arr1.length > 0 && arr2.length > 0) {
        if (arr1[0] <= arr2[0]) {
            res.push(arr1.shift());
        } else {
            res.push(arr2.shift());
        }
    }
    return res.concat(arr1).concat(arr2);
}
//test
nums = [5, 1, 4, 2, 8, 11, 2, 3];
let res = mergeSort(nums);
console.log(res);

二、防抖和节流

1.防抖

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <input type="text">
    <script>
        const input = document.querySelector('input');
        function debounce(fn, delay) {
            let timer = null;
            return function () {
                if (timer) {
                    clearTimeout(timer);
                }
                timer = setTimeout(() => {
                    fn();
                }, delay)
            }
        }
        input.onkeyup = debounce(() => {
            console.log(input.value);
        }, 3000)
    </script>
</body>

</html>

2.节流

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <input type="text">
    <script>
        const input = document.querySelector('input');
        function throttle(fn, delay) {
            let timer = null;
            return function () {
                if (timer) {
                    return;
                }
                timer = setTimeout(() => {
                    fn();
                    timer = null;
                }, delay)
            }
        }
        input.onkeyup = throttle(() => {
            console.log(input.value);
        }, 3000)
    </script>
</body>

</html>

三、函数柯里化

function sum(...args) {
    //对第一层参数求和
    let x = args.reduce((acc, next) => {
        return acc + next;
    })
    //返回一个新的函数 
    return function (...args2) { //第二层的参数
        //当没有第二层参数时,返回x即可
        if (args2.length == 0) return x;
        let y = args2.reduce((acc, next) => {
            return acc + next;
        })
        return sum(x + y); //返回x+y作为新的第一层
    }
}

console.log(sum(1, 2)(3)(4)()); // 10
console.log(sum(1)(2, 3, 4, 5)(10)()); // 25

//自己写一个减法的 天才!
function decrease(...args) {
    let x = args.reduce((acc, next) => {
        return acc - next;
    })
    console.log(x);
    return function (...args2) {
        if (args2.length == 0) return x;
        let y = args2.reduce((acc, next) => {
            return acc - next;
        }, 0)
        console.log(y);
        return decrease(x + y);
    }
}
console.log(decrease(1, 2)(3)(4, 5)()); // -13
console.log(decrease(1)(2, 3, 4, 5)(10)()); // -23

四、清除前后空格

String.prototype.trim = function () {
    return this.replace(/^\s+/g, "").replace(/\s+$/g, "");
}
let str = '   123   ';
console.log(str);
let str1 = str.trim();
console.log(str1);

五、数组拍平

function flatten(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            res = res.concat(flatten(arr[i]));
        } else {
            res.push(arr[i]);
        }
    }
    return res;
}

let arr = [1, 2, 3, [4, 5, 6, [7, 8, 9]], 10];
console.log(flatten(arr));

六、图片懒加载

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        img {
            display: block;
            width: 100%;
            height: 300px;
            margin-bottom: 10px;
        }
    </style>
</head>

<body>
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
</body>
<script>
    var imgs = document.querySelectorAll('img');

    //获得元素距离页面顶部的距离
    function getTop(e) {
        var T = e.offsetTop;
        while (e = e.offsetParent) {
            T += e.offsetTop;
        }
        return T;
    }

    function lazyLoad(imgs) {
        //获取可视区高度
        var H = window.innerHeight || document.documentElement.clientHeight;
        //获取被卷页面高度
        var S = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
        for (let i = 0; i < imgs.length; i++) {
            if (H + S > getTop(imgs[i])) {
                imgs[i].src = imgs[i].getAttribute('data-src');
            }
        }
    }

    window.onload = window.onscroll = function () {
        lazyLoad(imgs);
    }
</script>

</html>

七、深拷贝

function deepClone(obj) {
    var clone;
    if (obj && typeof obj !== 'object') clone = obj;
    else {
        clone = Array.isArray(obj) ? [] : {};
        for (let k in obj) {
            if (obj.hasOwnProperty(k)) {
                if (obj[k] && typeof obj[k] === 'object') {
                    clone[k] = deepClone(obj[k]);
                } else {
                    clone[k] = obj[k];
                }
            }
        }
    }
    return clone;
}

//test
let obj = {
    name: 'vivian',
    age: 18,
    mark: {
        one: 1,
        two: 2
    },
    arr: [1, 2, 3]
}

let clone = deepClone(obj);
console.log(clone);

八、bind、apply、call

1.bind

//bind改变this指向,不调用函数
Function.prototype.mybind = function (context, ...arg) {
    let fun = this;
    return function () {
        return fun.apply(context, arg)
    }
}

//test
let obj = {
    name: 'vivian'
}
function test(arg1, arg2, arg3) {
    console.log(this.name);
    console.log(arg1, arg2, arg3);
}
let f = test.bind(obj, 1, 2, 3); //返回一个函数
f();

2.apply

//apply改变this指向 调用函数 参数为数组
Function.prototype.myApply = function (context, arg) {
    context.fn = this;
    let res;
    if (!arg) {
        res = context.fn();
    } else {
        res = context.fn(...arg);
    }
    delete context.fn;
    return res;
}

//test
let obj = {
    name: 'vivian'
}
function test(arg1, arg2, arg3) {
    console.log(this.name);
    console.log(arg1, arg2, arg3);
}
test.myApply(obj, [1, 2, 3]);

3.call

//call改变this指向 调用函数
Function.prototype.mycall = function (context, ...arg) {
    context.fn = this; //this是test函数,把他作为context的方法而存在
    let res = context.fn(...arg);
    delete context.fn;
    return res;
}
//test
let obj = {
    name: 'vivian'
}
function test(arg1, arg2, arg3) {
    console.log(this.name);
    console.log(arg1, arg2, arg3);
}
test.mycall(obj, 1, 2, 3);

九、发布-订阅模式

class EventEmitter {
    constructor() {
        //缓存列表
        this.list = {};
    }
    //on:把callback函数注册到缓存列表中
    on(eventName, callback) {
        if (!this.list[eventName]) {
            //当还没有这个事件时,为这个事件添加一个缓存列表
            this.list[eventName] = [callback];
        } else {
            //当已经存在这个事件的缓存列表之后,直接添加
            this.list[eventName].push(callback);
        }
    }
    //emit:根据event去执行对应缓存列表里面的函数
    emit(eventName, ...args) {
        this.list[eventName].forEach(fn => fn.apply(this, args));
    }
    //once:只监听一次
    once(eventName, callback) {
        const fn = () => {
            callback();
            this.remove(eventName, fn);
        }
        this.on(eventName, fn);
    }
    //remove:取消订阅
    remove(eventName, callback) {
        this.list[eventName] = this.list[eventName].filter(fn => fn != callback);
    }
}

十、promise

const PEDDING = Symbol();
const FULFILLED = Symbol();
const REJECTED = Symbol();

const myPromise = function (fn) {
    this.status = PEDDING;
    this.value = '';

    const resolve = (value) => {
        this.status = FULFILLED;
        this.value = value;
    }
    const reject = (error) => {
        this.status = REJECTED;
        this.value = error;
    }

    this.then = (onFulfilled, onRejected) => {
        if (this.status == FULFILLED) {
            onFulfilled(this.value);
        }
        if (this.status == REJECTED) {
            onRejected(this.value);
        }
    }

    try {
        fn(resolve, reject);
    } catch (error) {
        reject(error);
    }
}



//test 
let p = new myPromise((resolve, reject) => {
    resolve(123);
})
p.then(value => {
    console.log(value);
})
let p2 = new Promise((resolve, reject) => {
    resolve(456);
})
p2.then(value => {
    console.log(value);
})

十一、promise.all

function isPromise(obj) {
    return !!obj && (typeof obj === 'function' || typeof obj === 'object') && typeof obj.then == 'function';
}

function myPromiseAll(arr) {
    let res = []
    return new Promise((resolve, reject) => {
        for (let i = 0; i < arr.length; i++) {
            if (isPromise(arr[i])) {
                arr[i].then(data => {
                    res[i] = data;
                    if (res.length === arr.length) {
                        resolve(res)
                    }
                }).catch(error => {
                    reject(error)
                })
            } else {
                res[i] = arr[i];
            }
        }
    })
}

//test
var p1 = Promise.resolve('a');
var p2 = Promise.resolve('b');
var p3 = Promise.resolve('c');
Promise.all([p1, p2, p3]).then(function (value) {
    console.log(value);
})
const ptest = myPromiseAll([p1, p2, p3]);
ptest.then(value => {
    console.log(value);
})

十二、promise.race

function myPromiseRace(arr) {
    return new Promise((resolve, reject) => {
        for (let i = 0; i < arr.length; i++) {
            return arr[i].then(resolve, reject);
        }
    })
}

//test 
const p1 = Promise.reject('a');
const p2 = Promise.resolve('b');
const p3 = Promise.resolve('c');

Promise.race([p1, p2, p3]).then(value => {
    console.log(value);
}).catch(error => {
    console.log(error);
})

myPromiseRace([p1, p2, p3]).then(value => {
    console.log(value);
}).catch(error => {
    console.log(error);
})

十三、jsonp

let newscript = document.createElement('script');
newscript.src = 'https://www.abc.com?callback=fn';;
document.body.appendChild(newscript);
function fn(data) {
    console.log(data);
}

十四、new原理实现

function _new() {
    //1. 创建一个新对象
    let obj = new Object();
    //2. 让this指向这个对象
    let [func, ...arg] = [...arguments];
    obj.__proto__ = func.prototype;
    //3. 执行构造函数里面的代码
    let res = func.apply(obj, arg);
    //4. 返回这个对象
    if (res && (typeof res === 'object') || (typeof res === 'Function')) {
        return res;
    } else {
        return obj;
    }
}

function Person(uname, age) {
    this.uname = uname;
    this.age = age;
}

let obj1 = new Person('vivian', 18);
let obj2 = _new(Person, 'vivian', 18);
console.log(obj1, obj2);

十五、instanceof原理实现

function instance_of(left, right) {
    let prototype = right.prototype;
    left = left.__proto__;
    while (true) {
        if (!left) return false;
        if (left == prototype) return true;
        left = left.__proto__;
    }
}
console.log([] instanceof Array);
console.log(instance_of([], Array));

十六、setTimeout实现setInterval

function myInterval(fn, delay) {
    let content = this;
    setTimeout(() => {
        fn.call(content);
        myInterval(fn, delay); //递归
    }, delay);
}

myInterval(() => {
    console.log(1);
}, 2000)

你可能感兴趣的:(前端面试,javascript,前端,面试)