手写面试题
敲一些基础的f**k手写题
<DataLog>{result}</DataLog>
包裹的数据,以及console的数据均可以在浏览器控制台看到输出的日志信息
数组扁平化(flat)
[1,2,[3,[4,5],6]] -> [1,2,3,4,5,6]
递归实现
实时编辑器
function (props) {const arr = [1,2,[3,[4,5],6]]const flatten = (arr) => {const res = []arr.forEach(val => {if (Array.isArray(val)) {res.push(...flatten(val))} else {res.push(val)}})return res}const result = flatten(arr)return <DataLog>{result}</DataLog>}
结果
Loading...
reduce实现
实时编辑器
function (props) {const arr = [1,2,[3,[4,5],6]]const flatten = (arr) => {return arr.reduce((res, target) => {return res.concat(Array.isArray(target) ? flatten(target) : target)}, [])}const result = flatten(arr)return <DataLog>{result}</DataLog>}
结果
Loading...
reduce不传初始值:回调函数的第一个参数为数组的第一项,首轮遍历target为第二个参数
es6原生flat
arr.flat(depth)
其中 depth 是 flat 的参数,depth 是可以传递数组的展开深度(默认不填、数值是 1),即展开一层数组。如果层数不确定,参数可以传进 Infinity,代表不论多少层都要展开
实时编辑器
function (props) {const arr = [1,2,[3,[4,5],6]]const result = arr.flat(Infinity) // 1,2...return <DataLog>{result}</DataLog>}
结果
Loading...
实现一个new
- 创建一个空对象,使空对象的原型链指向构造函数的原型。
bar obj2
- 然后执行构造函数内的方法,内部的属性如
foo obj
让this指向新的空对象 - 将运行结果返回回去( 因为构造函数可能直接有return引用类型的数据
[] {}
所以要判断一下返回值)
实时编辑器
function (props) {function newNew(Con, ...arg) {var _o = Object.create(null); //1 创建一个没有原型的对象_o.__proto__ = Con.prototype; //1var res = Con.apply(_o, arg); //2return typeof res === "object" ? res : _o; //3 因为 构造函数可能直接有return引用类型的数据 [] {} 所以要判断一下返回值}function A(baz, baz2) {this.foo = "foo";this.obj = {a: 1};this.baz = baz || "def";this.baz2 = baz2 || "def";}A.prototype.bar = "bar";A.prototype.obj2 = {b: 2};function B(baz, baz2) {this.foo = "foo";this.obj = {a: 1};this.baz = baz || "def";this.baz2 = baz2 || "def";return { haha: "我是真实的返回" };}B.prototype.bar = "bar";B.prototype.obj2 = {b: 2};console.log(newNew(A, 1, 2));console.log(newNew(B, 1, 2));return (<>new</>)}
结果
Loading...
实现object.create
实时编辑器
function () {// 将传入的对象作为原型Object.myCreate = function (obj) {function F() {}F.prototype = obj // 通过create创建的对象,属性需要从原型链获取(所以字面量创建简单快速读取性能更好)const resObj = new F()if (obj === null) {// 创建一个没有原型对象的对象,Object.create(null)resObj .__proto__ = null}return resObj}return <button onClick={() => {console.log(Object.myCreate({a:1}), Object.create({b:1}))console.log(Object.myCreate(null), Object.create(null))}}>obj.create</button>}
结果
Loading...
实现call
- 判断传入上下文对象是否存在,如果不存在,则设置为 window
- 处理传入的参数,截取第一个参数后的所有参数
- 将函数作为上下文对象的一个属性
- 使用上下文对象来调用这个方法,并保存返回结果
- 删除刚才新增的属性
- 返回结果
实时编辑器
function (props) {var o = {a: 123};var a = 1;Function.prototype.mcall = function(context = window, ...arg) {// var fn = this;context.fn = this;const result = context.fn(...arg);delete context.fn;return result;};function foo (b = 2) {console.log(this.a);console.log(b);}foo()foo.mcall(o, 111111)return <DataLog>call</DataLog>}
结果
Loading...
实现apply
- 判断传入上下文对象是否存在,如果不存在,则设置为 window
- 处理传入的参数,截取第一个参数后的所有参数(与call的差异)
- 将函数作为上下文对象的一个属性
- 使用上下文对象来调用这个方法,并保存返回结果
- 删除刚才新增的属性
- 返回结果
实时编辑器
function (props) {var o = {a: 123};var a = 1;Function.prototype.mapply = function(context = window, arg) {// var fn = this;context.fn = this;const result = context.fn(...arg);delete context.fn;return result;};function foo (b = 2, c=3) {console.log(this.a);console.log(b, c);}foo()foo.mapply(o, [1,2])return <DataLog>apply</DataLog>}
结果
Loading...
实现bind
fn.bind(obj, ...args1)(...args2)
与call不同,bind返回一个新的函数,执行该函数会调用源函数,并且绑定和调用时都可以接收参数
- 在bind调用时,保存当前函数的引用,获取其余传入参数值,返回一个新的函数
- 调用新的函数可以接收新的参数,与bind调用时的参数结合
- 使用上下文对象来调用这个方法,并保存返回结果
- 删除刚才新增的属性
- 返回结果
实时编辑器
function (props) {var o = {a: 123};var a = 1;Function.prototype.mbind = function(context = window, ...arg1) {context.fn = this;return function(...arg2) {const result = context.fn(...arg1, ...arg2);delete context.fn;return result;};};function foo(b = 2, c = 3) {console.log(this.a);console.log(b, c);}foo();foo.mbind(o, 1)(7);return <DataLog>bind</DataLog>}
结果
Loading...
防抖
- 接收执行函数及延时时间,并返回新的函数
- 新的函数接收原执行函数所需要调用的参数
实时编辑器
function (props) {function foo (str) {console.log('执行', str, +new Date())}function debunce (fn, delay = 300) {let timerreturn function (...args) {const context = this// 如果此时存在定时器则取消之前的定时器重新记时clearTimeout(timer)timer = setTimeout(function () {fn.apply(context, args)}, delay)}}return <button onClick={() => debunce(foo, 100)('防抖')}>点我防抖</button>}
结果
Loading...
节流
- 接收执行函数及执行间隔时间,并返回新的函数
- 新的函数接收原执行函数所需要调用的参数
实时编辑器
function (props) {function foo (str) {console.log('执行', str, +new Date())}function throttle (fn, delay = 300) {let startTime = +new Date()return function (...args) {const context = thisconst endTime = Date.now()// console.log(endTime - startTime)if (endTime - startTime > delay) {startTime = endTimereturn fn.apply(context, args);}}}return <button onClick={throttle(() => foo('节流'), 100)}>点我防抖</button>}
结果
Loading...
手写promise
- promise 内部保存一个状态 默认为等待状态
- 执行promise的函数体,需要更改状态执行 resolve 更改promise内的状态
- then方法执行回调,两个参数,成功和失败cb,promise内部判断状态不同的回调参数
这样实现有个问题 先执行then方法是无法执行内部更改状态的方法的。顺序应该是:
- 如果执行then时状态还是 pending 先订阅它,将其添加到内部的发布订阅任务队列中
- 状态更改后 遍历执行订阅队列的方法 (发布订阅的拆解)
实时编辑器
function (props) {class P {constructor (runFn) { // new Promise()时执行this.value // 内部定义结束状态的值this.status = 'pending'this.onResolvedCallbacks = [] // 内部维护两个任务队列,当内部resolve或reject时执行队列中的方法this.onRejectedCallbacks = []// 在promise传入的函数回调中会调用: resolve(val) 更改状态,内部定义一个resolve来接收参数并更改内部状态const resolve = res => {// 执行函数体更改状态的实参 --- 更改内部的状态 接收执行更改状态传入的实参if (this.status === 'pending') {// 状态只能更改一次 默认状态才能更改this.status = 'fulfilled'this.value = resthis.onResolvedCallbacks.forEach(cb => cb())}}const reject = rej => { // reject同理if (this.status === 'pending') {this.status = 'rejected'this.value = rejthis.onRejectedCallbacks.forEach(cb => cb())}}// 执行传入的方法,并将定义的resolve和reject作为参数传入 (new Promise((resolve, reject) => {})runFn(resolve, reject)}// .then(res => {}, rej => {}) 可以传入两个回调函数,接收内部 resolve(val) 传入的valthen (resolveCb, rejectCb) {switch (this.status) { //case 'fulfilled':resolveCb(this.value) // 将执行函数体 resolve更改内部状态值时的形参作为实参传入回调break;case 'rejected':rejectCb(this.value)break;default: // 当前还没处于终止状态时将回调传入到“发布订阅”的任务队列中,等待状态变更时执行this.onResolvedCallbacks.push(() => {resolveCb(this.value)});this.onRejectedCallbacks.push(() => {rejectCb(this.value)});break;}}}function foo () {new Promise((resolve) => {console.time('promise')setTimeout(() => {resolve(1)}, 1000)}).then(res => {console.timeEnd('promise')})new P((resolve, reject) => {console.time('mypromise')setTimeout(() => {resolve(1234)}, 2000)}).then(res => {console.log(res)console.timeEnd('mypromise')},err => {console.log(err)})}return <button onClick={foo}>promiseTest(console.log)</button>}
结果
Loading...
手写柯里化
实时编辑器
function () {function curry (func) {const argLength = func.lengthreturn function curried (...args) {if (args.length >= argLength) { // 长度符合执行条件立即执行return func.apply(this, args)} else { // 不符合条件返回新的偏函数,并将此次的参数与即将传入的参数进行合并return function(...args2) {return curried.apply(this, args.concat(args2))}}}}function sum (a, b, c) {console.log(a+b+c)return a+b+c}const newSum = curry(sum)return <button onClick={() => { newSum(1)(2)(3) }}>柯里化</button>}
结果
Loading...
instanceof判断
instanceof
可以正确判断对象的类型,其内部运行机制是判断在其原型链中能否找到该类型的原型
- 首先获取类型的原型
- 然后获得对象的原型
- 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为
null
,因为原型链最终为null
实时编辑器
function () {function myInstanceof (value, Objconstructor) {let leftProto = Object.getPrototypeOf(value)const prototype = Objconstructor.prototype // 获取构造函数的 prototype 对象while (true) {if (!leftProto) return falseif (leftProto === prototype) return trueleftProto = Object.getPrototypeOf(leftProto) // 不符合以上条件继续往上找原型}}return <button onClick={() => { console.log(myInstanceof([], Array)) }}>myInstanceof</button>}
结果
Loading...
数组去重
set
实时编辑器
function () {const arr = [1,2,3,4,5,3,1,1,3,54,9]function uniqueArray (arr) {return [...new Set(arr)]}return <button onClick={() => { console.log(uniqueArray(arr)) }}>去重</button>}
结果
Loading...
map
实时编辑器
function () {function uniqueArray (arr) {const map = new Map()const res = [] // 数组用于返回结果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值res.push(arr[i])}}return res}const arr = [1,2,3,4,5,3,1,1,3,54,9]return <button onClick={() => { console.log(uniqueArray(arr)) }}>map去重</button>}
结果
Loading...
filter
实时编辑器
function () {function uniqueArray (arr) {return arr.filter((val, idx) => {return arr.indexOf(val) === idx // 当前是否首次出现})}const arr = [1,2,3,4,5,3,1,1,3,54,9]return <button onClick={() => { console.log(uniqueArray(arr)) }}>filter去重</button>}
结果
Loading...
实现promise.all
Promise.all([promises]) promise.all 接收一个promise任务队列,队列中所有异步任务均执行完毕后会返回结果数组
实时编辑器
function () {Promise.myAll = (promises) => {return new Promise((resolveAll, rejectAll) => { // 返回一个新的promise,用于包装promises中的结果let count = 0 // 计数 数值等于promises数量表明已结束const pLen = promises.lengthconst result = []promises.forEach((p, i) => {// 注意有的数组项有可能不是Promise,需要手动转化一下Promise.resolve(p).then((res) => {count++// 收集每个Promise的返回值result[i] = res// 当所有的Promise都成功了,那么将返回的Promise结果设置为resultif (count === pLen) {resolveAll(result)}// 监听数组项中的Promise catch只要有一个失败,那么我们自己返回的Promise也会失败}).catch(rejectAll)})})}const arr = [1,2,3,4,5,3,1,1,3,54,9]return <button onClick={() => {console.time('pAll')Promise.myAll([Promise.resolve(1),new Promise((resolve) => {setTimeout(() => resolve(2), 1000)})]).then(res => {console.log(res)console.timeEnd('pAll')})}}>promiseAll</button>}
结果
Loading...
实现promise.race
实时编辑器
function () {Promise.myrace = (promises) => {return new Promise((resolveRace, rejectRace) => { // 返回一个新的promise,用于包装promises中的结果promises.forEach((p, i) => {// promise状态只能被改变一次,所以直接遍历调用就可以“谁先抢到算谁的”Promise.resolve(p).then(resolveRace).catch(rejectRace)})})}const arr = [1,2,3,4,5,3,1,1,3,54,9]return <button onClick={() => {console.time('pRace')Promise.myrace([new Promise((resolve) => {setTimeout(() => resolve(1), 2000)}),new Promise((resolve) => {setTimeout(() => resolve(2), 1000)})]).then(res => {console.log(res)console.timeEnd('pRace')})}}>promiseRace</button>}
结果
Loading...
实现promise.resolve
实时编辑器
function () {Promise.myResolve = (val) => {// 如果传入的本身就是promise直接返回即可if (val && typeof val === 'object' && (val instanceof Promise)) {return val}return new Promise(resolve => { // 返回一个新的promise,用于包装resolve(val)})}return <button onClick={() => {console.time('pRace')Promise.myResolve(// new Promise((resolve) => {// setTimeout(() => resolve(1), 1000)// })111).then(res => {console.log(res)console.timeEnd('pRace')})}}>promiseResolve</button>}
结果
Loading...
数字累加
实时编辑器
function () {function foo (val) {if (val === 1) return 1return foo(val - 1) + val}function bar (a, b, res = 0) {res = a+resif (a === b){return res} else {return bar(a+1, b, res)}}return <button onClick={() => {console.log(foo(100))console.log(bar(1, 100))}}>数字累加1+...+100</button>}
结果
Loading...
数组求和
实时编辑器
function () {function foo (val) {const arr = [1,2,3,4,5,6]const sum = arr.reduce((r, t) => t+r) // reduce不传初始值默认为数组第一个参数,首轮遍历target为第二个参数console.log(sum)}return <button onClick={() => {foo()}}>数组求和</button>}
结果
Loading...
小孩报数问题
有30个小孩儿,编号从1-30,围成一圈依此报数,1、2、3 数到 3 的小孩儿退出这个圈, 然后下一个小孩 重新报数 1、2、3,问最后剩下的那个小孩儿的编号是多少?
实时编辑器
function () {function foo () {const genArr = Array(30).fill(null).map((_v, idx) => idx+1)let currentIdx = 0let counter = 1 // 报数while (genArr.length > 1) {if (counter === 3) {genArr.splice(currentIdx, 1) //如果报3移出队列,因前一项已经移除,故遍历的下标不需自增counter = 1} else {counter++currentIdx++}if (currentIdx === genArr.length) {currentIdx = 0}console.log(genArr)}}return <button onClick={() => {foo()}}>报数</button>}
结果
Loading...
排序手写
实时编辑器
function () {const arr = [4,2,1,3,5,6,2,4,7,8,9]// 冒泡function foo (arr) {arr = arr.slice(0)const len = arr.lengthfor (let i = 0; i < len; i++) {for (let j = 0; j < len - 1 - i; j++) {if (arr[j] > arr[j+1]) { //相邻元素两两对比[arr[j + 1], arr[j]] = [arr[j], arr[j + 1]] // 元素交换//const temp = arr[j+1]//arr[j + 1] = arr[j]//arr[j] = temp}}}return arr}// 快速排序function bar (arr) {arr = arr.slice(0)if (arr.length < 1) {return arr;}const prvotIdx = ~~(arr.length / 2)const pivot = arr.splice(prvotIdx, 1)[0] // 取出中间值const leftArr = []const rightArr = []for (let i = 0; i < arr.length; i++) {if (arr[i] < pivot) {leftArr.push(arr[i])} else {rightArr.push(arr[i])}}return bar(leftArr).concat(pivot, bar(rightArr))}return <button onClick={() => {console.time('bubbleSort')console.log(foo(arr))console.timeEnd('bubbleSort')console.time('quickSort')console.log(bar(arr))console.timeEnd('quickSort')}}>排序</button>}
结果
Loading...