一般面试1个小时,20分钟会让你手写代码,有些题目写不出来,还是非常影响面试官对你评价的。手写题包括:常见的
方法
、编程题
、leetcode
的实现,本文精选常见的一些面试手写题,供大家参考、交流、学习。
1. 判断js常见的类型 ⭐️⭐️⭐️⭐️⭐️
function type(value) {
return Object.prototype.toString.call(value)
}
2. 实现call ⭐️⭐️⭐️⭐️
Function.prototype.myCall = function (context = window) {
// 1. 判断调用myCall的类型, 因为call方法是函数原型上的方法
if (typeof this !== 'function') {
throw new TypeError('caller is not a function');
}
// 2. 将fn赋值给context的一个属性,这样执行context.fn(), 函数内的this指向就是context了
context.fn = this;
// 3. 获取除了context以外的函数参数
const args = [...arguments].slice(1);
// 4. 执行函数
const result = context.fn(...args);
// 5. 删除属性
delete context.fn;
return result;
}
// test ...
const obj = {
name: 'a'
}
function fn(a,b) {
console.log(this.name, a, b)
}
fn.myCall(obj, 1) // 'a' 1
3. 实现apply⭐️⭐️⭐️⭐️
Function.prototype.myApply = function(context = window) {
if (typeof this !== 'function') {
throw new TypeError('caller is not a function');
}
context.fn = this;
// 这里函数第二个参数可能是undefined 或者 数组
const args = arguments[1] || [];
const result = context.fn(...args);
delete context.fn;
return result;
}
// test ...
const obj = {
name: 'a'
}
function fn(a,b) {
console.log(this.name, a, b)
}
fn.myApply(obj, [1, 2])
4. 实现bind ⭐️⭐️⭐️⭐️
Function.prototype.myBind = function(context = window) {
if (typeof this !== 'function') {
throw new TypeError('caller is not a function');
}
context.fn = this;
const args = [...arguments].slice(1)
return function() {
const result = context.fn(...args);
delete context.fn;
return result;
}
}
// test ...
const obj = {
name: 'a'
}
function fn(a,b) {
console.log(this.name, a, b)
}
const o = fn.myBind(obj, 1, 2)
o()
5. 实现sleep:休眠一段时间再执行后续代码 ⭐️⭐️⭐️⭐️
const sleep = time => new Promise(resolve => setTimeout(resolve, time))
// test...
async function main() {
await sleep(2000)
console.log('after sleep')
}
main()
6. delay: 推迟多久后执行函数 ⭐️⭐️⭐️⭐️
function delay(fn, time, ...args) {
return new Promise(resolve => {
setTimeout(() => {
Promise.resolve(fn(...args)).then(resolve)
}, time)
})
}
// test ...
async function main(a) {
console.log(a)
}
delay(main, 3000, 123)
7. 实现Promise.all ⭐️⭐️⭐️⭐️⭐️
function myAll(arr) {
let results = []
return new Promise((resolve,reject) => {
let count = 0
arr.forEach((item, index) => {
Promise.resolve(item).then(res => {
results[index] = res
if (++count === arr.length) {
resolve(results)
}
}).catch(e => reject(e))
})
})
}
// test ....
const p1 = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve('p1')
}, 2000)
})
}
const p2 = () => Promise.resolve(2)
const p3 = 3
myAll([p1(), p2(), p3]).then(res => {
console.log(res)
})
8. 实现isArray ⭐️⭐️⭐️
function isArray(value) {
return Object.prototype.toString.call(value) === '[object Array]'
}
// test...
console.log(isArray([]))
console.log(isArray({}))
console.log(isArray(1))
9. 实现flatten: 数组扁平化 ⭐️⭐️⭐️⭐️⭐️
const arr = [1, [2], [3], [[4, 5]]]
function flat(arr) {
return arr.reduce((prev, cur) => {
return prev.concat(Array.isArray(cur) ? flat(cur) : cur)
}, [])
}
console.log(flat(arr))
10. 实现forEach, reduce ⭐️⭐️⭐️⭐️⭐️
Array.prototype.myForEach = function(cb) {
const arr = this;
for (let i = 0; i < arr.length; i++) {
cb(arr[i], i)
}
}
// test ...
const arr = [
{ name: 'xz', age: 18},
{ name: 'tl', age: 20},
{ name: 'xl', age: 20},
]
arr.myForEach((item, index) => {
console.log(item ,index)
})
Array.prototype.myReduce = function(cb, initValue) {
const arr = this;
let result = initValue
for (let i = 0; i < arr.length; i ++) {
result = cb(result ,arr[i], i)
}
return result
}
// test ,..
const s = arr.myReduce((prev, cur, index) => {
return prev + cur.age
}, 0)
console.log(s)
11. 实现trim ⭐️⭐️⭐️⭐️
function trim(str) {
return str.trim?.() || str.replace(/^\s+|\s+$/g)
}
console.log(trim(' fdgbhn '))
12. 实现冒泡排序 ⭐️⭐️⭐️⭐️
function bubbleSort(arr) {
const len = arr.length;
for (let i = 0; i < len; i ++) {
for (let j = i + 1; j < len; j ++) {
if (arr[i] > arr[j]) {
const temp = arr[i];
arr[i] = arr[j]
arr[j] = temp
}
}
}
return arr
}
13. 实现快排 ⭐️⭐️⭐️⭐️
function quickSort(arr) {
const len = arr.length;
if (len <= 1) return arr
const midIndex = Math.floor(len / 2)
let left = []
let right = []
const midValue = arr[midIndex]
for (let i = 0; i < len; i ++) {
if (i !== midIndex) {
if (arr[i] < midValue) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
}
return arguments.callee(left).concat([midValue], arguments.callee(right))
}
14. 实现instanceof ⭐️⭐️⭐️
function instanceOf(obj, object) {
let O = object.prototype
obj = obj.__proto__;
while(true) {
if (obj === null) {
return false;
}
if (O === obj) {
return true
}
obj = obj.__proto__;
}
}
15. 实现new操作符 ⭐️⭐️⭐️
function _new(fn, ...args) {
const obj = Object.create(fn.prototype)
const result = fn.apply(obj, args)
return result instanceof Object ? result : obj
}
// test. ..
function Animal(name) {
this.name = name;
}
const dog = _new(Animal, 'dog')
console.log(dog)
16. 柯里化函数curry ⭐️⭐️⭐️⭐️
function curry(fn) {
const len = fn.length;
return function currying(...args) {
if (len === args.length) {
return fn.apply(null, args)
}
return function (...args2) {
return currying.apply(null, [...args, ...args2])
}
}
}
// test ...
function add (x, y) {
return x + y
}
const fn = curry(add)
console.log(fn(2)(3))
17. 部门数据结构转换 ⭐️⭐️⭐️⭐️⭐️
// 给定的数据结构
let arr = [
{ id: 1, name: "部门1", pid: 0 },
{ id: 2, name: "部门2", pid: 1 },
{ id: 3, name: "部门3", pid: 1 },
{ id: 4, name: "部门4", pid: 3 },
{ id: 5, name: "部门5", pid: 4 },
];
// 转化成带部门层级的结构
// [
// {
// "id": 1,
// "name": "部门1",
// "pid": 0,
// "children": [
// {
// "id": 2,
// "name": "部门2",
// "pid": 1,
// "children": []
// },
// {
// "id": 3,
// "name": "部门3",
// "pid": 1,
// "children": [
// // 结果 ,,,
// ]
// }
// ]
// }
// ]
const transform = (items) => {
let result = [];
const itemMap = {};
for (const item of items) {
itemMap[item.id] = { ...item, children: [] };
}
for (const item of items) {
const id = item.id;
const pid = item.pid;
const treeItem = itemMap[id];
if (pid === 0) {
result.push(treeItem);
} else {
if (!itemMap[pid]) {
itemMap[pid] = {
children: [],
};
}
itemMap[pid].children.push(treeItem);
}
}
return result;
};
console.log(transform(arr));
18. 实现一个发布订阅事件 ⭐️⭐️⭐️⭐️⭐️
class Subscribe {
eventMap = {}
on(event, cb) {
if (this.eventMap[event]) {
this.eventMap[event].push(cb)
} else {
this.eventMap[event] = [cb]
}
}
emit(event, ...args) {
if (!this.eventMap[event]) console.log("-----no-event")
if (this.eventMap[event]) {
this.eventMap[event].forEach(fn => {
fn(...args)
})
}
}
off (event) {
delete this.eventMap[event]
}
}
// test ...
const s = new Subscribe()
s.on('click', x => console.log(x))
s.emit('click', { id: 3 } )
s.off('click')
s.emit('click', { id: 3 } )
19. 求数组中最大的数 ⭐️⭐️⭐️⭐️⭐️
function max(arr) {
if (arr.length === 0) return 0
return arr.reduce((prev, cur) => prev > cur ? prev : cur)
}
console.log(max([1,2,3,4,5,6,7]))
20. 求出现次数最多的字符 ⭐️⭐️⭐️⭐️⭐️
// 解法一:
function maxChar(str) {
const dict = {}
for (const s of str) {
dict[s] = (dict[s] || 0) + 1
}
let max = ['', 0]
Object.keys(dict).forEach(item => {
if (dict[item] > max[1]) {
max = [item , dict[item]]
}
})
return max
}
console.log(maxChar(str))
// 优化后:
function maxChar(str) {
const dict = {}
let maxChar = ['', 0]
for (const s of str) {
dict[s] = (dict[s] || 0) + 1;
if (dict[s] > maxChar[1]) {
maxChar = [s, dict[s]]
}
}
return maxChar
}
const str = 'asdfdgfhjhkassaaaaa'
console.log(maxChar(str))
21. 对字符编码压缩 ⭐️⭐️⭐️⭐️⭐️
// Input: 'aaaabbbccd'
// Output: 'a4b3c2d1'
const str = "aaaabbbccd"
const dict = {
a: 4,
b: 3,
c: 2,
d: 1
}
function encode(str) {
const dict = {}
for (const s of str) {
dict[s] = (dict[s] || 0) + 1
}
let result = ''
Object.keys(dict).forEach(item => {
result += item + dict[item]
})
return result
}
console.log(encode(str)) // a4b3c2d1
22. LRU cache ⭐️⭐️⭐️⭐️⭐️
class LRUCache {
constructor(limit) {
this.limit = limit;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) return undefined;
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
put(key, value) {
if (this.cache.has(key)) this.cache.delete(key);
else if (this.cache.size >= this.limit) {
this.cache.delete(this.cache.keys().next().value);
}
this.cache.set(key, value);
}
}
// ["LRUCache","put","put","get","put","get","put","get","get","get"]
// [[2],[1,1],[2,2],[1],[3,3],[2],[4,4],[1],[3],[4]]
const lruCache = new LRUCache(2);
lruCache.put(1, 1);
lruCache.put(2, 2);
const res1 = lruCache.get(1);
lruCache.put(3, 3);
const res2 = lruCache.get(2);
lruCache.put(4, 4);
const res3 = lruCache.get(1);
const res4 = lruCache.get(3);
const res5 = lruCache.get(4);
console.log(res1, res2, res3, res4, res5);
// 1 undefined undefined 3 4
23. 实现jsonp ⭐️⭐️⭐️⭐️⭐️
function jsonp({ url, params, onData }) {
const script = document.createElement('script');
script.src = `${url}/${JSON.stringify({ callback: 'callback', ...params })}`
window['callback'] = onData;
document.body.appendChild(script)
}
jsonp({
url: 'localhost:8000',
params: {
id: 1
},
onData(data) {
console.log('data', data)
}
})
24. 给数字添加千位符 ⭐️⭐️⭐️⭐️⭐️
function numFormat(num) {
num = num.toString().split("."); // [123456789, 123456678]
const arr = num[0].split("").reverse();
let res = [];
for (let i = 0; i < arr.length; i++) {
if (i !== 0 && i % 3 === 0) {
res.push(",");
}
res.push(arr[i]);
}
res.reverse();
if (num[1]) {
res = res.join("") + "." + num[1];
} else {
res = res.join("");
}
return res;
}
console.log(numFormat(123456789.12345678))
25. 斐波那契数列 ⭐️⭐️⭐️⭐️⭐️
// 1,1,2,3,5,8,13.........n
// -----------100----------
function fib(n) {
if (n <= 2) return 1
return fib(n-1) + fib(n-2)
}
26. 斐波那契数列: 重复计算优化 ⭐️⭐️⭐️⭐️⭐️
let fib = (function () {
let memo = new Map()
return function(n) {
const memorized = memo.get(n)
if (memorized) return memorized
if (n <= 2) return 1
let f1 = fib(n - 1)
let f2 = fib(n - 2)
memo.set(n-1, f1)
memo.set(n-2, f2)
return f1 + f2
}
})()
console.log(fib(50))
27. 斐波那契数列: 堆栈优化 ⭐️⭐️⭐️⭐️⭐️
function fib(n, current = 0, next = 1) {
if (n === 0) return 0
if (n === 1) return next;
return fib(n - 1, next, current + next)
}
28. 斐波那契数列: 自下而上求解 ⭐️⭐️⭐️⭐️⭐️
// 将数值保存在dp数组里
function fib(n) {
let dp = []
dp[0] = 0
dp[1] = 1
dp[2] = 1
for (let i = 3; i <= n; i ++) {
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
}
29. 叶子节点路径和 ⭐️⭐️⭐️⭐️
let tree = {
val: 1,
left: {
val: 2,
left: {
val: 4,
left: null,
right: null,
},
right: {
val: 5,
left: null,
right: null,
},
},
right: {
val: 3,
left: null,
right: null,
},
};
// 递归遍历二叉树,同时记录路径数值,遇到叶子节点计算路径和。
function hasPathSum(root, target) {
if (!root) return false;
if (!root.left && !root.right) {
// 叶子节点与剩余的数比较
return target === root.val;
}
return (
hasPathSum(root.left, target - root.val) ||
hasPathSum(root.right, target - root.val)
);
}
console.log(hasPathSum(tree, 8));
30. deepClone ⭐️⭐️⭐️⭐️⭐️
const obj = {
a: 1,
b: {
c: 1
}
}
function deepClone(obj) {
if (typeof obj === null) return null;
if (typeof obj !== "object") return obj;
let result = Array.isArray(obj) ? [] : {}
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] =
typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key];
}
}
return result;
}
const o = deepClone(obj)
o.b.c = 2
console.log(obj)
31. throttle ⭐️⭐️⭐️⭐️⭐️
// n 秒内只执行一次
function throttle(fn, wait) {
let lastTime = 0
return function () {
const now = Date.now()
if (now - lastTime > wait) {
fn.apply(null, arguments)
lastTime = now
}
}
}
const test = i => console.log(`test+${i}`)
const throttled = throttle(test, 1000)
for(let i = 0; i < 10; i ++) {
throttled(i)
}
32. debounce ⭐️⭐️⭐️⭐️⭐️
// n 秒后执行最后一次的请求
function debounce(fn, delay) {
let timer = null;
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(null, arguments)
}, delay)
}
}
const test = i => console.log(`test-----${i}`)
const debounced = debounce(test, 1000)
for(let i = 0; i < 10; i++) {
debounced(i)
}
33. isEqual ⭐️⭐️⭐️⭐️⭐️
var obj1 = {
x1: 1,
x2: { x: 1, y: 1 }
};
var obj2 = {
x1: 1,
x2: { x: 1, y: 1 }
};
function isObject(obj) {
return typeof obj === 'object' && obj !== null;
}
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
return obj1 === obj2
}
if (obj1 === obj2) return true;
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) return false;
for (const key in obj1) {
const res = isEqual(obj1[key], obj2[key])
console.log('-----res', obj1[key], obj2[key])
if (!res) return false;
}
return true
}
console.log(isEqual(obj1, obj2))
34. compose ⭐️⭐️⭐️⭐️⭐️
// 从左向右计算
const composeLeft = (...fns) => fns.reduce((f,g) => (...args) => g(f(...args)))
// 从右向左计算
const composeRight = (...fns) => fns.reduceRight((f, g) => (...args) => g(f(...args)))
const add5 = x => x + 5
const multiply = x => x * 10
const left = composeLeft(
add5,
multiply
)
const right = composeRight(
add5,
multiply
)
console.log(left(10)) // 105
console.log(right(10)) // 105
35. shuffle: 随机洗牌函数 ⭐️⭐️⭐️
const arr = [3, 1, 5, 4]
//随机洗牌函数
const shuffle = (list) => list.sort((x, y) => Math.random() - 0.5)
console.log(shuffle(arr))
36. sample: 从数组中随机取一个数 ⭐️⭐️⭐️
// 从数组中随机取一个数
Array.prototype.sample = function () {
if (!Array.isArray(this)) return new TypeError('this is not array')
const len = this.length;
return this[Math.floor(Math.random() * len)]
}
// Math.random() // (0, 1)
// len (1, 4)
const arr = [1, 2, 3, 4]
const random = arr.sample()
console.log(random)
37. difference: 取数组交集 ⭐️⭐️⭐️⭐️⭐️
// 取数组交集
const arr1 = [1, 2, 3, 5]
const arr2 = [1, 2, 4]
const intersection = (...list) => {
const result = list.reduce((x, y) => {
return x.filter(i => y.includes(i))
})
return [...new Set(result)]
}
console.log(intersection(arr1, arr2))
ps: github-code
最后祝大家跳槽成功,财源滚滚🧧🧧🧧