本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
一、背景
在面试的过程中,面试官为了考察编程功底,通常会出两道题目让应聘者手写代码。手写代码的题目通常会有一道算法题,有一道编程题。编程题常考的是手写js api,或者实现防抖,节流的功能。
为了对面试做充足的准备,我们平时应该经常练习,加强编程能力。这篇文章总结了面试中经常出现的15道js编程题。
二、15道编程题
1. 实现防抖和节流功能
防抖:
const debounce = function (cb, delay) {
let timer = null
return (...argv) => {
clearTimeout(timer)
timer = setTimeout(function () {
cb(...argv)
}, delay)
}
}
节流:
const throttle = (cb, delay) => {
let pre = 0,
timer = null
return (...argv) => {
let now = new Date().getTime()
if (now - pre > delay) {
cb(...argv)
pre = now
} else {
clearTimeout(timer)
timer = setTimeout(function () {
cb(...argv)
}, delay)
}
}
}
2. 实现深拷贝
function deepClone(object, hash = new WeakMap()) {
if (object == null) return object
if (object instanceof Date) return new Date(object)
if (object instanceof RegExp) return new RegExp(object)
let newObj = object.constructor()
console.log(Reflect.ownKeys(object), newObj)
Reflect.ownKeys(object).forEach((key) => {
newObj[key] = object[key]
})
return newObj
}
3. 手写call函数
Function.prototype.newCall = function (_this, ...argvs) {
let context = _this
context.fn = this
let result = context.fn(...argvs)
delete context.fn
return result
}
4. 手写apply函数
Function.prototype.newApply = function (_this, argvs) {
let context = _this
context.fn = this
let result = context.fn(...argvs)
delete context.fn
return result
}
5. 手写bind函数
Function.prototype.newBind = function (_this) {
let fn = this,
bindArgs = Array.prototype.slice.call(arguments, 1)
return (...args) => {
return fn.call(_this, ...bindArgs, ...args)
}
}
6. 实现函数满足add(1)(2)(3)
这道题目的考察点在于你对于函数柯里化的理解程度
function curry(fn, args) {
var length = fn.length;
args = args || [];
return function() {
var _args = args.slice(0),
arg, i;
for (i = 0; i < arguments.length; i++) {
arg = arguments[i];
_args.push(arg);
}
if (_args.length < length) {
return curry.call(this, fn, _args);
}
else {
return fn.apply(this, _args);
}
}
}
var add = curry(function(a, b, c) {
console.log([a, b, c]);
});
add("a", "b", "c") // ["a", "b", "c"]
add("a", "b")("c") // ["a", "b", "c"]
add("a")("b")("c") // ["a", "b", "c"]
add("a")("b", "c") // ["a", "b", "c"]
7. 实现观察者模式
class Subject {
constructor(name) {
this.name = name
this.state = '灯不亮了'
this.observers = []
}
attach(observer) {
this.observers.push(observer)
}
setState(newState) {
this.state = newState
this.observers.forEach((observer) => observer.update(newState))
}
}
//观察者
class Observer {
constructor(name) {
this.name = name
}
update(newState) {
console.log(this.name + '说' + newState)
}
}
let light = new Subject('灯')
let mm = new Observer('小明')
let jj = new Observer('小件')
light.attach(mm) // 订阅
light.attach(jj)
light.setState('灯不亮了') // 发布
8. 实现一个EventEmitter(发布订阅模式)
class EventEmitter {
constructor() {
this.hooks = {}
}
on(name, fn) {
this.hooks[name] ? this.hooks[name].push(fn) : (this.hooks[name] = [fn])
}
emit(name) {
if (this.hooks[name]) {
this.hooks[name].forEach((fn) => fn())
}
}
}
9. 封装一个jsonp
function jsonp(url, data = {}, callback = 'callback') {
// 处理json对象,拼接url
data.callback = callback
let params = []
for (let key in data) {
params.push(key + '=' + data[key])
}
console.log(params.join('&'))
// 创建script元素
let script = document.createElement('script')
script.src = url + '?' + params.join('&')
document.body.appendChild(script)
// 返回promise
return new Promise((resolve, reject) => {
window[callback] = (data) => {
try {
resolve(data)
} catch (e) {
reject(e)
} finally {
// 移除script元素
script.parentNode.removeChild(script)
console.log(script)
}
}
})
}
jsonp('http://photo.sina.cn/aj/index', {
page: 1,
cate: 'recommend'
}, 'jsoncallback').then(data => {
console.log(data)
})
10. 实现new 关键字
-
定义一个对象obj
-
此对象的__proto__指向构造函数的prototype
-
执行构造函数,确保构造函数的this指向obj
function myNew() {
let constructor = [].shift.call(arguments)
let obj = {}
obj.__proto__ = constructor.prototype
let result = constructor.apply(obj, arguments)
return typeof result === 'object' ? result || obj : obj
}
11. 实现instanceof关键字
核心:原型链查找
function _instanceof(A, B) {
while (B.prototype) {
if (A.__proto__ == B.prototype) {
return true
} else {
B = B.prototype
}
}
return false
}
12. 实现Object.assign
Object.newAssign = function () {
let target = [].shift.call(arguments)
for (let i = 0; i < arguments.length; i++) {
let nextObj = arguments[i]
if (nextObj != null) {
for (let key in nextObj) {
if (nextObj.hasOwnProperty(key)) {
target[key] = nextObj[key]
}
}
}
}
return target
}
13. 实现一个解析url参数为对象的函数
function parse(url) {
var regExp = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/
let list = url.match(regExp)
let fields = [
'url',
'scheme', // Scheme(协议)
'slash', // 斜线
'host', // Host(主机名)
'port', // 端口
'path', // 路径
'query', // 参数
'hash', // 锚点
]
let obj = {}
for (let i = 0; i < list.length; i++) {
obj[fields[i]] = list[i]
}
console.log(obj)
return obj
}
parse('https://harttle.land:80/tags.html?simple=true#HTML')
14. js格式化数字(添加千位分隔符)
function formNum(num) {
if (typeof num !== 'number') return num
num += ''
return num.replace(/\d{1,3}(?=(\d{3})+$)/g, function (s) {
return s + ','
})
}
15. 函数去重
函数去重其实难度不大,但是面试官往往会让你多说几种实现方案来考察你知识面的广度。此处我写了6种函数去重的方式。
1. new Set去重,缺点:没法去重{}
function unique(arr) {
return [...new Set(arr)]
}
2. 双重for循环+splice,缺点:NaN和{} 无法去重
function unique1(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1)
j--
}
}
}
return arr
}
3.for循环+新建数组+indexof,缺点:NaN和{} 无法去重
function unique2(arr) {
let array = []
for (let i = 0; i < arr.length; i++) {
if (array.indexOf(arr[i]) == -1) {
array.push(arr[i])
}
}
return array
}
4.for循环+新建数组 + includes ,缺点:{}没有去重
function unique3(arr) {
let array = []
for (let i = 0; i < arr.length; i++) {
if (!array.includes(arr[i])) {
array.push(arr[i])
}
}
return array
}
5. hasOwnProperty + object + filter, 全部去重了
function unique4(arr) {
let obj = {}
return arr.filter((item, index) => {
let res = obj.hasOwnProperty(typeof item + item)
? false
: (obj[typeof item + item] = true)
return res
})
}
6.new Map + 新建数组,缺点:{}没有去重
function unique5(arr) {
let map = new Map(),
array = []
for (let i = 0; i < arr.length; i++) {
if (!map.has(arr[i])) {
map.set(arr[i], true)
array.push(arr[i])
}
}
return array
}