Front-end interview questions and answers (2022 latest version)

Collect and sort out the latest front-end interview questions and answers in 2022, which is convenient for reviewing and remembering at ordinary times. Welcome everyone to add.

Generally speaking, it is almost enough to be proficient in writing the high-frequency inscriptions in the following basics. Of course, it is not enough to go to the big factory, and you have to do some algorithm questions.

Base

high frequency

1. Handwritten instanceof

// 原理:验证当前类的原型prototype是否会出现在实例的原型链proto上,只要在它的原型链上,则结果都为true
function myinstanceOf_(obj, class_name) {
      // let proto = obj.__proto__;
    let proto = Object.getPrototypeOf(obj)
    let prototype = class_name.prototype
    while (true) {
        if (proto == null) return false
        if (proto == prototype) return true
          // proto = proto.__proto__;
        proto = Object.getPrototypeOf(proto)
    }
}

​2. Handwritten new operator

function myNew(){
    //1.创建一个新的对象
    let obj=new Object();
    //获得构造函数
    let con = [].shift.call(arguments); //[]为Array构造函数的实例  将类数组转化为真正的数组
    //2.新对象的隐式原型__proto__链接到构造函数的显式原型prototype
    obj.__proto__ = con.prototype;
    //3.构造函数内部的 this 绑定到这个新创建的对象 执行构造函数
    let result = con.apply(obj, arguments)
    //4.如果构造函数没有返回非空对象,则返回创建的新对象
    return typeof result == 'object' ? result:obj;
}
var test_create = myNew(Car, 'a', 'b', 'c');
console.log(test_create)

3. Handwritten call, apply, bind functions

  • call(thisArg, ...args)
// 给函数的原型添加 _call 方法,使得所有函数都能调用 _call
// thisArg 就是要绑定的那个this;...args 扩展操作符传参,适合不定长参数,args是一个数组
Function.prototype._call = function(thisArg,...args){
    // 1.获取需要执行的函数
    fn = this

    // 2.将 thisArg 转成对象类型(防止它传入的是非对象类型,例如123数字)
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window

    // 3.使用 thisArg 调用函数,绑定 this
    thisArg.fn = fn
    let result = thisArg.fn(...args)
    delete thisArg.fn

    // 4.返回结果
    return result
}
  • apply(thisArg, argsArray)
Function.prototype._apply = function(thisArg,argArray){
    // 1.获取需要执行的函数
    fn = this

    // 2.将 thisArg 转成对象类型(防止它传入的是非对象类型,例如123数字)
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
    // 判断一些边界情况
    argArray = argArray || []

    // 3.使用 thisArg 调用函数,绑定 this
    thisArg.fn = fn
    // 将传递过来的数组(可迭代对象)拆分,传给函数
    let result = thisArg.fn(...argArray)
    delete thisArg.fn

    // 4.返回结果
    return result
}
  • bind(thisArg, ...args)
Function.prototype._call = function(thisArg,...args){
    fn = this
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window

    thisArg.fn = fn
    let result = thisArg.fn(...args)
    delete thisArg.fn

    return result
}

// 利用 call 模拟 bind
Function.prototype._bind = function(thisArg,...args){
    let fn = this // 需要调用的那个函数的引用
    // bind 需要返回一个函数
    return function(){
        return fn._call(thisArg, ...args)
    }
}

4. Handwritten deep copy

PS: Shallow copy can also use the same template, of course, deep copy is more tested

function deepCopy(object) {
  if (!object || typeof object !== "object") return object;

  let newObject = Array.isArray(object) ? [] : {};

  for (let key in object) {
    if (object.hasOwnProperty(key)) {
      newObject[key] = deepCopy(object[key]);
    }
  }

  return newObject;
}

Advanced: Solving deep copies of circular references

function deepClone(obj, hash = new WeakMap()) {
  if (!object || typeof object !== "object") return object;

  // 是对象的话就要进行深拷贝,遇到循环引用,将引用存储起来,如果存在就不再拷贝
  if (hash.get(obj)) return hash.get(obj);
  let cloneObj = Array.isArray(object) ? [] : {};
  hash.set(obj, cloneObj);

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      // 实现一个递归拷贝
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  return cloneObj;
}

5. Handwriting anti-shake throttling

function debounce(func, delay) {

  // 这里使用了闭包,所以 timer 不会轻易被销毁
  let timer = null

  // 生成一个新的函数并返回
  return function (...args) {
    // 清空定时器
    if (timer) {
      clearTimeout(timer)
    }
    // 重新启动定时器
    timer = setTimeout(() => {
      func.call(this, ...args)
    }, delay)
  }
}

function throttle(func, delay) {
  let timer = null
  // 在 delay 时间内,最多执行一次 func
  return function (...args) {
    if (!timer) {
      timer = setTimeout(() => {
        func.call(this, ...args)
        // 完成一次计时,清空,待下一次触发
        timer = null
      }, delay)
    }
  }
}

6. Handwritten Ajax requests

function ajax(url) {
    // 创建一个 XHR 对象
    return new Promise((resolve,reject) => {
        const xhr = new XMLHttpRequest()
        // 指定请求类型,请求URL,和是否异步
        xhr.open('GET', url, true)
        xhr.onreadystatechange = funtion() {
          // 表明数据已就绪
            if(xhr.readyState === 4) {
                if(xhr.status === 200){
                    // 回调
                    resolve(JSON.stringify(xhr.responseText))
                }
                else{
                    reject('error')
                }
            }
        }
        // 发送定义好的请求
        xhr.send(null)
    })
}

7. Handwritten array deduplication

// 1.Set + 数组复制
fuction unique1(array){
    // Array.from(),对一个可迭代对象进行浅拷贝
    return Array.from(new Set(array))
}

// 2.Set + 扩展运算符浅拷贝
function unique2(array){
    // ... 扩展运算符
    return [...new Set(array)]
}

// 3.filter,判断是不是首次出现,如果不是就过滤掉
function unique3(array){
    return array.filter((item,index) => {
        return array.indexOf(item) === index
    })
}

// 4.创建一个新数组,如果之前没加入就加入
function unique4(array){
    let res = []
    array.forEach(item => {
        if(res.indexOf(item) === -1){
            res.push(item)
        }
    })
    return res
}

Advanced: If there are arrays and objects in the array, how to remove the duplicates (at this time, the addresses of the objects are different, and the duplicates cannot be removed with Set)

If you need to open the genuine WebStorm, you can contact me, 56 yuan a year, the genuine authorization is activated, and the validity period can be checked on the official website. If you need it, add me on WeChat: poxiaozhiai6, Remarks: 915.

8. Handwritten Array Flat

// 方法1-3:递归
function flat1(array){
    // reduce(): 对数组的每一项执行归并函数,这个归并函数的返回值会作为下一次调用时的参数,即 preValue
    // concat(): 合并两个数组,并返回一个新数组
    return array.reduce((preValue,curItem) => {
        return preValue.concat(Array.isArray(curItem) ? flat1(curItem) : curItem)
    },[])
}

function flat2(array){
    let res = []
    array.forEach(item => {
        if(Array.isArray(item)){
            // res.push(...flat2(item))
        // 如果遇到一个数组,递归
            res = res.concat(flat2(item))
        }
        else{
            res.push(item)
        }
    })
    return res
}

function flat3(array){
    // some(): 对数组的每一项都运行传入的函数,如果有一项返回 TRUE,则这个方法返回 TRUE
    while(array.some(item => Array.isArray(item))){
    // ES6 增加了扩展运算符,用于取出参数对象的所有可遍历属性,拷贝到当前对象之中:
        array = [].concat(...array)
        console.log(...array)
    }
    return array
}

// 方法4、5:先转成字符串,再变回数组
function flat4(array){
    //[1,[2,3]].toString()  =>  1,2,3
    return array.toString().split(',').map(item => parseInt(item))
}

function flat5(array){
    return array.join(',').split(',').map(item => Number(item))
}

9. Handwritten array out of order

// 方法1: sort + Math.random()
function shuffle1(arr){
    return arr.sort(() => Math.random() - 0.5);// 
}

// 方法2:时间复杂度 O(n^2)
// 随机拿出一个数(并在原数组中删除),放到新数组中
function randomSortArray(arr) {
    let backArr = [];
    while (arr.length) {
        let index = parseInt(Math.random() * arr.length);
        backArr.push(arr[index]);
        arr.splice(index, 1);
    }
    return backArr;
}

// 方法3:时间复杂度 O(n)
// 随机选一个放在最后,交换
function randomSortArray2(arr) {
    let lenNum = arr.length - 1;
    for (let i = 0; i < lenNum; i++) {
        let index = parseInt(Math.random() * (lenNum + 1 - i));
        [a[index],a[lenNum - i]] = [a[lenNum - i],a[index]]
    }
    return arr;
}

10. Handwritten Promise.all(), Promise.race()

PS: Those who have the ability can write down Promise and other Promise methods

function myAll(promises){
    // 问题关键:什么时候要执行resolve,什么时候要执行 reject
    return new Promise((resolve,reject) => {
        values = []
        // 迭代数组中的 Promise,将每个 promise 的结果保存到一个数组里
        promises.forEach(promise => {
            // 如果不是 Promise 类型要先包装一下
            // 调用 then 得到结果
            Promise.resolve(promise).then(res => {
                values.push(res)
                // 如果全部成功,状态变为 fulfilled
                if(values.length === promises.length){
                    resolve(values)
                }
                },err => { // 如果出现了 rejected 状态,则调用 reject() 返回结果
                reject(err)
            })
        })
    }
)
}

11. Shredded quick row

PS: It is best to memorize common sorting algorithms such as bubbling, selection, and insertion sorting. Heap sorting merge sorting can be written if it can be written. If you pass the test, if you can't write it, you can go back and wait for the notification.

const _quickSort = array => {
    // 补全代码
    quickSort(array, 0, array.length - 1)
    // 别忘了返回数组
    return array
}

const quickSort = (array, start, end) => {
    // 注意递归边界条件
    if(end - start < 1)    return
    // 取第一个数作为基准
    const base = array[start]
    let left = start
    let right = end
    while(left < right){
        // 从右往左找小于基准元素的数,并赋值给右指针 array[right]
        while(left < right &&  array[right] >= base)    right--
        array[left] = array[right]
        // 从左往右找大于基准元素的数,并赋值给左指针 array[left]
        while(left < right && array[left] <= base)    left++
        array[right] = array[left]
    }
    // 双指针重合处,将基准元素填到这个位置。基准元素已经事先保存下来了,因此不用担心上面的赋值操作会覆盖掉基准元素的值
    // array[left] 位置已经确定,左边的都比它小,右边的都比它大
    array[left] = base
    quickSort(array, start, left - 1)
    quickSort(array, left + 1, end)
    return array
}

12. Handwritten JSONP

// 动态的加载js文件
function addScript(src) {
  const script = document.createElement('script');
  script.src = src;
  script.type = "text/javascript";
  document.body.appendChild(script);
}
addScript("http://xxx.xxx.com/xxx.js?callback=handleRes");
// 设置一个全局的callback函数来接收回调结果
function handleRes(res) {
  console.log(res);
}

// 接口返回的数据格式,加载完js脚本后会自动执行回调函数
handleRes({a: 1, b: 2});

13. Handwritten Parasitic Composition Inheritance

PS: Combination inheritance should also be able to write out

function Parent(name) {
  this.name = name;
  this.say = () => {
    console.log(111);
  };
}
Parent.prototype.play = () => {
  console.log(222);
};
function Children(name,age) {
  Parent.call(this,name);
  this.age = age
}
Children.prototype = Object.create(Parent.prototype);
Children.prototype.constructor = Children;
// let child = new Children("111");
// // console.log(child.name);
// // child.say();
// // child.play();

14. Array/string operation questions

You can find some basic exercises by yourself, so I won’t list them one by one.

15. Handwritten Binary Search

//  迭代版
function search(nums, target) {
  // write code here
    if(nums.length === 0)    return -1
    let left = 0,right = nums.length - 1
        // 注意这里的边界,有等号
    while(left <= right){
        let mid = Math.floor((left + right) / 2)
        if(nums[mid] < target)    left = mid + 1
        else if(nums[mid] > target)    right = mid - 1
        else    return mid
    }
    return -1
}
// 递归版
function binary_search(arr, low, high, key) {
    if (low > high) {
        return -1;
    }
    var mid = parseInt((high + low) / 2);
    if (arr[mid] == key) {
        return mid;
    } else if (arr[mid] > key) {
        high = mid - 1;
        return binary_search(arr, low, high, key);
    } else if (arr[mid] < key) {
        low = mid + 1;
        return binary_search(arr, low, high, key);
    }
};

16. Handwritten function currying

function sum(x,y,z) {
    return x + y + z
}

function hyCurrying(fn) {
    // 判断当前已经接收的参数的个数,和函数本身需要接收的参数是否一致
    function curried(...args) {
        // 1.当已经传入的参数 大于等于 需要的参数时,就执行函数
        if(args.length >= fn.length){
            // 如果调用函数时指定了this,要将其绑定上去
            return fn.apply(this, args)
        }
        else{
            // 没有达到个数时,需要返回一个新的函数,继续来接收参数
            return function(...args2) {
                //return curried.apply(this, [...args, ...args2])
                // 接收到参数后,需要递归调用 curried 来检查函数的个数是否达到
                return curried.apply(this, args.concat(args2))
            }
        }
    }
    return curried
}

var curryAdd = hyCurry(add1)

curryAdd(10,20,30)
curryAdd(10,20)(30)
curryAdd(10)(20)(30)

other

1. Handwritten event delegation

2. Handwritten combination function

3. Common DOM operations

4. Common method of handwritten array Array.filter/map/fill/reduce

5. Handwritten Object.create()

6. Handwritten Object.is()

7. Handwritten Object.freeze()

8. Convert handwritten list to tree

9. Divide thousands of digits

10. Underscore to camel case

11. Addition of large numbers

Scenario simulation questions

high frequency

1. Implement the sleep function

async function test() {
    console.log('开始')
    await sleep(4000)
    console.log('结束')
}

function sleep(ms) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve()
        }, ms)
    })
}
test()

2. setTimeout implements setInterval

function setInterval(fn, time){
    var interval = function(){
    // time时间过去,这个异步被执行,而内部执行的函数正是interval,就相当于进了一个循环
        setTimeout(interval, time);
    // 同步代码
        fn();
    }
    //interval被延迟time时间执行
    setTimeout(interval,time); 
}

3. Asynchronous loop printing 1, 2, 3

var sleep = function (time, i) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve(i);
    }, time);
  })
};


var start = async function () {
  for (let i = 1; i <= 3; i++) {
    let result = await sleep(1000, i);
    console.log(result);
  }
};

start();

4. Cycle printing red, yellow, green

function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}

const task = (timer, light) => {
    new Promise((resolve, reject) => {
        setTimeout(() => {
            if (light === 'red') {
                red()
            }
            else if (light === 'green') {
                green()
            }
            else if (light === 'yellow') {
                yellow()
            }
            resolve()
        }, timer)
    })
}
const taskRunner =  async () => {
    await task(3000, 'red')
    await task(2000, 'green')
    await task(2100, 'yellow')
    taskRunner()
}
taskRunner()

other

Topics related to Promise concurrency control, such as implementing a Promise scheduler with parallel constraints

More Promise interview questions are here: If you want to come to 45 Promise interview questions, you can have a good time , and the brothers of the big noodle factory can take a look

Advanced

1. Handwritten Promise (important)

2. Handwritten publish-subscribe model

3. Handwritten observer mode

4. Handwritten two-way binding

5. Handwritten Event Bus

6. Handwritten LRU

Guess you like

Origin blog.csdn.net/qq_38140936/article/details/126866599