1. 防抖
function debounce(func, ms = 1000) {let timer;return function (...args) {if (timer) {clearTimeout(timer)}timer = setTimeout(() => {func.apply(this, args)}, ms)}
}// 测试
const task = () => { console.log('run task') }
const debounceTask = debounce(task, 1000)
window.addEventListener('scroll', debounceTask)
  1. 节流
function throttle(func, ms = 1000) {let canRun = truereturn function (...args) {if (!canRun) returncanRun = falsesetTimeout(() => {func.apply(this, args)canRun = true}, ms)}
}// 测试
const task = () => { console.log('run task') }
const throttleTask = throttle(task, 1000)
window.addEventListener('scroll', throttleTask)
  1. 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)
  1. bind
Function.prototype.myBind = function (context = globalThis) {const fn = thisconst 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 {// 通过普通函数形式调用,绑定 contextfn.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.myBind(me)
meSay()
const otherSay = say.myBind(other)
otherSay()
  1. call
Function.prototype.myCall = function (context = globalThis) {// 关键步骤,在 context 上调用方法,触发 this 绑定为 context,使用 Symbol 防止原有属性的覆盖const key = Symbol('key')context[key] = this// es5 可通过 for 遍历 arguments 得到参数数组const args = [...arguments].slice(1)const res = context[key](...args)delete context[key]return res
};// 测试
const me = { name: 'Jack' }
function say() {console.log(`My name is ${this.name || 'default'}`);
}
say.myCall(me)
  1. apply
Function.prototype.myApply = function (context = globalThis) {// 关键步骤,在 context 上调用方法,触发 this 绑定为 context,使用 Symbol 防止原有属性的覆盖const key = Symbol('key')context[key] = thislet resif (arguments[1]) {res = context[key](...arguments[1])} else {res = context[key]()}delete context[key]return res
}// 测试
const me = { name: 'Jack' }
function say() {console.log(`My name is ${this.name || 'default'}`);
}
say.myApply(me)
  1. 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 () {return 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]); // false
console.log(newObj.meta.birth === source.meta.birth); // false
  1. 事件总线 | 发布订阅模式
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)
  1. 柯里化:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数
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))
  1. 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 = ChildChild.prototype.sayAge = function () {console.log(this.age)
}// 测试
const child = new Child(18, 'Jack')
child.sayName()
child.sayAge()
  1. instanceof
function isInstanceOf(instance, klass) {let proto = instance.__proto__let prototype = klass.prototypewhile (true) {if (proto === null) return falseif (proto === prototype) return trueproto = proto.__proto__}
}// 测试
class Parent {}
class Child extends Parent {}
const child = new Child()
console.log(isInstanceOf(child, Parent), isInstanceOf(child, Child), isInstanceOf(child, Array));
  1. 异步并发数限制
/*** 关键点* 1. new promise 一经创建,立即执行* 2. 使用 Promise.resolve().then 可以把任务加到微任务队列,防止立即执行迭代方法* 3. 微任务处理过程中,产生的新的微任务,会在同一事件循环内,追加到微任务队列里* 4. 使用 race 在某个任务完成时,继续添加任务,保持任务按照最大并发数进行执行* 5. 任务完成后,需要从 doingTasks 中移出*/
function limit(count, array, iterateFunc) {const tasks = []const doingTasks = []let i = 0const 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)
})
  1. 异步串行 | 异步并行
// 字节面试题,实现一个异步加法
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');
})()
  1. vue reactive
// Dep module
class Dep {static stack = []static target = nulldeps = nullconstructor() {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)}this.target = t}static popTarget() {this.target = this.stack.pop()}
}// reactive
function reactive(o) {if (o && typeof o === 'object') {Object.keys(o).forEach(k => {defineReactive(o, k, o[k])})}return o
}function defineReactive(obj, k, val) {let dep = new Dep()Object.defineProperty(obj, k, {get() {dep.depend()return val},set(newVal) {val = newValdep.notify()}})if (val && typeof val === 'object') {reactive(val)}
}// watcher
class Watcher {constructor(effect) {this.effect = effectthis.update()}update() {Dep.pushTarget(this)this.value = this.effect()Dep.popTarget()return this.value}
}// 测试代码
const data = reactive({msg: 'aaa'
})new Watcher(() => {console.log('===> effect', data.msg);
})setTimeout(() => {data.msg = 'hello'
}, 1000)
  1. promise
// 建议阅读 [Promises/A+ 标准](https://promisesaplus.com/)
class MyPromise {constructor(func) {this.status = 'pending'this.value = nullthis.resolvedTasks = []this.rejectedTasks = []this._resolve = this._resolve.bind(this)this._reject = this._reject.bind(this)try {func(this._resolve, this._reject)} catch (error) {this._reject(error)}}_resolve(value) {setTimeout(() => {this.status = 'fulfilled'this.value = valuethis.resolvedTasks.forEach(t => t(value))})}_reject(reason) {setTimeout(() => {this.status = 'reject'this.value = reasonthis.rejectedTasks.forEach(t => t(reason))})}then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) => {this.resolvedTasks.push((value) => {try {const res = onFulfilled(value)if (res instanceof MyPromise) {res.then(resolve, reject)} else {resolve(res)}} catch (error) {reject(error)}})this.rejectedTasks.push((value) => {try {const res = onRejected(value)if (res instanceof MyPromise) {res.then(resolve, reject)} else {reject(res)}} catch (error) {reject(error)}})})}catch(onRejected) {return this.then(null, onRejected);}
}// 测试
new MyPromise((resolve) => {setTimeout(() => {resolve(1);}, 500);
}).then((res) => {console.log(res);return new MyPromise((resolve) => {setTimeout(() => {resolve(2);}, 500);});}).then((res) => {console.log(res);throw new Error('a error')}).catch((err) => {console.log('==>', err);})
  1. 数组扁平化
// 方案 1
function recursionFlat(ary = []) {const res = []ary.forEach(item => {if (Array.isArray(item)) {res.push(...recursionFlat(item))} else {res.push(item)}})return res
}
// 方案 2
function reduceFlat(ary = []) {return ary.reduce((res, item) => res.concat(Array.isArray(item) ? reduceFlat(item) : item), [])
}// 测试
const source = [1, 2, [3, 4, [5, 6]], '7']
console.log(recursionFlat(source))
console.log(reduceFlat(source))
  1. 对象扁平化
function objectFlat(obj = {}) {const res = {}function flat(item, preKey = '') {Object.entries(item).forEach(([key, val]) => {const newKey = preKey ? `${preKey}.${key}` : keyif (val && typeof val === 'object') {flat(val, newKey)} else {res[newKey] = val}})}flat(obj)return res
}// 测试
const source = { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } }
console.log(objectFlat(source));
  1. 图片懒加载
// <img src="default.png" data-src="https://xxxx/real.png">
function isVisible(el) {const position = el.getBoundingClientRect()const windowHeight = document.documentElement.clientHeight// 顶部边缘可见const topVisible = position.top > 0 && position.top < windowHeight;// 底部边缘可见const bottomVisible = position.bottom < windowHeight && position.bottom > 0;return topVisible || bottomVisible;
}function imageLazyLoad() {const images = document.querySelectorAll('img')for (let img of images) {const realSrc = img.dataset.srcif (!realSrc) continueif (isVisible(img)) {img.src = realSrcimg.dataset.src = ''}}
}// 测试
window.addEventListener('load', imageLazyLoad)
window.addEventListener('scroll', imageLazyLoad)
// or
window.addEventListener('scroll', throttle(imageLazyLoad, 1000))

前端面试常考的手写代码不是背出来的!相关推荐

  1. web前端面试常考问题——持续更新中(5.20)

    1.介绍一下你的技术栈             HTML5 + CSS3 + ES6 + Jquery + React + Webpack + git + npm .... 2.简单自我介绍 3.电商 ...

  2. JavaScript前端面试常考算法模板

    一.二分查找 使用条件 排序数组 (30-40%是二分) 当面试官要求你找一个比 O(n) 更小的时间复杂度算法的时候(99%) 找到数组中的一个分割位置,使得左半部分满足某个条件,右半部分不满足(1 ...

  3. html盒子模型子元素怎么水平占满父元素_前端面试常考问题之css盒模型

    一.题目:谈一谈你对CSS盒模型的认识 专业的面试,一定会问 CSS 盒模型.对于这个题目,我们要回答一下几个方面: (1)基本概念:content.padding.margin. (2)标准盒模型. ...

  4. 前端面试常考知识点---js

    1.同步和异步 详细- 文章总结: setTimeout(fn,ms)这个函数,是经过指定时间后,把要执行的任务加入到Event Queue中,又因为是单线程任务要一个一个执行,如果前面的任务需要的时 ...

  5. 2020年前端面试之JS手写代码题合集

    2020年前端面试之JS手写代码题合集 预计会有上千道题,后续慢慢补! 1.  写一个把字符串大小写切换的方法 function caseConvert(str){return str.replace ...

  6. 前端面试高频手写代码题

    前端面试高频手写代码题 一.实现一个解析URL参数的方法 方法一:String和Array的相关API 方法二: Web API 提供的 URL 方法三:正则表达式+string.replace方法 ...

  7. 前端面试:手写代码JS实现字符串反转

    前端萌新面试:手写代码JS实现字符串反转 前言 因为做前年小红书的前端校招面试题,发现出现好几道关于字符串对象和数组对象的题目,说难不难,但突然要写的话一时想不起来,这不想着做个小总结. 首先明白字符 ...

  8. 2021-最新Web前端经典面试试题及答案-史上最全前端面试题(含答案)---手写代码篇

    ★★★ 手写代码:实现forEach map filter reduce ★★★ 手写实现一个简易的 Vue Reactive ★★★ 手写代码,监测数组变化,并返回数组长度 ★★★ 手写原生继承,并 ...

  9. 前端面试之手写代码篇

    手写代码 1.手写instanceof方法 2.手写new操作符 3.手写Promise.all() 4.手写防抖函数 5.手写节流函数 6.手写call.apply函数 7.手写bind函数 8.封 ...

最新文章

  1. 【Codeforces】716B Complete the Word (26个字母)
  2. LaTeX 修改参考文献的方法
  3. NHibernate Step by Step:序篇 (转)
  4. 深入分析 Redis Lua 脚本运行原理
  5. javascript--拖动图片时取消浏览器默认提示
  6. [转载]项目风险管理七种武器-霸王枪
  7. HTML如何添加锚点,我先收藏为敬
  8. Windows系统使用minGW+msys 编译ffmpeg 0.5的全过程详述
  9. Per-class allocator 2
  10. 查看User Profile的名称和显示名称
  11. 基于51单片机编写的六位电子密码锁由LCD1602显示
  12. 预测控制matlab程序,预测控制matlab程序
  13. JavaScript 小白手册
  14. 以后在校期间的规划计算机专业的,计算机专业职业生涯规划方案书样本.doc
  15. 常见的数据结构与算法
  16. Excel函数返回定期付息证券的应计利息
  17. buu crypto 变异凯撒
  18. 冬季送暖!实用围巾打法!(图)
  19. 三季度高歌猛进,广告主为何向微博平台迁移?
  20. muParser公式库使用简介( 转)

热门文章

  1. Java基础学习总结(119)——Java8 JVM与Java7 JVM比较
  2. Android学习总结(3)——Handler深入详解
  3. Maven学习总结(20)——Maven pom.xml配置再体会
  4. mysql 列很多_MySQL:多个表还是一个表有很多列?
  5. 企业分布式微服务云SpringCloud SpringBoot mybatis (二)服务消费者(rest+ribbon)
  6. 打车应用上马快递业务靠谱吗?
  7. MyEclipse 2015 CI
  8. 解读系统资质审批的相关政策
  9. jdk1.5、1.6、1.7新特性详细介绍(整理)
  10. 谈现代企业中(一)菜鸟和大牛