最近在准备面试,花了一段时间,广采精华,整理出了以下在前端面试中非常大概率会考到的JS手写面试题。希望对诸君面试准备和巩固知识起到帮助。
如有代码优化的建议,请评论指出,我们共同进步!
话不多说,直接上代码!
1、使用js代码实现一个事件类Event,包含以下功能:绑定事件、解绑事件、派发事件
class Event {
constructor() {
this._cache = []
}
on (type, callback) {
let fns = this._cache[type] || []
if (fns.indexOf(callback) === -1) {
fns.push(callback)
}
return this
}
off (type, callback) {
let fns = this._cache[type]
if (Array.isArray(fns)) {
if (callback) {
let index = fns.indexOf(callback)
if (index !== -1) {
fns.splice(index, 1)
}
} else {
fns.length = 0
}
}
return this
}
trigger (type, data) {
let fns = this._cache[type]
if (Array.isArray(fns)) {
fns.forEach((fn) => {
fn(data)
})
}
return this
}
}
2、实现深拷贝deepClone
const deepClone = (obj) => {
if (typeof obj !== 'object') return
let newObj = obj instanceof Array ? [] : {}
//使用Reflect.ownKeys可以获取Symbol属性
//for(let key of Reflect.ownKeys(obj)) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
}
}
return newObj
}
3、快速排序
const quickSort = (array) => {
let pivot = array[array.length - 1]
let left = arrary.filter((v, i) => v < pivot && i !== array.length - 1)
let right = array.filter(v => v > pivot)
return [...quickSort(left), pivot, ...quickSort(right)]
}
4、冒泡排序
const bubbleSort = (array) => {
let temp
for(let i = 0; i < array.length; i++) {
for(let j = 0;j< array.length - 1 -i; j++) {
if(array[j] > array[j+1]) {
temp = array[j]
result[j] = result[j+1]
result[j+1] = result[j]
}
}
}
return array
}
5、二分查找
(1)一维二分查找
const binarySearch = (target, array) => {
let i = 0
let j = array.length-1
while(i <= j) {
if (target > array[i]) {
i++
}else if (target < array[j]) {
j--
} else {
return true
}
}
return false
}
(2)二维二分查找(长宽一致正方形矩阵)
const binarySearch = (target, array) => {
let i = 0
let j = array[i].length - 1
while (i < array[i].length && j >= 0) {
if ( target > array[i][j]) {
i++
} else if (target < array[i][j]) {
j--
} else {
return true
}
}
return false
}
6、实现call
Function.prototype.myCall = (context, ...args) => {
context = context || window
const key = symbol()
context[key] = this
const result = context[key](...args)
delete context[key]
return result
}
7、实现apply
Function.prototype.myApply = (context, args) => {
context = context || window
const key = symbol()
context[key] = this
const result = context[key](args)
delete context[key]
return result
}
8、实现bind
Function.prototype.myBind = (context, ...args) => {
const fn = this
return function newFn (...newArgs) {
return fn.apply(context, [...args, ...newArgs])
}
}
9、实现new
const myNew = (obj, ...args) => {
let newObj = {}
newObj.__proto__ = obj.prototype
//以上两步可以合并为:
//let newObj = Object.create(obj.prototype)
let result = obj.apply(newObj, args)
return typeof result === 'object' ? result : newObj
}
10、实现instanceof
const myInstanceOf = (left, right) => {
let leftVal = left.prototype
let rightVal = right.prototype
while (true) {
if (leftVal === null) return false
if (leftVal === rightVal) return true
leftVal = leftVal.__proto__
}
}
11、函数防抖deBounce
const deBounce = (fn, wait = 50) => {
let timer = 0
return function (...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
}
12、函数节流throttle
const throttle = (fn, wait = 50) => {
let prev = 0
return function (...args) {
let now = +new Date()
if (now - prev > wait || !prev) {
prev = now
fn.apply(this, args)
}
}
}
13、数组去重
//方法一
const uniqueArr = [...new Set(array)] //Array.from(new Set(array))
//方法二
const uniqueArr = (array) => {
let newArr = []
array.forEach(item => {
if (newArr.indexOf(item) === -1) {
newArr.push(item)
}
})
return newArr
}
//方法三
const uniqueArr = (array) => {
return array.filter((item, index) => {
return array.indexOf(item) === index
})
}
//方法四
const uniqueArr = (array) => {
return array.reduce((prev, curr) => {
return prev.includes(curr) ? prev : [...prev, ...curr] //prev.concat(curr)
}, [])
}
14、数组扁平化
//方法一
const arrayFlat = array.flat(Infinity)
//方法二
const arrayFlat = (array) => {
return array.reduce((prev, curr) => {
return prev.concat(Array.isArray(curr) ? arrayFlat(curr) : curr)
}, [])
}
15、数组柯里化
//方法一
const curry = (fn, args) => {
const length = fn.length //期望函数的参数个数
args = args || []
return function () {
const newArgs = args.concat(Array.prototype.slice.call(arguments))
// 如果参数个数小于最初的fn所需参数个数,则递归调用继续收集参数,否则直接调用期望函数
if (newArgs.length >= length) {
return fn.apply(this, newArgs)
} else {
return curry.call(this, fn, newArgs)
}
}
}
//ES6版本
let curry = (fn, arr = []) => (...args) => (
arg => arg.length === fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args])
//用例
function multiFn (a, b, c) {
return a * b * c;
}
let multi = curry(multiFn);
multi(2)(3)(4);
16、继承的实现
//***原型链关系begin***
//构造器原型链
Child.__proto__ === Parent
Parent.__proto__ === Function.prototype
Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
//实例原型链
child.__proto__ === Child.prototype
Child.prototype.__proto__ === Parent.prototype
Parent.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
//***原型链关系end***
//原型链继承
Child.prototype = new Parent()
Child.prototype.constructor = Child
//类式继承
function Child (name, parentName) {
Parent.call(this, parentName)
this.name = name
}
//组合式继承
function Child (name, parentName) {
Parent.call(this, parentName)
this.name = name
}
Child.prototype = new Parent()
Child.prototype.constructor = Child
//寄生组合继承
function Child (name, parentName) {
Parent.call(this, parentName)
this.name = name
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
//ES6继承
class Parent {
constructor(name) {
this.name = name
}
}
class Child extends Parent {
constructor(name, parentName) {
super(parentName)
this.name = name
}
}
const child = new Child('son', 'father')
17、实现promise
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function MyPromise (fn) {
const taht = this
that.state = PENDING
that.value = null
that.resolvedCallbacks = []
that.rejectedCallbacks = []
function resolve (value) {
if (that.state === PENDING) {
that.state = RESOLVED
that.value = value
that.resolvedCallbacks.map(cb => cb(that.value))
}
}
function reject (value) {
if (that.state === PENDING) {
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map(cb => cb(that.value))
}
}
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
MyPromise.prototype.then = (onFulfilled, onRejected) => {
const that = this
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected :
r => {
throw r
}
if (that.state === PENDING) {
that.resolvedCallbacks.push(onFulfilled)
that.rejectedCallbacks.push(onRejected)
}
if (that.state === RESOLVED) {
onFulfilled(that.value)
}
if (that.state === REJECTED) {
onRejected(that.value)
}
}
//用例
new MyPromise((resolve, reject) => {
resolve(1)
}).then(value => {
console.log(value)
})
18、原生实现Ajax
let request = new XMLHttpRequest()
request.open('GET', url, true)
request.onreadystatechange = function () {
if (request.readyState === 4 && request.status === 200) {
console.log('请求正常')
} else {
console.log('请求异常')
}
}
request.send()
19、实现Object.create
const create = (proto) => {
function F () { }
F.prototype = proto //将原型挂在构造函数的prototype上
F.prototype.constructor = F
return new F()
}
20、实现千位分隔符
//方法一
const format = (num) => {
const reg = /\d{1,3}(?=(\d{3})+$)/g //表示前面有1~3个数字,后面的至少由一组3个数字结尾
return (num + '').replace(reg, '$&,') //$& 表示与正则表达式相匹配的内容
}
//方法二
const format = (num) => {
num = num + ''
let str = ''
for (let i = num.length - 1, j = 1; i >= 0; i--, j++) {
if (j % 3 === 0 && i != 0) { //每隔三位加逗号,过滤正好在第一个数字的情况
str += num[i] + ','
continue
}
str += num[i] //倒着累加数字
}
return str.split('').reverse().join('') //字符串=>数组=>反转=>字符串
}
21、解析URL params为对象
let str = "http://www.baidu.com/we/index.html?id=898602B8261890349226&aaa=123&ccc=456";
let urlObject = (str) => {
let finalObj = new Object()
let data = str.slice(str.indexOf('?') + 1, str.length - 1)
let subParams = data.substr(1).split('&')
for (let i = 0; i < subParams.length; i++) {
let singleParam = subParams[i].split('=')
finalObj[singleParam[0]] = singleParam[1]
}
return finalObj
}
22、判断是否为数组
//方法一 通过instanceof判断
arr instanceof Array
//方法二 通过constructor判断
arr.constructor === Array
//方法三 通过Object.prototype.toString.call()判断
Object.prototype.toString.call(arr) === '[object Array]'
//方法四 通过Array.isArray()判断
Array.isArray(arr)
23、实现jsonp
//简述
//1、定义一个函数,用于处理接收到的跨域数据
//2、生成一个script节点(dom节点),src属性记入传入的目的URL以及参数
//3、将新创建的节点添加到DOM树上
//详述
//1、动态创建地创建script标签以发起请求,在src中填写请求的目标路径,并传入查询参数callback也就是回调函数的函数名。
//2、服务器接收到请求时,会根据查询参数callback返回执行回调函数的语句,并在参数传入请求方想要的数据。
//3、请求方接收到响应后就会执行这个语句也就是执行回调函数,这样请求方就能在回调函数中取得想要的数据。
const callbackFunc = (result) => {
console.log(result)
}
let jsonpScript = document.createElement('script')
jsonpScript.src = 'http://demo.com/test?callback=callbackFunc'
document.body.appendChild(jsonpScript)
24、实现一个简单的cache工具
//使用闭包来隐藏数据,只提供API
const myCache = () => {
let data = {}
return {
set: function(key, val) {
data[key] = val
},
get: function (key) {
return data[key]
}
}
}
25、实现一个简单的图片懒加载
//通过Promise实现
const loadImage = (path) => {
return new Promise ((resolve, reject) => {
let img = new Image()
img.onload = function() {
resolve(img)
}
img.onerror = function() {
reject('图片加载失败')
}
img.src = path
})
}
//用例
let result = loadImage('www.baidu.com').then((res)=>{console.log(res,1)}).catch((e) => {console.log(e,2)})