关键词:
收集整理2022年最新前端面试题及答案,方便平时翻看记忆,欢迎各位大佬们补充。
一般来说,把下面基础中的高频题写熟练就差不多了。当然去面大厂这些远远不够,还要再刷一些算法题。
基础
高频
1.手写 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.手写 new 操作符
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.手写 call、apply、bind 函数
- 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.手写深拷贝
PS:浅拷贝也可以用一样的模板,当然深拷贝考得多
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;
进阶:解决循环引用的深拷贝
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.手写防抖节流
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.手写Ajax请求
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.手写数组去重
// 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
进阶:如果数组内有数组和对象,应该怎么去重(此时对象的地址不同,用Set去不了重)
需要开通正版 WebStorm 的可以联系我,56元一年,正版授权激活,官网可查有效期,有需要的加我微信:poxiaozhiai6,备注:915。
8.手写数组扁平
// 方法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.手写数组乱序
// 方法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.手写 Promise.all()、Promise.race()
PS: 有能力的可以去写下 Promise 和其他的 Promise 方法
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.手撕快排
PS: 常见的排序算法,像冒泡,选择,插入排序这些最好也背一下,堆排序归并排序能写则写。万一考到了呢,要是写不出就直接回去等通知了。
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.手写 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.手写寄生组合继承
PS: 组合继承也要能写出来
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.数组/字符串操作题
可以自己找些基础的练一下,就不一一列举了
15.手写二分查找
// 迭代版
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.手写函数柯里化
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)
其他
1.手写事件委托
2.手写组合函数
3.常见DOM操作
4.手写数组常见方法 Array.filter/map/fill/reduce
5.手写Object.create()
6.手写Object.is()
7.手写Object.freeze()
8.手写列表转树
9.数字千分位分割
10.下划线转驼峰
11.大数相加
场景模拟题
高频
1.实现 sleep 函数
async function test()
console.log('开始')
await sleep(4000)
console.log('结束')
function sleep(ms)
return new Promise(resolve =>
setTimeout(() =>
resolve()
, ms)
)
test()
2.setTimeout 实现 setInterval
function setInterval(fn, time)
var interval = function()
// time时间过去,这个异步被执行,而内部执行的函数正是interval,就相当于进了一个循环
setTimeout(interval, time);
// 同步代码
fn();
//interval被延迟time时间执行
setTimeout(interval,time);
3.异步循环打印 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.循环打印红、黄、绿
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()
其他
Promise 并发控制相关的题目,例如实现有并行限制的 Promise 调度器
更多 Promise 的面试题在这里:要就来45道Promise面试题一次爽到底,面大厂的兄弟可以看看
进阶
1.手写 Promise(重要)
2.手写发布-订阅模式
3.手写观察者模式
4.手写双向绑定
5.手写 Event Bus
6.手写 LRU
2022年腾讯,阿里,美团等android高频面试题及答案,知识脉络整理(代码片段)
前言这篇文章非常的干!覆盖了安卓面试的大多数知识点,值得收藏反复查看!安逸久了就容易迷失方向,多看看高质量的面试题找找差距,然后查漏补缺!##问题区:1.Activity的启动过程,AMS、PMS... 查看详情
2022年java面试题最新整理,附白话答案(代码片段)
2022年Java开发面试题最新整理,附白话答案写在前面:本篇面试题整理是我在北京地区进行面试整理得出,常见的基本都在下面了。基本都是可以直接在面试时用白话回答的答案总结,面试时照此回答即可,有... 查看详情
21年最新python面试题及答案汇总详解(上)(代码片段)
Python面试你做准备了吗?下面小千整理了一套2021年最新Python常见面试题目,及Python面试题目答案汇总。希望能够帮助到大家。21年最新Python面试题及答案汇总详解(上)1、列表(list)和元组(tuple)有什么区别?在我每一次... 查看详情
java面试题及答案2020java最新面试题及答案2020一(代码片段)
java最新面试题及答案20201.一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制?一个“.java”源文件里面可以包含多个类,但是只允许有一java最新面试题及答案个public类,并且类名必须和文件名一致。每... 查看详情
2022最新android面试题及答案整理(共计4176页pdf)包含腾讯字节百度小米阿里等大厂面试真题
前言最近在准备面试,然后复习下之前写过的项目,书籍,笔记,文章。一看很多知识点都没有印象,最可拍的是连自己为了防止忘记写的文章竟然都感觉不是自己写的。有些开始怀疑人生了。好了,废话... 查看详情
vue3面试题:2022最新前端vue3.0面试题及答案(持续更新中……)(代码片段)
1、Vue2.0和Vue3.0有什么区别?响应式系统的重新配置,使用代理替换对象.define属性,使用代理优势:可直接监控阵列类型的数据变化监听的目标是对象本身,不需要像Object.defineProperty那样遍历每个属性,有... 查看详情
600+道java面试题及答案整理(2021最新版)
栈长整理了2021年最新、最全的Java面试题,题目涉及Java基础、集合、多线程、IO、分布式、Spring全家桶、MyBatis、Dubbo、缓存、消息队列、Linux…等等。题库共600+道,带全部答案,非常齐全!Java基础1、面向对象... 查看详情
java面试题及答案2020java最新面试题及答案2020一(代码片段)
java最新面试题及答案20201.一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制?一个“.java”源文件里面可以包含多个类,但是只允许有一java最新面试题及答案个public类,并且类名必须和文件名一致。每... 查看详情
android大厂高频面试题及答案,知识脉络整理(代码片段)
前言这篇文章非常的干!覆盖了安卓面试的大多数知识点,值得收藏反复查看!安逸久了就容易迷失方向,多看看高质量的面试题找找差距,然后查漏补缺!问题区:1.Activity的启动过程,AMS、PMS源... 查看详情
jvm面试题及答案整理(最新版)
Jvm面试题及答案整理965道(2021最新版)这是我收集的《Jvm最常见的965道面试题》高级Java面试问题列表。这些问题主要来自JVM核心部分,你可能知道这些棘手的JVM问题的答案,或者觉得这些不足以挑战你的Java知识,... 查看详情
2023前端面试题及答案整理(js面试题)(代码片段)
ES6let/const全局作用域中,用const和let声明的变量不在window上,那到底在哪里?如何去获取?ES6规定,var命令和function命令声明的全局变量,依旧是顶层对象的属性,但let、const、class命令声明的全局变量... 查看详情
2023前端面试题及答案整理(javascript)(代码片段)
JS类型string,number,boolean,undefined,null,symbol(es6),BigInt(es10),object值类型和引用类型的区别两种类型的区别是:存储位置不同;值类型存储在栈(stack)中,占... 查看详情
2022年最新android面试题分享,助你轻松拿下名企offer
...伙伴可以往下翻翻看,说不定本文会对你有帮助Android面试题及答案(2022中高级Android面试必备百题)免费分享!!!发现网上很多Android面试题及答案整理都没有答案,所以花了很长时间搜集,本套A... 查看详情
最全java面试题及答案整理(2023最新版)
所有的面试题目都不是一成不变的,面试题目只是给大家一个借鉴作用,最主要的是给自己增加知识的储备,有备无患。前言面试前还是很有必要针对性的刷一些题,很多朋友的实战能力很强,但是理论比较薄弱,面试前不做准... 查看详情
2023前端面试题及答案整理(vue)(代码片段)
watch和computed区别watch是监听动作,computed是计算属性watch没缓存,只要数据变化就执行。computed有缓存,只在属性变化的时候才去计算。watch可以执行异步操作,而computed不能watch常用于一个数据影响多个数据,co... 查看详情
java面试题及答案,2020年最新面试题集合
面试题内容涵盖:Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、Redis、MySQL、Spring、SpringBoot、SpringCloud、RabbitMQ、Kafka、Linux等技术栈,一共有上百个面试题集合,资源难得,而且还是近一年的真实面试题; 由于面试题答案... 查看详情
springcloud面试题及答案300道,springcloud面试题总结(持续更新)
...f08;SpringCloud面试题大全带答案)2021年面试题及答案【最新版】高级SpringCloud面试题大全,发现网上很多SpringCloud面试题及答案整理都没有答案,所以花了很长时间搜集,本套SpringCloud面试题大全,有大量经典的S... 查看详情
2021最新阿里java高级面试题及答案,大厂面试题汇总(代码片段)
什么是数据脱敏先来看看什么是数据脱敏?数据脱敏也叫数据的去隐私化,在我们给定脱敏规则和策略的情况下,对敏感数据比如手机号、银行卡号等信息,进行转换或者修改的一种技术手段,防止敏感数据... 查看详情