2021年前端各大公司都考了那些手写题(附带代码)
Posted 前端开发博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021年前端各大公司都考了那些手写题(附带代码)相关的知识,希望对你有一定的参考价值。
大家好,我是漫步 ,分享一篇比较全的面试题,喜欢记得关注我并设为星标。
🎄 前言 前端开发博客
本文主要总结了 2021 年前端提前批和秋招所考察的手写题,题目来源于牛客网前端面经区,统计时间自 3 月初至 10 月底,面经来源于阿里、腾讯、百度、字节、美团、京东、快手、拼多多等 15 家公司,并做了简单的频次划分。
⭐⭐⭐⭐⭐: 在 15 家公司面试中出现 10+
⭐⭐⭐⭐:在 15 家公式面试中出现 5-10
⭐⭐⭐:在 15 家公司面试中出现 3-5
无星:出现 1-2
题目解析一部分来源于小包的编写,另一部分如果我感觉题目扩展开来更好的话,我就选取部分大佬的博客链接。
🌟 promise
实现promise
考察频率: (⭐⭐⭐⭐⭐)
参考代码[1]
实现promise.all
考察频率: (⭐⭐⭐⭐⭐)
function PromiseAll(promises)
return new Promise((resolve, reject)=>
if(!Array.isArray(promises))
throw new TypeError("promises must be an array")
let result = []
let count = 0
promises.forEach((promise, index) =>
promise.then((res)=>
result[index] = res
count++
count === promises.length && resolve(result)
, (err)=>
reject(err)
)
)
)
实现 promise.finally
考察频率: (⭐⭐⭐⭐⭐)
Promise.prototype.finally = function (cb)
return this.then(function (value)
return Promise.resolve(cb()).then(function ()
return value
)
, function (err)
return Promise.resolve(cb()).then(function ()
throw err
)
)
实现promise.allSettled
考察频率: (⭐⭐⭐⭐)
function allSettled(promises)
if (promises.length === 0) return Promise.resolve([])
const _promises = promises.map(
item => item instanceof Promise ? item : Promise.resolve(item)
)
return new Promise((resolve, reject) =>
const result = []
let unSettledPromiseCount = _promises.length
_promises.forEach((promise, index) =>
promise.then((value) =>
result[index] =
status: 'fulfilled',
value
unSettledPromiseCount -= 1
// resolve after all are settled
if (unSettledPromiseCount === 0)
resolve(result)
, (reason) =>
result[index] =
status: 'rejected',
reason
unSettledPromiseCount -= 1
// resolve after all are settled
if (unSettledPromiseCount === 0)
resolve(result)
)
)
)
实现promise.race
考察频率: (⭐⭐⭐)
Promise.race = function(promiseArr)
return new Promise((resolve, reject) =>
promiseArr.forEach(p =>
Promise.resolve(p).then(val =>
resolve(val)
, err =>
rejecte(err)
)
)
)
来说一下如何串行执行多个Promise
参考代码[2]
promise.any
Promise.any = function(promiseArr)
let index = 0
return new Promise((resolve, reject) =>
if (promiseArr.length === 0) return
promiseArr.forEach((p, i) =>
Promise.resolve(p).then(val =>
resolve(val)
, err =>
index++
if (index === promiseArr.length)
reject(new AggregateError('All promises were rejected'))
)
)
)
resolve
Promise.resolve = function(value)
if(value instanceof Promise)
return value
return new Promise(resolve => resolve(value))
reject
Promise.reject = function(reason)
return new Promise((resolve, reject) => reject(reason))
🐳 Array篇 前端开发博客
数组去重
考察频率: (⭐⭐⭐⭐⭐)
使用双重 for
和 splice
function unique(arr)
for(var i=0; i<arr.length; i++)
for(var j=i+1; j<arr.length; j++)
if(arr[i]==arr[j])
//第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
// 删除后注意回调j
j--;
return arr;
使用 indexOf
或 includes
加新数组
//使用indexof
function unique(arr)
var uniqueArr = []; // 新数组
for (let i = 0; i < arr.length; i++)
if (uniqueArr.indexOf(arr[i]) === -1)
//indexof返回-1表示在新数组中不存在该元素
uniqueArr.push(arr[i])//是新数组里没有的元素就push入
return uniqueArr;
// 使用includes
function unique(arr)
var uniqueArr = [];
for (let i = 0; i < arr.length; i++)
//includes 检测数组是否有某个值
if (!uniqueArr.includes(arr[i]))
uniqueArr.push(arr[i])//
return uniqueArr;
sort
排序后,使用快慢指针的思想
function unique(arr)
arr.sort((a, b) => a - b);
var slow = 1,
fast = 1;
while (fast < arr.length)
if (arr[fast] != arr[fast - 1])
arr[slow ++] = arr[fast];
++ fast;
arr.length = slow;
return arr;
sort
方法用于从小到大排序(返回一个新数组),其参数中不带以上回调函数就会在两位数及以上时出现排序错误(如果省略,元素按照转换为的字符串的各个字符的 Unicode
位点进行排序。两位数会变为长度为二的字符串来计算)。
ES6
提供的 Set
去重
function unique(arr)
const result = new Set(arr);
return [...result];
//使用扩展运算符将Set数据结构转为数组
Set
中的元素只会出现一次,即 Set
中的元素是唯一的。
使用哈希表存储元素是否出现(ES6
提供的 map
)
function unique(arr)
let map = new Map();
let uniqueArr = new Array(); // 数组用于返回结果
for (let i = 0; i < arr.length; i++)
if(map.has(arr[i])) // 如果有该key值
map.set(arr[i], true);
else
map.set(arr[i], false); // 如果没有该key值
uniqueArr.push(arr[i]);
return uniqueArr ;
map
对象保存键值对,与对象类似。但 map
的键可以是任意类型,对象的键只能是字符串类型。
如果数组中只有数字也可以使用普通对象作为哈希表。
filter
配合 indexOf
function unique(arr)
return arr.filter(function (item, index, arr)
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
//不是那么就证明是重复项,就舍弃
return arr.indexOf(item) === index;
)
这里有可能存在疑问,我来举个例子:
const arr = [1,1,2,1,3]
arr.indexOf(arr[0]) === 0 // 1 的第一次出现
arr.indexOf(arr[1]) !== 1 // 说明前面曾经出现过1
reduce
配合 includes
function unique(arr)
let uniqueArr = arr.reduce((acc,cur)=>
if(!acc.includes(cur))
acc.push(cur);
return acc;
,[]) // []作为回调函数的第一个参数的初始值
return uniqueArr
数组扁平化
考察频率: (⭐⭐⭐)
参考代码[3]
forEach
考察频率: (⭐⭐⭐)
Array.prototype.myForEach = function (callbackFn)
// 判断this是否合法
if (this === null || this === undefined)
throw new TypeError("Cannot read property 'myForEach' of null");
// 判断callbackFn是否合法
if (Object.prototype.toString.call(callbackFn) !== "[object Function]")
throw new TypeError(callbackFn + ' is not a function')
// 取到执行方法的数组对象和传入的this对象
var _arr = this, thisArg = arguments[1] || window;
for (var i = 0; i < _arr.length; i++)
// 执行回调函数
callbackFn.call(thisArg, _arr[i], i, _arr);
reduce
考察频率: (⭐⭐⭐)
Array.prototype.myReduce = function(callbackFn)
var _arr = this, accumulator = arguments[1];
var i = 0;
// 判断是否传入初始值
if (accumulator === undefined)
// 没有初始值的空数组调用reduce会报错
if (_arr.length === 0)
throw new Error('initVal and Array.length>0 need one')
// 初始值赋值为数组第一个元素
accumulator = _arr[i];
i++;
for (; i<_arr.length; i++)
// 计算结果赋值给初始值
accumulator = callbackFn(accumulator, _arr[i], i, _arr)
return accumulator;
map
Array.prototype.myMap = function(callbackFn)
var _arr = this, thisArg = arguments[1] || window, res = [];
for (var i = 0; i<_arr.length; i++)
// 存储运算结果
res.push(callbackFn.call(thisArg, _arr[i], i, _arr));
return res;
filter
Array.prototype.myFilter = function(callbackFn)
var _arr = this, thisArg = arguments[1] || window, res = [];
for (var i = 0; i<_arr.length; i++)
// 回调函数执行为true
if (callbackFn.call(thisArg, _arr[i], i, _arr))
res.push(_arr[i]);
return res;
every
Array.prototype.myEvery = function(callbackFn)
var _arr = this, thisArg = arguments[1] || window;
// 开始标识值为true
// 遇到回调返回false,直接返回false
// 如果循环执行完毕,意味着所有回调返回值为true,最终结果为true
var flag = true;
for (var i = 0; i<_arr.length; i++)
// 回调函数执行为false,函数中断
if (!callbackFn.call(thisArg, _arr[i], i, _arr))
return false;
return flag;
some
Array.prototype.mySome = function(callbackFn)
var _arr = this, thisArg = arguments[1] || window;
// 开始标识值为false
// 遇到回调返回true,直接返回true
// 如果循环执行完毕,意味着所有回调返回值为false,最终结果为false
var flag = false;
for (var i = 0; i<_arr.length; i++)
// 回调函数执行为false,函数中断
if (callbackFn.call(thisArg, _arr[i], i, _arr))
return true;
return flag;
find/findIndex
Array.prototype.myFind = function(callbackFn)
var _arr = this, thisArg = arguments[1] || window;
// 遇到回调返回true,直接返回该数组元素
// 如果循环执行完毕,意味着所有回调返回值为false,最终结果为undefined
for (var i = 0; i<_arr.length; i++)
// 回调函数执行为false,函数中断
if (callbackFn.call(thisArg, _arr[i], i, _arr))
return _arr[i];
return undefined;
indexOf
function indexOf(findVal, beginIndex = 0)
if (this.length < 1 || beginIndex > findVal.length)
return -1;
if (!findVal)
return 0;
beginIndex = beginIndex <= 0 ? 0 : beginIndex;
for (let i = beginIndex; i < this.length; i++)
if (this[i] == findVal) return i;
return -1;
实现sort
参考代码[4]
🌊 防抖节流前端开发博客
实现防抖函数debounce
考察频率: (⭐⭐⭐⭐⭐)
function debounce(func, wait, immediate)
var timeout, result;
var debounced = function ()
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate)
// 如果已经执行过,不再执行
var callNow = !timeout;
timeout = setTimeout(function()
timeout = null;
, wait)
if (callNow) result = func.apply(context, args)
else
timeout = setTimeout(function()
result = func.apply(context, args)
, wait);
return result;
;
debounced.cancel = function()
clearTimeout(timeout);
timeout = null;
;
return debounced;
实现节流函数throttle
考察频率: (⭐⭐⭐⭐⭐)
// 第四版
function throttle(func, wait, options)
var timeout, context, args, result;
var previous = 0;
if (!options) options = ;
var later = function()
previous = options.leading === false ? 0 : new Date().getTime();
timeout = null;
func.apply(context, args);
if (!timeout) context = args = null;
;
var throttled = function()
var now = new Date().getTime();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait)
if (timeout)
clearTimeout(timeout);
timeout = null;
previous = now;
func.apply(context, args);
if (!timeout) context = args = null;
else if (!timeout && options.trailing !== false)
timeout = setTimeout(later, remaining);
;
return throttled;
⛲ Object篇
能不能写一个完整的深拷贝
考察频率: (⭐⭐⭐⭐⭐)
const getType = obj => Object.prototype.toString.call(obj);
const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null;
const canTraverse =
'[object Map]': true,
'[object Set]': true,
'[object Array]': true,
'[object Object]': true,
'[object Arguments]': true,
;
const mapTag = '[object Map]';
const setTag = '[object Set]';
const boolTag = '[object Boolean]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';
const handleRegExp = (target) =>
const source, flags = target;
return new target.constructor(source, flags);
const handleFunc = (func) =>
// 箭头函数直接返回自身
if(!func.prototype) return func;
const bodyReg = /(?<=)(.|\\n)+(?=)/m;
const paramReg = /(?<=().+(?=)\\s+)/;
const funcString = func.toString();
// 分别匹配 函数参数 和 函数体
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if(!body) return null;
if (param)
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
else
return new Function(body[0]);
const handleNotTraverse = (target, tag) =>
const Ctor = target.constructor;
switch(tag)
case boolTag:
return new Object(Boolean.prototype.valueOf.call(target));
case numberTag:
return new Object(Number.prototype.valueOf.call(target));
case stringTag:
return new Object(String.prototype.valueOf.call(target));
case symbolTag:
return new Object(Symbol.prototype.valueOf.call(target));
case errorTag:
case dateTag:
return new Ctor(target);
case regexpTag:
return handleRegExp(target);
case funcTag:
return handleFunc(target);
default:
return new Ctor(target);
const deepClone = (target, map = new WeakMap()) =>
if(!isObject(target))
return target;
let type = getType(target);
let cloneTarget;
if(!canTraverse[type])
// 处理不能遍历的对象
return handleNotTraverse(target, type);
else
// 这波操作相当关键,可以保证对象的原型不丢失!
let ctor = target.constructor;
cloneTarget = new ctor();
if(map.get(target))
return target;
map.set(target, true);
if(type === mapTag)
//处理Map
target.forEach((item, key) =>
cloneTarget.set(deepClone(key, map), deepClone(item, map));
)
if(type === setTag)
//处理Set
target.forEach(item =>
cloneTarget.add(deepClone(item, map));
)
// 处理数组和对象
for (let prop in target)
if (target.hasOwnProperty(prop))
cloneTarget[prop] = deepClone(target[prop], map);
return cloneTarget;
参考博客[5]
实现new
考察频率: (⭐⭐⭐⭐)
function createObject(Con)
// 创建新对象obj
// var obj = ;也可以
var obj = Object.create(null);
// 将obj.__proto__ -> 构造函数原型
// (不推荐)obj.__proto__ = Con.prototype
Object.setPrototypeOf(obj, Con.prototype);
// 执行构造函数,并接受构造函数返回值
const ret = Con.apply(obj, [].slice.call(arguments, 1));
// 若构造函数返回值为对象,直接返回该对象
// 否则返回obj
return typeof(ret) === 'object' ? ret: obj;
继承
考察频率: (⭐⭐⭐⭐)
原型链继承
借用构造函数(经典继承)
组合继承
原型式继承
寄生式继承
寄生组合式继承
Class实现继承(补充一下)
class Animal
constructor(name)
this.name = name
getName()
return this.name
class Dog extends Animal
constructor(name, age)
super(name)
this.age = age
参考代码[6]
实现object.create
function newCreate(proto, propertiesObject)
if (typeof proto !== 'object' && typeof proto !== 'function')
throw TypeError('Object prototype may only be an Object: ' + proto)
function F()
F.prototype = proto
const o = new F()
if (propertiesObject !== undefined)
Object.keys(propertiesObject).forEach(prop =>
let desc = propertiesObject[prop]
if (typeof desc !== 'object' || desc === null)
throw TypeError('Object prorotype may only be an Object: ' + desc)
else
Object.defineProperty(o, prop, desc)
)
return o
🚂 Function篇
call
考察频率: (⭐⭐⭐⭐)
Function.prototype.myCall = function (thisArg)
thisArg = thisArg || window;
thisArg.func = this;
const args = []
for (let i = 1; i<arguments.length; i++)
args.push('arguments['+ i + ']')
const result = eval('thisArg.func(' + args +')')
delete thisArg.func;
return result;
bind
考察频率: (⭐⭐⭐⭐)
Function.prototype.sx_bind = function (obj, ...args)
obj = obj || window
const fn = Symbol()
obj[fn] = this
const _this = this
const res = function (...innerArgs)
console.log(this, _this)
if (this instanceof _this)
this[fn] = _this
this[fn](...[...args, ...innerArgs])
delete this[fn]
else
obj[fn](...[...args, ...innerArgs])
delete obj[fn]
res.prototype = Object.create(this.prototype)
return res
apply
考察频率: (⭐⭐⭐⭐)
Function.prototype.myApply = function (thisArg, arr)
thisArg = thisArg || window;
thisArg.func = this;
const args = []
for (let i = 0; i<arr.length; i++)
args.push('arr['+ i + ']')
const result = eval('thisArg.func(' + args +')')
delete thisArg.func;
return result;
实现柯里化
考察频率: (⭐⭐⭐)
参考代码[7]
实现链式调用
参考代码[8]
偏函数
参考代码[9]
🌍 ajax 与 jsonp
考察频率: (⭐⭐⭐)
实现ajax
function ajax(
url= null,
method = 'GET',
dataType = 'JSON',
async = true)
return new Promise((resolve, reject) =>
let xhr = new XMLHttpRequest()
xhr.open(method, url, async)
xhr.responseType = dataType
xhr.onreadystatechange = () =>
if(!/^[23]\\d2$/.test(xhr.status)) return;
if(xhr.readyState === 4)
let result = xhr.responseText
resolve(result)
xhr.onerror = (err) =>
reject(err)
xhr.send()
)
实现jsonp
const jsonp = ( url, params, callbackName ) =>
const generateUrl = () =>
let dataSrc = ''
for (let key in params)
if (params.hasOwnProperty(key))
dataSrc += `$key=$params[key]&`
dataSrc += `callback=$callbackName`
return `$url?$dataSrc`
return new Promise((resolve, reject) =>
const scriptEle = document.createElement('script')
scriptEle.src = generateUrl()
document.body.appendChild(scriptEle)
window[callbackName] = data =>
resolve(data)
document.removeChild(scriptEle)
)
🛫 ES6篇
实现set
class Set
constructor()
this.items = ;
this.size = 0;
has(element)
return element in this.items;
add(element)
if(! this.has(element))
this.items[element] = element;
this.size++;
return this;
delete(element)
if (this.has(element))
delete this.items[element];
this.size--;
return this;
clear()
this.items =
this.size = 0;
values()
let values = [];
for(let key in this.items)
if(this.items.hasOwnProperty(key))
values.push(key);
return values;
实现 map
function defaultToString(key)
if(key === null)
return 'NULL';
else if (key === undefined)
return 'UNDEFINED'
else if (Object.prototype.toString.call(key) === '[object Object]' || Object.prototype.toString.call(key) === '[object Array]')
return JSON.stringify(key);
return key.toString();
class Map
constructor()
this.items = ;
this.size = 0;
set(key, value)
if(!this.has(key))
this.items[defaultToString(key)] = value;
this.size++;
return this;
get(key)
return this.items[defaultToString(key)];
has(key)
return this.items[defaultToString(key)] !== undefined;
delete(key)
if (this.has(key))
delete this.items[key];
this.size--;
return this;
clear()
this.items =
this.size = 0;
keys()
let keys = [];
for(let key in this.items)
if(this.has(key))
keys.push(key)
return keys;
values()
let values = [];
for(let key in this.items)
if(this.has(key))
values.push(this.items[key]);
return values;
实现es6的class
参考代码[10]
🦉 其他
instanceof
考察频率: (⭐⭐⭐⭐)
function instance_of(Case, Constructor)
// 基本数据类型返回false
// 兼容一下函数对象
if ((typeof(Case) != 'object' && typeof(Case) != 'function') || Case == 'null') return false;
let CaseProto = Object.getPrototypeOf(Case);
while (true)
// 查到原型链顶端,仍未查到,返回false
if (CaseProto == null) return false;
// 找到相同的原型
if (CaseProto === Constructor.prototype) return true;
CaseProto = Object.getPrototypeOf(CaseProto);
实现千分位分隔符
考察频率: (⭐⭐⭐)
var str = "100000000000",
reg = /(?=(\\B\\d3)+$)/g;
str.replace(reg, ",")
把一个JSON对象的key从下划线形式(Pascal)转换到小驼峰形式(Camel)
考察频率: (⭐⭐⭐)
参考代码[11]
实现数据类型判断函数
function myTypeof(obj)
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
实现数组转树
参考代码[12]
实现sleep函数
// promise
const sleep = time =>
return new Promise(resolve => setTimeout(resolve,time))
sleep(1000).then(()=>
console.log(1)
)
// ES5
function sleep(callback,time)
if(typeof callback === 'function')
setTimeout(callback,time)
function output()
console.log(1);
sleep(output,1000);
实现发布订阅模式
class EventEmitter
constructor()
this.cache =
on(name, fn)
if (this.cache[name])
this.cache[name].push(fn)
else
this.cache[name] = [fn]
off(name, fn)
let tasks = this.cache[name]
if (tasks)
const index = tasks.findIndex(f => f === fn || f.callback === fn)
if (index >= 0)
tasks.splice(index, 1)
emit(name, once = false, ...args)
if (this.cache[name])
// 创建副本,如果回调函数内继续注册相同事件,会造成死循环
let tasks = this.cache[name].slice()
for (let fn of tasks)
fn(...args)
if (once)
delete this.cache[name]
🛕 更多题目 前端开发博客
传送门: 前端题目[13]
如果感觉有帮助的话,别忘了给小包点个 ⭐ 。
💘 往期精彩文章
牛客最新前端JS笔试百题[14]
牛客最新前端面经面试题汇总(含解析)[15]
抓取牛客最新前端面试题五百道 数据分析JS面试热点[16]
给VSCode和网站领养喵咪 一起快乐撸猫[17]
原生javascript灵魂拷问(一),你能答上多少?[18]
JavaScript之彻底理解原型与原型链[19]
JavaScript之彻底理解EventLoop[20]
《2w字大章 38道面试题》彻底理清JS中this指向问题[21]
💥 后语
伙伴们,如果大家感觉本文对你有一些帮助,给阿包点一个赞👍或者关注➕都是对我最大的支持。
另外如果本文章有问题,或者对文章其中一部分不理解,都可以评论区回复我,我们来一起讨论,共同学习,一起进步!
如果感觉评论区说不明白,也可以添加我的微信(li444186976)或者 qq(3315161861) 详细交流,名字都是战场小包。
关于本文
作者:战场小包,已授权
https://juejin.cn/post/7033275515880341512
推荐链接
创作不易,加个点赞、在看 支持一下哦!
以上是关于2021年前端各大公司都考了那些手写题(附带代码)的主要内容,如果未能解决你的问题,请参考以下文章