前端八股文之手写系列
面试中常常会遇到同样的问题,大多数面经都将其整理成“面试八股文”以供背诵,尤其是技术乱七八糟一大堆的前端,八股文现象尤为显著
所以面试中击败你的,不光是 985 博士和海龟等精英,还有很多“八股文选手”
本着打不过就加入的原则,本篇文章会持续更新前端八股文系列
# 快排
这里给出分治+递归的写法 代码比较短,方便记忆
const sort = array => {
if (array.length < 2) {
return array
}
const mid = array.splice(Math.floor(array.length / 2), 1)[0]
const left = []
const right = []
array.forEach(item => {
if (item < mid) {
left.push(item)
} else {
right.push(item)
}
})
return [...sort(left), mid, ...sort(right)]
}
# 数组扁平化
建议背前 3 个,后面的可以理解下思路(如果面试官问到别的方法的话)
递归(推荐) 元素可以是任意类型
const flatArray = array => {
const result = []
array.forEach(item => {
if (Array.isArray(item)) {
result.push(...flatArray(item))
} else {
result.push(item)
}
})
return result
}
循环+concat(推荐) 元素可以是任意类型
const flatArray = array => {
while (array.some(item => Array.isArray(item))) {
array = [].concat(...array)
}
return array
}
reduce(推荐) 元素可以是任意类型
const flatArray = array => {
return []
.concat(array)
.reduce(
(item, next) =>
item.concat(Array.isArray(array) ? flatArray(next) : next),
[]
)
}
toString+split(不推荐)
代码短但是数组元素只能是number
或者同一种类型的基本类型
const flatArray = array => {
return array
.toString()
.split(',')
.map(item => Number(item))
}
join+split(不推荐) 优缺点同上,join 也可以转字符串
const flatArray = array => {
return array
.join(',')
.split(',')
.map(item => Number(item))
}
JSON+正则(不推荐) 优缺点同上,正则不太好记忆
const flatArray = array => {
return JSON.stringify(array)
.replace(/(\[|\])/g, '')
.split(',')
.map(item => Number(item))
}
ES6 自带的 flat(不推荐) 千万别写(就是考你 flat 实现的,你直接调了现成的。。。) 但是要了解一下 ES6 这个新引入的函数
const flatArray = array => {
while (array.some(item => Array.isArray(item))) {
array = array.flat()
}
return array
}
# call、apply、bind
# call
- 判断
null
和undefined
时指向全局对象 - 对基本类型装箱:
new Object(...)
Function.prototype._call = function(context) {
if (context === undefined || context === null) {
context = globalThis
}
context = new Object(context)
context.fn = this
const args = []
Array.prototype.forEach.call(
Array.prototype.slice.call(arguments, 1),
item => args.push(item)
)
const result = context.fn(...args)
delete context.fn
return result
}
# apply
类似 call 的实现,传参方式不同
Function.prototype._apply = function(context, args) {
if (context === undefined || context === null) {
context = globalThis
}
context = new Object(context)
context.fn = this
const result = context.fn(...args)
delete context.fn
return context
}
# bind
简易版 存在问题:返回的函数不能构造器调用(new xxx())
Function.prototype._bind = function(...args) {
const fn = this
const that = args[0]
const bindArgs = args.slice(1)
return (...args) => {
fn.apply(that, [...bindArgs, ...args])
}
}
类似 MDN 的实现
Function.prototype._bind = function(context) {
if (typeof this !== 'function') {
throw new Error(
'Function.prototype.bind - what is trying to be bound is not callable'
)
}
const fn = this
const args = Array.prototype.slice.call(arguments, 1)
const noop = function() {}
const result = function() {
const bindArgs = Array.prototype.slice.call(arguments)
return fn.apply(this instanceof noop ? this : context, args.bindArgs)
}
noop.prototype = fn.prototype
result.prototype = new noop()
return result
}
# 数组去重
有很多种思路,这里只列出几种代码较少的
利用 Set 利用 Set 的不可重复特性(ES6)
const unique = array => [...new Set(array)]
利用 Map 将元素设置为 Map 的键再检索(ES6)
const unique = array => {
const map = new Map()
return array.filter(item => !map.has(item) && map.set(item, 1))
}
reduce
const unique = array =>
array.reduce((item, next) =>
item.includes(next) ? item : [...item, next], [])
}
ES5 对象
这个方法会将'1'
和1
看做相同
const unique = array => {
const map = {}
return array.filter(item => !map.hasOwnProperty(item) && (map[item] = 1))
}
# 防抖、节流
# 防抖
const debounce = (fn, time) => {
let timer = null
return function() {
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, arguments)
}, time)
}
}
# 节流
const throttle = (fn, time) => {
let flag = true
return function() {
if (!flag) {
return
}
flag = false
setTimeout(() => {
fn.apply(this, arguments)
flag = true
}, time)
}
}
# 函数柯里化
实现 add 函数,使得add(1,2,3)
、add(1)(2)(3)
和add(1,2)(3)
都得到正确结果
function add() {
const args = [...arguments]
function fn() {
args.push(...arguments)
return fn
}
fn.toString = function() {
return args.reduce((item, next) => item + next)
}
return fn
}
# 类数组转数组
类数组是指具有 length 属性,但不具有数组方法,索引都为非负整数的对象
Array.from
const trans = Array.from
Array.slice
const transform = likeArray => Array.prototype.slice.call(likeArray)
concat
const transform = likeArray => Array.prototype.concat.apply([], likeArray)
# 深/浅拷贝
# 深拷贝
利用 JSON
对函数、undefined
、Symbol
元素无效
const deepCopy = obj => JSON.parse(JSON.stringify(obj))
递归
const deepCopy = obj => {
let objNew = Array.isArray(obj) ? [] : {}
if (obj && typeof obj === 'object') {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === 'object') {
objNew[key] = deepCopy(obj[key])
} else {
objNew[key] = obj[key]
}
}
}
}
return objNew
}
# 浅拷贝
循环判断
const shallowCopy = obj => {
let newObj = obj instanceof Array ? [] : {}
for (key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = obj[key]
}
}
return newObj
}
Object.assign
const shallowCopy = obj => Object.assign({}, obj)
# 链式调用
class Person {
constructor() {
this._name = ''
this._age = 0
}
name(name) {
this._name = name
return this
}
age(age) {
this._age = age
return this
}
}
// 使用
let person = new Person().name('王昭君').age(21)