前端面试出场率奇高的18个手写代码

return function (…args) {

if (!canRun) return

canRun = false

setTimeout(() => {

func.apply(this, args)

canRun = true

}, ms)

}

}

// 测试 const task = () => { console.log(‘run task’) }

const throttleTask = throttle(task, 1000)

window.addEventListener(‘scroll’, throttleTask)

3. new

function myNew(Func, …args) {

const instance = {};

if (Func.prototype) {

Object.setPrototypeOf(instance, Func.prototype)

}

const res = Func.apply(instance, args)

if (typeof res === “function” || (typeof res === “object” && res !== null)) {

return res

}

return instance

}

// 测试 function Person(name) {

this.name = name

}

Person.prototype.sayName = function() {

console.log(My name is ${this.name})

}

const me = myNew(Person, ‘Jack’)

me.sayName()

console.log(me)

4. bind

Function.prototype.myBind = function (context = globalThis) {

const fn = this

const args = Array.from(arguments).slice(1)

const newFunc = function () {

const newArgs = args.concat(…arguments)

if (this instanceof newFunc) {

// 通过 new 调用,绑定 this 为实例对象 fn.apply(this, newArgs)

} else {

// 通过普通函数形式调用,绑定 context fn.apply(context, newArgs)

}

}

// 支持 new 调用方式 newFunc.prototype = Object.create(fn.prototype)

return newFunc

}

// 测试 const me = { name: ‘Jack’ }

const other = { name: ‘Jackson’ }

function say() {

console.log(My name is ${this.name || 'default'});

}

const meSay = say.bind(me)

meSay()

const otherSay = say.bind(other)

otherSay()

5. call


Function.prototype.myCall = function (context = globalThis) {

// 关键步骤,在 context 上调用方法,触发 this 绑定为 context,使用 Symbol 防止原有属性的覆盖 const key = Symbol(‘key’)

context[key] = this

let args = [].slice.call(arguments, 1)

let res = contextkey

delete context[key]

return res

};

// 测试 const me = { name: ‘Jack’ }

function say() {

console.log(My name is ${this.name || 'default'});

}

say.myCall(me)

6. apply

Function.prototype.myApply = function (context = globalThis) {

// 关键步骤,在 context 上调用方法,触发 this 绑定为 context,使用 Symbol 防止原有属性的覆盖 const key = Symbol(‘key’)

context[key] = this

let res

if (arguments[1]) {

res = contextkey

} else {

res = contextkey

}

delete context[key]

return res

}

// 测试 const me = { name: ‘Jack’ }

function say() {

console.log(My name is ${this.name || 'default'});

}

say.myApply(me)

7. deepCopy

function deepCopy(obj, cache = new WeakMap()) {

if (!obj instanceof Object) return obj

// 防止循环引用 if (cache.get(obj)) return cache.get(obj)

// 支持函数 if (obj instanceof Function) {

return function () {

obj.apply(this, arguments)

}

}

// 支持日期 if (obj instanceof Date) return new Date(obj)

// 支持正则对象 if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)

// 还可以增加其他对象,比如:Map, Set等,根据情况判断增加即可,面试点到为止就可以了

// 数组是 key 为数字素银的特殊对象 const res = Array.isArray(obj) ? [] : {}

// 缓存 copy 的对象,用于处理循环引用的情况 cache.set(obj, res)

Object.keys(obj).forEach((key) => {

if (obj[key] instanceof Object) {

res[key] = deepCopy(obj[key], cache)

} else {

res[key] = obj[key]

}

});

return res

}

// 测试 const source = {

name: ‘Jack’,

meta: {

age: 12,

birth: new Date(‘1997-10-10’),

ary: [1, 2, { a: 1 }],

say() {

console.log(‘Hello’);

}

}

}

source.source = source

const newObj = deepCopy(source)

console.log(newObj.meta.ary[2] === source.meta.ary[2]);

8. 事件总线 | 发布订阅模式

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) {

const 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) {

if (this.cache[name]) {

// 创建副本,如果回调函数内继续注册相同事件,会造成死循环 const tasks = this.cache[name].slice()

for (let fn of tasks) {

fn();

}

}

}

emit(name, once = false) {

if (this.cache[name]) {

// 创建副本,如果回调函数内继续注册相同事件,会造成死循环 const tasks = this.cache[name].slice()

for (let fn of tasks) {

fn();

}

if (once) {

delete this.cache[name]

}

}

}

}

// 测试 const eventBus = new EventEmitter()

const task1 = () => { console.log(‘task1’); }

const task2 = () => { console.log(‘task2’); }

eventBus.on(‘task’, task1)

eventBus.on(‘task’, task2)

setTimeout(() => {

eventBus.emit(‘task’)

}, 1000)

9. 柯里化:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数

function curry(func) {

return function curried(…args) {

// 关键知识点:function.length 用来获取函数的形参个数 // 补充:arguments.length 获取的是实参个数 if (args.length >= func.length) {

return func.apply(this, args)

}

return function (…args2) {

return curried.apply(this, args.concat(args2))

}

}

}

// 测试 function sum (a, b, c) {

return a + b + c

}

const curriedSum = curry(sum)

console.log(curriedSum(1, 2, 3))

console.log(curriedSum(1)(2,3))

console.log(curriedSum(1)(2)(3))

10. es5 实现继承

function create(proto) {

function F() {}

F.prototype = proto;

return new F();

}

// Parent function Parent(name) {

this.name = name

}

Parent.prototype.sayName = function () {

console.log(this.name)

};

// Child function Child(age, name) {

Parent.call(this, name)

this.age = age

}

Child.prototype = create(Parent.prototype)

Child.prototype.constructor = Child

Child.prototype.sayAge = function () {

console.log(this.age)

}

// 测试 const child = new Child(18, ‘Jack’)

child.sayName()

child.sayAge()

11. instanceof

function isInstanceOf(instance, klass) {

let proto = instance.proto

let prototype = klass.prototype

while (true) {

if (proto === null) return false

if (proto === prototype) return true

proto = proto.proto

}

}

// 测试 class Parent {}

class Child extends Parent {}

const child = new Child()

console.log(isInstanceOf(child, Parent), isInstanceOf(child, Child), isInstanceOf(child, Array))

12. 异步并发数限制

/**

  • 关键点 * 1. new promise 一经创建,立即执行 * 2. 使用 Promise.resolve().then 可以把任务加到微任务队列,防止立即执行迭代方法 * 3. 微任务处理过程中,产生的新的微任务,会在同一事件循环内,追加到微任务队列里 * 4. 使用 race 在某个任务完成时,继续添加任务,保持任务按照最大并发数进行执行 * 5. 任务完成后,需要从 doingTasks 中移出 */

function limit(count, array, iterateFunc) {

const tasks = []

const doingTasks = []

let i = 0

const enqueue = () => {

if (i === array.length) {

return Promise.resolve()

}

const task = Promise.resolve().then(() => iterateFunc(array[i++]))

tasks.push(task)

const doing = task.then(() => doingTasks.splice(doingTasks.indexOf(doing), 1))

doingTasks.push(doing)

const res = doingTasks.length >= count ? Promise.race(doingTasks) : Promise.resolve()

return res.then(enqueue)

};

return enqueue().then(() => Promise.all(tasks))

}

// test const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i))

limit(2, [1000, 1000, 1000, 1000], timeout).then((res) => {

console.log(res)

})

13. 异步串行 | 异步并行

// 字节面试题,实现一个异步加法 function asyncAdd(a, b, callback) {

setTimeout(function () {

callback(null, a + b);

}, 500);

}

// 解决方案 // 1. promisify const promiseAdd = (a, b) => new Promise((resolve, reject) => {

asyncAdd(a, b, (err, res) => {

if (err) {

reject(err)

} else {

resolve(res)

}

})

})

// 2. 串行处理 async function serialSum(…args) {

return args.reduce((task, now) => task.then(res => promiseAdd(res, now)), Promise.resolve(0))

}

// 3. 并行处理 async function parallelSum(…args) {

if (args.length === 1) return args[0]

const tasks = []

for (let i = 0; i < args.length; i += 2) {

tasks.push(promiseAdd(args[i], args[i + 1] || 0))

}

const results = await Promise.all(tasks)

return parallelSum(…results)

}

// 测试 (async () => {

console.log(‘Running…’);

const res1 = await serialSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)

console.log(res1)

const res2 = await parallelSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)

console.log(res2)

console.log(‘Done’);

})()

14. vue reactive

// Dep module class Dep {

static stack = []

static target = null

deps = null

constructor() {

this.deps = new Set()

}

depend() {

if (Dep.target) {

this.deps.add(Dep.target)

}

}

notify() {

this.deps.forEach(w => w.update())

}

static pushTarget(t) {

if (this.target) {

this.stack.push(this.target)

最后

为了帮助大家更好的了解前端,特别整理了《前端工程师面试手册》电子稿文件。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值