前端面试 - 常考的手撕代码题

本文详细解析了前端面试中常见的手撕代码题目,包括各种排序和查找算法(冒泡、快速、归并等)、call、bind、apply的实现、深拷贝、防抖和节流函数的模拟、字符串转驼峰、数组拍平和去重方法,以及函数柯里化的概念和应用。这些知识点对于提升前端面试技巧至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 各种排序+查找算法


冒泡排序 快速排序 归并排序 插入排序 选择排序 顺序查找 二分查找 [https://blog.youkuaiyun.com/Sabrina_cc/article/details/106857519](()

2. call bind apply


apply 、 call 、bind 三者都是用来改变函数的this对象的指向的;第一个参数都是this要指向的对象,也就是想指定的上下文;

call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。

所以我们模拟的步骤可以分为:(1)将函数设为对象的属性(2)执行该函数(3)删除该函数

Function.prototype.myCall = function(context) {

/*if (typeof this !== ‘function’) {

throw new TypeError(‘Error’)

}*/

context = context || window; //第一个参数为null或者undefined时,this指向全局对象window

context.fn = this; // 非常关键:改变 this 的作用域

const args = […arguments].slice(1); //从 Arguments 对象中取取第二到最后一个参数放到一个数组里

const result = context.fn(…args); //把这个参数数组放到要执行的函数的参数里面去

delete context.fn; // 必须删除,会给 context 增加方法 fn

return result;

}

// 简单测试

let obj = {

name: ‘xxx’

};

function test(){

console.log(‘arguments=’,arguments); // Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]

console.log(‘this=’,this); // {name: “xxx”, fn: ƒ}

}

test.myCall(obj,1,2);

apply 接受一个数组(或者类数组对象)作为参数输入

Function.prototype.myApply = function(context) {

/* if (typeof this !== ‘function’) {

throw new TypeError(‘Error’)

}*/

context = context || window;

context.fn = this;

let result;

if (arguments[1]) {

result = context.fn(…arguments[1]); // 处理参数和 call 有区别

} else {

result = context.fn();

}

delete context.fn;

return result;

}

let foo = {

value: 1

}

function bar(name, age) {

console.log(name)

console.log(age)

console.log(this.value)

}

bar.apply(foo, [‘kevin’, 18]) // kevin 18 1

会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )

由此我们可以首先得出 bind 函数的两个特点:(1)返回一个函数(2)可以传入参数(3)一个绑定函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数。

Function.prototype.bind 《大厂前端面试题解析+Web核心总结学习笔记+企业项目实战源码+最新高清讲解视频》无偿开源 徽信搜索公众号【编程进阶路】 = function(context, …bindArgs) {

const func = this;// func 为调用 bind 的原函数

context = context || window;

if (typeof func !== ‘function’) {

throw new TypeError(‘Bind must be called on a function’);

}

// bind 返回一个绑定 this 的函数

return function(…callArgs) {

let args = bindArgs.concat(callArgs);

if (this instanceof func) {

// 意味着是通过 new 调用的 而 new 的优先级高于 bind

return new func(…args);

}

return func.call(context, …args);

}

}

参考链接:[JavaScript 之 call和apply,bind 的模拟实现](()

[深入理解 call,apply 和 bind](()

[apply、call和 bind 方法详解(含手写)](()

3. 深拷贝


function deepClone(obj = {}) {

if (typeof obj !== ‘object’ || obj == null) {

return obj // obj 是 null ,或者不是对象和数组,直接返回

}

let result // 初始化返回结果

if (obj instanceof Array) {    // 判断是否为数组

result = []

} else {

result = {}

}

for (let key in obj) {

if (obj.hasOwnProperty(key)) { // 保证 key 不是原型的属性

result[key] = deepClone(obj[key]) // 递归调用!!!

}

}

return result    // 返回结果

}

let a = {

age: undefined,

sex: Symbol(‘male’),

jobs: function () {},

name: ‘cmk’

}

let b = JSON.parse(JSON.stringify(a))

console.log(b) // { name: ‘cmk’ }

let c = deepClone(a)

console.log© // { age: undefined, sex: Symbol(male), jobs: [Function: jobs], name: ‘cmk’ }

4. 防抖 节流


scroll 事件本身会触发页面的重新渲染,同时 scroll 事件的 handler 又会被高频度的触发, 因此事件的 handler 内部不应该有复杂操作,例如 DOM 操作就不应该放在事件处理中。 针对此类高频度触发事件问题(例如页面 scroll ,屏幕 resize,监听用户输入等),有两种常用的解决方法,防抖和节流。

Function.prototype.myThrottle = function(cb, wait) {

let flag = false;

return function(…rest) {

if (flag) {

return;

}

flag = true;// 在wait时间范围内。。通过flag不让执行cb;

setTimeout(() => {

cb.apply(this, rest);

flag = false;

}, wait)

}

}

Function.prototype.myDebounce = function(cb, wait) {

let timer = null;

return function (…rest) {

if (timer) {

// 如果上次的定时器还存在,则清除。重新等待时间

clearTimeour(timer);

}

timer = setTimeout(() => {// 使用箭头函数,确保this还能定义在当前执行环境

// 里边的闭包的this必须保持和callback保持一致。确保使用ok

cb.apply(this, rest);

}, wait)

}

}

6. 字符串转驼峰


// 字符串转驼峰,注意第一个字符不大写,如border-bottom-color -> borderBottomColor

function Change(str) {

let arr = str.split(‘-’) // 字符串分割为数组

// map返回由新的项组成的数组

arr = arr.map((item, index) => {

return index === 0 ? item : item.charAt(0).toUpperCase() + item.substring(1) // 第一个字符转大写与之后的字符合并

})

return arr.join(“”) // 数组合并为字符串

}

console.log(Change(‘border-bottom-color’))

7. 数组拍平


function flat(arr) {

// 验证 arr 中,还有没有深层数组 [1, 2, [3, 4]]

const isDeep = arr.some(item => item instanceof Array)

if (!isDeep) {

return arr // 已经是 flatern [1, 2, 3, 4]

}

// oncat只能解决单层[]

const res = Array.prototype.concat.apply([], arr)

return flat(res) // 递归

}

const res = flat( [1, 2, [3, 4, [10, 20, [100, 200]]], 5] )

console.log(res)

function flattern(arr) {

return arr.reduce((preValue, currValue, currIndex, array) => {

return preValue.concat(Array.isArray(currValue) ? flattern(currValue) : currValue)

}, []) // []作为第一个preValue

}

// toString & split

function flattern2(arr) {

return arr.toString().split(‘,’).map(item => {

return Number(item) // split分隔后数组元素为字符串形式, 需要转换为数值形式

})

}

// join & split

function flattern3(arr) {

return arr.join(‘,’).split(‘,’).map(item => { // join方法和toString方法效果一样?

return parseInt(item)

})

}

// 扩展运算符

function flattern5(arr) {

while (arr.some(item => Array.isArray(item))) { // 如果数组元素中有数组

arr = [].concat(…arr) // [].concat(…[1, 2, 3, [4, 5]]) 扩展运算符可以将二维数组变为一维的

}

return arr

}

8. 数组去重


// 传统方式

function unique(arr) {

const res = []

arr.forEach(item => {

if (res.indexOf(item) < 0) { // 没有当前元素

res.push(item)

}

})

return res

}

// 使用 Set (无序,不能重复)

function unique(arr) {

const set = new Set(arr)

return […set] // 解构

}

const res = unique([30, 10, 20, 30, 40, 10])

console.log(res)

9. 函数柯里化


在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。函数柯里化的主要作用和特点就是参数复用、提前返回和延迟执行。

function curry(fn, args) {

var length = fn.length;

var args = args || [];

return function(){

newArgs = args.concat(Array.prototype.slice.call(arguments));

if (newArgs.length < length) {

return curry.call(this,fn,newArgs);

}else{

return fn.apply(this,newArgs);

}

}

}

function multiFn(a, b, c) {

return a * b * c;

}

var multi = curry(multiFn);

multi(2)(3)(4);

multi(2,3,4);

multi(2)(3,4);

multi(2,3)(4);

10. 实现jQuery


class jQuery {

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值