美团前端常考手写面试题总结
实现观察者模式
观察者模式(基于发布订阅模式) 有观察者,也有被观察者
观察者需要放到被观察者中,被观察者的状态变化需要通知观察者 我变化了 内部也是基于发布订阅模式,收集观察者,状态变化后要主动通知观察者
class Subject { // 被观察者 学生constructor(name) {this.state = 'happy'this.observers = []; // 存储所有的观察者}// 收集所有的观察者attach(o){ // Subject. prototype. attchthis.observers.push(o)}// 更新被观察者 状态的方法setState(newState) {this.state = newState; // 更新状态// this 指被观察者 学生this.observers.forEach(o => o.update(this)) // 通知观察者 更新它们的状态}
}class Observer{ // 观察者 父母和老师constructor(name) {this.name = name}update(student) {console.log('当前' + this.name + '被通知了', '当前学生的状态是' + student.state)}
}let student = new Subject('学生'); let parent = new Observer('父母');
let teacher = new Observer('老师'); // 被观察者存储观察者的前提,需要先接纳观察者
student. attach(parent);
student. attach(teacher);
student. setState('被欺负了');
交换a,b的值,不能用临时变量
巧妙的利用两个数的和、差:
a = a + b
b = a - b
a = a - b
手写类型判断函数
function getType(value) {// 判断数据是 null 的情况if (value === null) {return value + "";}// 判断数据是引用类型的情况if (typeof value === "object") {let valueClass = Object.prototype.toString.call(value),type = valueClass.split(" ")[1].split("");type.pop();return type.join("").toLowerCase();} else {// 判断数据是基本数据类型的情况和函数的情况return typeof value;}
}
实现数组去重
给定某无序数组,要求去除数组中的重复数字并且返回新的无重复数组。
ES6方法(使用数据结构集合):
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8]
ES5方法:使用map存储不重复的数字
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];uniqueArray(array); // [1, 2, 3, 5, 9, 8]function uniqueArray(array) {let map = {};let res = [];for(var i = 0; i < array.length; i++) {if(!map.hasOwnProperty([array[i]])) {map[array[i]] = 1;res.push(array[i]);}}return res;
}
实现数组的push方法
let arr = [];
Array.prototype.push = function() {for( let i = 0 ; i < arguments.length ; i++){this[this.length] = arguments[i] ;}return this.length;
}
实现数组的乱序输出
主要的实现思路就是:
- 取出数组的第一个元素,随机产生一个索引值,将该第一个元素和这个索引对应的元素进行交换。
- 第二次取出数据数组第二个元素,随机产生一个除了索引为1的之外的索引值,并将第二个元素与该索引值对应的元素进行交换
- 按照上面的规律执行,直到遍历完成
var arr = [1,2,3,4,5,6,7,8,9,10];
for (var i = 0; i < arr.length; i++) {const randomIndex = Math.round(Math.random() * (arr.length - 1 - i)) + i;[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
}
console.log(arr)
还有一方法就是倒序遍历:
var arr = [1,2,3,4,5,6,7,8,9,10];
let length = arr.length,randomIndex,temp;while (length) {randomIndex = Math.floor(Math.random() * length--);temp = arr[length];arr[length] = arr[randomIndex];arr[randomIndex] = temp;}
console.log(arr)
参考 前端进阶面试题详细解答
手写防抖函数
函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
// 函数防抖的实现
function debounce(fn, wait) {let timer = null;return function() {let context = this,args = arguments;// 如果此时存在定时器的话,则取消之前的定时器重新记时if (timer) {clearTimeout(timer);timer = null;}// 设置定时器,使事件间隔指定事件后执行timer = setTimeout(() => {fn.apply(context, args);}, wait);};
}
实现数组的filter方法
Array.prototype._filter = function(fn) {if (typeof fn !== "function") {throw Error('参数必须是一个函数');}const res = [];for (let i = 0, len = this.length; i < len; i++) {fn(this[i]) && res.push(this[i]);}return res;
}
实现instanceOf
// 模拟 instanceof
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式var O = R.prototype; // 取 R 的显示原型L = L.__proto__; // 取 L 的隐式原型while (true) {if (L === null) return false;if (O === L)// 这里重点:当 O 严格等于 L 时,返回 truereturn true;L = L.__proto__;}
}
实现深拷贝
- 浅拷贝: 浅拷贝指的是将一个对象的属性值复制到另一个对象,如果有的属性的值为引用类型的话,那么会将这个引用的地址复制给对象,因此两个对象会有同一个引用类型的引用。浅拷贝可以使用 Object.assign 和展开运算符来实现。
- 深拷贝: 深拷贝相对浅拷贝而言,如果遇到属性值为引用类型的时候,它新建一个引用类型并将对应的值复制给它,因此对象获得的一个新的引用类型而不是一个原有类型的引用。深拷贝对于一些对象可以使用 JSON 的两个函数来实现,但是由于 JSON 的对象格式比 js 的对象格式更加严格,所以如果属性值里边出现函数或者 Symbol 类型的值时,会转换失败
(1)JSON.stringify()
JSON.parse(JSON.stringify(obj))
是目前比较常用的深拷贝方法之一,它的原理就是利用JSON.stringify
将js
对象序列化(JSON字符串),再使用JSON.parse
来反序列化(还原)js
对象。- 这个方法可以简单粗暴的实现深拷贝,但是还存在问题,拷贝的对象中如果有函数,undefined,symbol,当使用过
JSON.stringify()
进行处理之后,都会消失。
let obj1 = { a: 0,b: {c: 0}};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.a = 1;
obj1.b.c = 1;
console.log(obj1); // {a: 1, b: {c: 1}}
console.log(obj2); // {a: 0, b: {c: 0}}
(2)函数库lodash的_.cloneDeep方法
该函数库也有提供_.cloneDeep用来做 Deep Copy
var _ = require('lodash');
var obj1 = {a: 1,b: { f: { g: 1 } },c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
(3)手写实现深拷贝函数
// 深拷贝的实现
function deepCopy(object) {if (!object || typeof object !== "object") return;let newObject = Array.isArray(object) ? [] : {};for (let key in object) {if (object.hasOwnProperty(key)) {newObject[key] =typeof object[key] === "object" ? deepCopy(object[key]) : object[key];}}return newObject;
}
实现一个call
call做了什么:
- 将函数设为对象的属性
- 执行&删除这个函数
- 指定this到函数并传入给定参数执行函数
- 如果不传入参数,默认指向为 window
// 模拟 call bar.mycall(null);
//实现一个call方法:
Function.prototype.myCall = function(context) {//此处没有考虑context非object情况context.fn = this;let args = [];for (let i = 1, len = arguments.length; i < len; i++) {args.push(arguments[i]);}context.fn(...args);let result = context.fn(...args);delete context.fn;return result;
};
实现日期格式化函数
输入:
dateFormat(new Date('2020-12-01'), 'yyyy/MM/dd') // 2020/12/01
dateFormat(new Date('2020-04-01'), 'yyyy/MM/dd') // 2020/04/01
dateFormat(new Date('2020-04-01'), 'yyyy年MM月dd日') // 2020年04月01日
const dateFormat = (dateInput, format)=>{var day = dateInput.getDate() var month = dateInput.getMonth() + 1 var year = dateInput.getFullYear() format = format.replace(/yyyy/, year)format = format.replace(/MM/,month)format = format.replace(/dd/,day)return format
}
用Promise实现图片的异步加载
let imageAsync=(url)=>{return new Promise((resolve,reject)=>{let img = new Image();img.src = url;img.οnlοad=()=>{console.log(`图片请求成功,此处进行通用操作`);resolve(image);}img.οnerrοr=(err)=>{console.log(`失败,此处进行失败的通用操作`);reject(err);}})}imageAsync("url").then(()=>{console.log("加载成功");
}).catch((error)=>{console.log("加载失败");
})
解析 URL Params 为对象
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 结果{ user: 'anonymous', id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型 city: '北京', // 中文需解码 enabled: true, // 未指定值得 key 约定为 true}*/
function parseParam(url) {const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中let paramsObj = {};// 将 params 存到对象中paramsArr.forEach(param => {if (/=/.test(param)) { // 处理有 value 的参数let [key, val] = param.split('='); // 分割 key 和 valueval = decodeURIComponent(val); // 解码val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值paramsObj[key] = [].concat(paramsObj[key], val);} else { // 如果对象没有这个 key,创建 key 并设置值paramsObj[key] = val;}} else { // 处理没有 value 的参数paramsObj[param] = true;}})return paramsObj;
}
使用 setTimeout 实现 setInterval
setInterval 的作用是每隔一段指定时间执行一个函数,但是这个执行不是真的到了时间立即执行,它真正的作用是每隔一段时间将事件加入事件队列中去,只有当当前的执行栈为空的时候,才能去从事件队列中取出事件执行。所以可能会出现这样的情况,就是当前执行栈执行的时间很长,导致事件队列里边积累多个定时器加入的事件,当执行栈结束的时候,这些事件会依次执行,因此就不能到间隔一段时间执行的效果。
针对 setInterval 的这个缺点,我们可以使用 setTimeout 递归调用来模拟 setInterval,这样我们就确保了只有一个事件结束了,我们才会触发下一个定时器事件,这样解决了 setInterval 的问题。
实现思路是使用递归函数,不断地去执行 setTimeout 从而达到 setInterval 的效果
function mySetInterval(fn, timeout) {// 控制器,控制定时器是否继续执行var timer = {flag: true};// 设置递归函数,模拟定时器执行。function interval() {if (timer.flag) {fn();setTimeout(interval, timeout);}}// 启动定时器setTimeout(interval, timeout);// 返回控制器return timer;
}
手写节流函数
函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
// 函数节流的实现;
function throttle(fn, delay) {let curTime = Date.now();return function() {let context = this,args = arguments,nowTime = Date.now();// 如果两次时间间隔超过了指定时间,则执行函数。if (nowTime - curTime >= delay) {curTime = Date.now();return fn.apply(context, args);}};
}
使用ES5和ES6求函数参数的和
ES5:
function sum() {let sum = 0Array.prototype.forEach.call(arguments, function(item) {sum += item * 1})return sum
}
ES6:
function sum(...nums) {let sum = 0nums.forEach(function(item) {sum += item * 1})return sum
}
手写 Object.create
思路:将传入的对象作为原型
function create(obj) {function F() {}F.prototype = objreturn new F()
}
Promise.race
Promise.race = function(promiseArr) {return new Promise((resolve, reject) => {promiseArr.forEach(p => {// 如果不是Promise实例需要转化为Promise实例Promise.resolve(p).then(val => resolve(val),err => reject(err),)})})
}
解析 URL Params 为对象
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url)
/* 结果
{ user: 'anonymous',id: [ 123, 456 ], // 重复出现的 key 要组装成数组,能被转成数字的就转成数字类型city: '北京', // 中文需解码enabled: true, // 未指定值得 key 约定为 true
}
*/
function parseParam(url) {const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中let paramsObj = {};// 将 params 存到对象中paramsArr.forEach(param => {if (/=/.test(param)) { // 处理有 value 的参数let [key, val] = param.split('='); // 分割 key 和 valueval = decodeURIComponent(val); // 解码val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值paramsObj[key] = [].concat(paramsObj[key], val);} else { // 如果对象没有这个 key,创建 key 并设置值paramsObj[key] = val;}} else { // 处理没有 value 的参数paramsObj[param] = true;}})return paramsObj;
}
相关文章:

美团前端常考手写面试题总结
实现观察者模式 观察者模式(基于发布订阅模式) 有观察者,也有被观察者 观察者需要放到被观察者中,被观察者的状态变化需要通知观察者 我变化了 内部也是基于发布订阅模式,收集观察者,状态变化后要主动通知观…...

MyBatis基于XML的详细使用——动态sql
目录 动态sql if where trim foreach choose、when、otherwise set bind sql MyBatis常用OGNL表达式 动态sql 动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如…...

CMake编译opencv4.6
openCV系列文章目录 文章目录openCV系列文章目录前言一、准备工作二、使用步骤1.使用CMake编译openCV总结前言 最近在项目中遇到图片处理,一拍脑袋就想到大名鼎鼎的opencv 一、准备工作 1.openCV官网下载 2.CMake官方下载 3.vs2019官方下载 二、使用步骤 1.使用…...

数据分片(mycat)
1. 数据分片概念: 1.1. 分库分表 什么是分库分表: 将存放在一台数据库服务器中的数据,按照特定方式(指的是程序开发的算法)进行拆分,分散存放到多台数据库服务器中,以达到分散单台服务器负载的…...

基于STM32设计的倒车雷达系统(超声波模块多方位测距应用)
一、项目背景 汽车高科技产品家族中,专为倒车泊位设置的“倒车雷达”应运而生,倒车雷达的加装可以解决驾驶人员的后顾之忧,大大降低到车事故的发生。汽车倒车雷达全称为“倒车防撞雷达”,也叫“泊车辅助装置”,是汽车泊车安全辅助装置,能以声音或者更为直观的显示来告知…...

Robot Framework + Selenium2Library环境下,结合Selenium Grid实施分布式自动化测试
最近一段时间,公司在推行自动化测试流程,本人有幸参与了自定义通用控件的关键字封装和脚本辅助编写、数据驱动管理、测试用例执行管理等一系列工具软件的研发工作,积累了一些经验,在此与大家做一下分享,也算是做一个总…...

洛谷——前缀和与差分
前缀和与差分 文章目录前缀和与差分应用总结前缀和截断数组思路代码最大加权矩形题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示思路代码差分海底高铁题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示思路代码改变数组元素思路代码应用总结 前缀和用来…...

离线内网环境部署更新问题记录
文章目录低级错误错误一 配置文件参数错误错误二 文件位置错误新遇到的错误其他遇到的问题经验教训低级错误 错误一 配置文件参数错误 在与现场实施人员沟通时,出现信息错位,实施人员发来的截图里的ip地址不是正在使用的ip地址(机器c重装系…...

【Git】Git是什么?简单说说Git的工作机制?Git的常用命令有那些?
目录 一、Git是什么? 二、简单说说Git的工作机制? 三、Git的常用命令有那些? 💟 创作不易,不妨点赞💚评论❤️收藏💙一下 一、Git是什么? Git 是一个免费的、开源的分布式版本控制系统,可…...

《精通Spring4.x 企业应用开发实战》第1章 Spring概述
目录标题前言一、Spring带给我们什么二、Spring体系结构三、Spring4.0新特性核心容器的增强泛型依赖注入Map依赖注入Lazy延迟依赖注入List注入Conditional 注解CGLIB 代理类增强其他四、Spring 子项目总结前言 汇总:《精通Spring4.x 企业应用开发实战》 一、Spring带…...

【Spring Cloud Alibaba】003-Nacos 概述与单机搭建
【Spring Cloud Alibaba】003-Nacos 概述与单机搭建 文章目录【Spring Cloud Alibaba】003-Nacos 概述与单机搭建一、Nacos 概述0、新技术学习思路推荐1、什么是 Nacos2、架构图架构图架构图信息二、Nacos 单机搭建1、下载与启动下载地址编辑 startup.cmd 文件下面对两种模式的…...

如何使用 API 工具做 Websocket 测试
在 API 测试中,对 Websocket 协议的支持呼声越来越高,今天给大家推荐一款 开源的 API 管理工具——Postcat,以及教教大家,如何利用 API 管理工具做 Websocket 测试。 在线 Demo 链接:Postcat - Open Source API Ecosys…...

90%的人都理解错了HTTP中GET与POST的区别
Get和Post是HTTP请求的两种基本方法,要说它们的区别,接触过WEB开发的人都能说出一二。 最直观的区别就是Get把参数包含在URL中,Post通过request body传递参数。 你可能自己写过无数个Get和Post请求,或者已经看过很多权威网站总结…...

【C++】秋招实习面经汇总篇
C面经汇总 系列综述: 目的:本系列是个人整理为了秋招和实习面试的,整理期间苛求每个知识点,平衡背诵量与深入程度。 来源:材料主要源于阿秀的笔记和《王道考研复习指导》进行的,每个知识点的修正和深入主要…...

干货分享:2023欧美市场分析与机会
1、2023年欧美市场分析美国是⼀个消费大国,正常情况下做外贸不可忽略的市场。如何找客户:专注产业链 。产业链对接,其上游是什么,那么他就是我的客户 ( 原材料-⼯⼚)南美洲是北美的经济殖民地(矿产资源农产品),非洲仍然…...

介绍Kadence Elements元素模板:按您的方式设计网站
随着 Kadence Pro 1.0.4 和 Kadence Blocks Pro 1.5.8 的发布,Kadence 团队很高兴地宣布推出最强大的新方法,帮助网站所有者使用 Kadence Elements Templates 创建动态和高度定制的 WordPress 网站。如果您曾经创建过 WordPress 网站,并且发现…...

物联网发展的重要通信技术Wi-Fi
Wi-Fi 可以适应各种场景的联网需求 Wi-Fi 在实现物联网创新方面发挥了基础性作用,提供了广泛的连接性,将各种“事物”相互连接、连接到互联网,以及连接到全球使用的 180 亿台 Wi-Fi 设备。物联网的经济潜力是无限的,Wi-Fi 为智能…...

OSS上传(Java和Js)
OSS上传(Java和Js)准备工作创建RAM用户创建角色创建权限策略给角色授予权限策略获取临时访问凭证Java普通上传OSSJava分片上传OSSJS普通上传OSSJS分片上传OSS使用RAM用户或STS方式访问 由于阿里云账号AccessKey拥有所有API访问权限,建议遵循阿…...

【虚拟机】VirtualBox Host-Only + 主机网络共享配置
文章目录创建Host-Only虚拟机配置主机配置其它工作中经常会使用到虚拟机进行各种技术的试验,之前为了省事常用桥接模式,可是我经常变换办公地点,每个办公地点的局域网网段并不一样,所以我采取了仅主机模式网络共享这种方式&#x…...

小公司“混”的3年,我认真做了5件事,真的受益终生
小公司“混”的3年,我认真做了5件事,真的受益终生 目录:导读 功能测试很重要但不值钱 自动化测试在小公司没市场,但是你得会 给自己的一些忠告 第一件事:分清阶段,制定计划 第二件事:梳理…...

Linux Crontab命令定时任务基本语法与操作教程
Linux Crontab命令定时任务基本语法与操作教程 一、Crontab查看编辑重启 1、查看crontab定时执行任务列表 crontab -l 2、编辑crontab定时执行任务 crontab -e 3、删除crontab定时任务 crontab -r 4、相关命令: sudo service crond start #启动服务 sudo …...

文档测试要测什么,如何进行测试?
文档测试要测什么,如何进行测试? 对于交付用户文档来说,以需求、用户手册、安装手册等为主,检查用户文档是否和实际的存在差别,主要从以下几个方面来考虑: 阅读者:文档面向的读者定位要清晰&…...

.net 6 引入EFCore
这里默认使用sql server数据库 DBFirst nuget引入程序集 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.SqlServer Microsoft.EntityFrameworkCore.Design Microsoft.EntityFrameworkCore.Tools Microsoft.Extensions.Logging.Console 执行脚本 设置DAL…...

MySQL------自定义排序
1、MySQL函数 field() 实现自定义 语法: SELECT * from table_name ORDER BY FIELD(str,str1,str2,str3,…) str: 字段名, str1,str2,str3: 自定义排序的数值 例1排序-所有值: 先姓名排序后出生日期排序 SELECT * from name_info ORDER BY FIELD(name…...

FFMPEG自学二 ⾳频编码实战
一、FFmpeg编码流程二、流程关键函数avcodec_find_encoder:根据指定的AVCodecID查找注册的编码器。 avcodec_alloc_context3:为AVCodecContext分配内存。 avcodec_open2:打开编码器。 avcodec_send_frame:将AVFrame⾮压缩数据给…...

一致魔芋在北交所上市:市值突破11亿元,吴平夫妇为实控人
2月21日,湖北一致魔芋生物科技股份有限公司(下称“一致魔芋”,BJ:839273)在北京证券交易所上市。本次上市,一致魔芋的发行价为11.38元/股,发行1350万股,募资总额约为1.54亿元。 本次发行后&…...

进程或线程终止是否会释放锁
线程锁的必要性比如一个多线程抢票程序,tickets作为临界资源,所有的线程都要对它进行判断ticket是否大于0,以及ticket–的操作。用ticket–操作举例,虽然他看起来是一行C语言的代码,但是实际上它的底层汇编经历了三个阶…...

mysql复制表提示某些为null字段无效
sql_mode‘ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION’ explicit_defaults_for_timestamp ON 1、方式1、 首先用下面的命令看下sql_mode show variables like ‘sql_mode’; 如果查询的结果如下&#…...

【数据库】redis 配置文件与发布订阅
目录 配置文件 一,Units 二, INCLUDE 三,NETWORK 1, bind 2, tcp-backlog 3,timeout 4, tcp-keepalive 四,GENERAL 1,daemonize 2, pidfile 3&…...

ChatGPT来了,英语不能丢,但我不想上班
文 / 谷雨(微信公众号:王不留) 好久没写文,可能大伙已把我忘了。春节之后,状态一直不太好。我在2月1号时从老家直接来到了深圳出差,而后以996的工作状态疲于应付工作中的各种问题。 终于这周末休息了两天&a…...