web前端JS高阶面试题(1),高级开发工程师面试

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

const args = arguments
// 清除待执行的定时器任务
if (callback.timeoutId) {
clearTimeout(callback.timeoutId)
}
// 每隔delay的时间, 启动一个新的延迟定时器, 去准备调用callback
callback.timeoutId = setTimeout(function () {
callback.apply(that, args)
// 如果定时器回调执行了, 删除标记
delete callback.timeoutId
}, delay)
}
}

#####手写一个节流函数

/*
实现函数节流的函数
*/
export function throttle(callback, delay) {
let start = 0 // 必须保存第一次点击立即调用
return function () {
// 它的this是谁就得让callback()中的this是谁, 它接收的所有实参都直接交给callback()
console.log(‘throttle 事件’)
const current = Date.now()
if (current - start > delay) { // 从第2次点击开始, 需要间隔时间超过delay
callback.apply(this, arguments)
start = current
}
}
}

#####手写一个深拷贝函数

/*
1). 大众乞丐版
问题1: 函数属性会丢失
问题2: 循环引用会出错
/
export function deepClone1(target) {
return JSON.parse(JSON.stringify(target))
}
/

获取数据的类型字符串名
/
function getType(data) {
return Object.prototype.toString.call(data).slice(8, -1)
}
/

2). 面试基础版本
解决问题1: 函数属性还没丢失
*/
export function deepClone2(target) {
const type = getType(target)
if (type===‘Object’ || type===‘Array’) {
const cloneTarget = type === ‘Array’ ? [] : {}
for (const key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = deepClone2(target[key])
}
}
return cloneTarget
} else {
return target
}
}

/*
3). 面试加强版本
解决问题2: 循环引用正常
*/
export function deepClone3(target, map = new Map()) {
const type = getType(target)
if (type===‘Object’ || type===‘Array’) {
let cloneTarget = map.get(target)
if (cloneTarget) {
return cloneTarget
}
cloneTarget = type===‘Array’ ? [] : {}
map.set(target, cloneTarget)
for (const key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = deepClone3(target[key], map)
}
}
return cloneTarget
} else {
return target
}
}

/*
4). 面试加强版本2(优化遍历性能)
数组: while | for | forEach() 优于 for-in | keys()&forEach()
对象: for-in 与 keys()&forEach() 差不多
*/
export function deepClone4(target, map = new Map()) {
const type = getType(target)
if (type===‘Object’ || type===‘Array’) {
let cloneTarget = map.get(target)
if (cloneTarget) {
return cloneTarget
}
if (type===‘Array’) {
cloneTarget = []
map.set(target, cloneTarget)
target.forEach((item, index) => {
cloneTarget[index] = deepClone4(item, map)
})
} else {
cloneTarget = {}
map.set(target, cloneTarget)
Object.keys(target).forEach(key => {
cloneTarget[key] = deepClone4(target[key], map)
})
}
return cloneTarget
} else {
return target
}
}

#####自定义instanceof工具函数

/*
自定义instanceof工具函数:
语法: myInstanceOf(obj, Type)
功能: 判断obj是否是Type类型的实例
实现: Type的原型对象是否是obj的原型链上的某个对象, 如果是返回true, 否则返回false
*/
export function myInstanceOf(obj, Type) {
// 得到原型对象
let protoObj = obj.proto
// 只要原型对象存在
while(protoObj) {
// 如果原型对象是Type的原型对象, 返回true
if (protoObj === Type.prototype) {
return true
}
// 指定原型对象的原型对象
protoObj = protoObj.proto
}
return false
}

#####自定义new工具函数

/*
自定义new工具函数
语法: newInstance(Fn, …args)
功能: 创建Fn构造函数的实例对象
实现: 创建空对象obj, 调用Fn指定this为obj, 返回obj
*/
export function newInstance(Fn, …args) {
// 创建一个新的对象
const obj = {}
// 执行构造函数
const result = Fn.apply(obj, args) // 相当于: obj.Fn()
// 如果构造函数执行的结果是对象, 返回这个对象
if (result instanceof Object) {
return result
}
// 如果不是, 返回新创建的对象
obj.proto.constructor = Fn // 让原型对象的构造器属性指向Fn
return obj
}

#####手写axios函数

/*

  1. 函数的返回值为promise, 成功的结果为response, 失败的结果为error
  2. 能处理多种类型的请求: GET/POST/PUT/DELETE
  3. 函数的参数为一个配置对象
    {
    url: ‘’, // 请求地址
    method: ‘’, // 请求方式GET/POST/PUT/DELETE
    params: {}, // GET/DELETE请求的query参数
    data: {}, // POST或DELETE请求的请求体参数
    }
  4. 响应json数据自动解析为js的对象/数组
    /
    /
    发送任意类型请求的函数 /
    function axios({
    url,
    method=‘GET’,
    params={},
    data={}
    }) {
    // 返回一个promise对象
    return new Promise((resolve, reject) => {
    // 处理method(转大写)
    method = method.toUpperCase()
    // 处理query参数(拼接到url上) id=1&xxx=abc
    /

    {
    id: 1,
    xxx: ‘abc’
    }
    /
    let queryString = ‘’
    Object.keys(params).forEach(key => {
    queryString += ${key}=${params[key]}&
    })
    if (queryString) { // id=1&xxx=abc&
    // 去除最后的&
    queryString = queryString.substring(0, queryString.length-1)
    // 接到url
    url += ‘?’ + queryString
    }
    // 1. 执行异步ajax请求
    // 创建xhr对象
    const request = new XMLHttpRequest()
    // 打开连接(初始化请求, 没有请求)
    request.open(method, url, true)
    // 发送请求
    if (method===‘GET’) {
    request.send()
    } else if (method===‘POST’ || method===‘PUT’ || method===‘DELETE’){
    request.setRequestHeader(‘Content-Type’, ‘application/json;charset=utf-8’) // 告诉服务器请求体的格式是json
    request.send(JSON.stringify(data)) // 发送json格式请求体参数
    }
    // 绑定状态改变的监听
    request.onreadystatechange = function () {
    // 如果请求没有完成, 直接结束
    if (request.readyState!==4) {
    return
    }
    // 如果响应状态码在[200, 300)之间代表成功, 否则失败
    const {status, statusText} = request
    // 2.1. 如果请求成功了, 调用resolve()
    if (status>=200 && status<=299) {
    // 准备结果数据对象response
    const response = {
    data: JSON.parse(request.response),
    status,
    statusText
    }
    resolve(response)
    } else { // 2.2. 如果请求失败了, 调用reject()
    reject(new Error('request error status is ’ + status))
    }
    }
    })
    }
    /
    发送特定请求的静态方法 */
    axios.get = function (url, options) {
    return axios(Object.assign(options, {url, method: ‘GET’}))
    }
    axios.delete = function (url, options) {
    return axios(Object.assign(options, {url, method: ‘DELETE’}))
    }
    axios.post = function (url, data, options) {
    return axios(Object.assign(options, {url, data, method: ‘POST’}))
    }
    axios.put = function (url, data, options) {
    return axios(Object.assign(options, {url, data, method: ‘PUT’}))
    }
    export default axios

#####自定义事件总线

/*

  • 自定义事件总线
    /
    const eventBus = {}
    /

    {
    add: [callback1, callback2]
    delete: [callback3]
    }
    /
    let callbacksObj = {}
    /

    绑定事件监听
    /
    eventBus.on = function (eventName, callback) {
    const callbacks = callbacksObj[eventName]
    if (callbacks) {
    callbacks.push(callback)
    } else {
    callbacksObj[eventName] = [callback]
    }
    }
    /

    分发事件
    /
    eventBus.emit = function (eventName, data) {
    const callbacks = callbacksObj[eventName]
    if (callbacks && callbacks.length > 0) {
    callbacks.forEach(callback => {
    callback(data)
    })
    }
    }
    /

    移除事件监听
    */
    eventBus.off = function (eventName) {
    if (eventName) {
    delete callbacksObj[eventName]
    } else {
    callbacksObj = {}
    }
    }
    export default eventBus

#####自定义消息订阅与发布

/*
自定义消息订阅与发布
/
const PubSub = {}
/

{
add: {
token1: callback1,
token2: callback2
},
update: {
token3: callback3
}
}
/
let callbacksObj = {} // 保存所有回调的容器
let id = 0 // 用于生成token的标记
// 1. 订阅消息
PubSub.subscribe = function (msgName, callback) {
// 确定token
const token = ‘token_’ + ++id
// 取出当前消息对应的callbacks
const callbacks = callbacksObj[msgName]
if (!callbacks) {
callbacksObj[msgName] = {
[token]: callback
}
} else {
callbacks[token] = callback
}
// 返回token
return token
}
// 2. 发布异步的消息
PubSub.publish = function (msgName, data) {
// 取出当前消息对应的callbacks
let callbacks = callbacksObj[msgName]
// 如果有值
if (callbacks) {
// callbacks = Object.assign({}, callbacks)
// 启动定时器, 异步执行所有的回调函数
setTimeout(() => {
Object.values(callbacks).forEach(callback => {
callback(data)
})
}, 0)
}
}
// 3. 发布同步的消息
PubSub.publishSync = function (msgName, data) {
// 取出当前消息对应的callbacks
const callbacks = callbacksObj[msgName]
// 如果有值
if (callbacks) {
// 立即同步执行所有的回调函数
Object.values(callbacks).forEach(callback => {
callback(data)
})
}
}
/

4. 取消消息订阅
1). 没有传值, flag为undefined
2). 传入token字符串
3). msgName字符串
*/
PubSub.unsubscribe = function (flag) {
// 如果flag没有指定或者为null, 取消所有
if (flag === undefined) {
callbacksObj = {}
} else if (typeof flag === ‘string’) {
if (flag.indexOf(‘token_’) === 0) { // flag是token
// 找到flag对应的callbacks
const callbacks = Object.values(callbacksObj).find(callbacks => callbacks.hasOwnProperty(flag))
// 如果存在, 删除对应的属性
if (callbacks) {
delete callbacks[flag]
}
} else { // flag是msgName
delete callbacksObj[flag]
}
} else {
throw new Error(‘如果传入参数, 必须是字符串类型’)
}
}
export default PubSub

#####自定义数组声明式系列方法

/*
实现数组声明式处理系列工具函数
/
/

实现map()
/
export function map (array, callback) {
const arr = []
for (let index = 0; index < array.length; index++) {
arr.push(callback(array[index], index))
}
return arr
}
/

实现reduce()
/
export function reduce (array, callback, initValue) {
let result = initValue
for (let index = 0; index < array.length; index++) {
// 调用回调函数将返回的结果赋值给result
result = callback(result, array[index], index)
}
return result
}
/

实现filter()
/
export function filter(array, callback) {
const arr = []
for (let index = 0; index < array.length; index++) {
if (callback(array[index], index)) {
arr.push(array[index])
}
}
return arr
}
/

实现find()
/
export function find (array, callback) {
for (let index = 0; index < array.length; index++) {
if (callback(array[index], index)) {
return array[index]
}
}
return undefined
}
/

实现findIndex()
/
export function findIndex (array, callback) {
for (let index = 0; index < array.length; index++) {
if (callback(array[index], index)) {
return index
}
}
return -1
}
/

实现every()
/
export function every (array, callback) {
for (let index = 0; index < array.length; index++) {
if (!callback(array[index], index)) { // 只有一个结果为false, 直接返回false
return false
}
}
return true
}
/

实现some()
*/
export function some (array, callback) {
for (let index = 0; index < array.length; index++) {
if (callback(array[index], index)) { // 只有一个结果为true, 直接返回true
return true
}
}
return false
}
export function test() {
console.log(‘test()222’)
}

#####手写Promise

const PENDING = ‘pending’ // 初始未确定的状态
const RESOLVED = ‘resolved’ // 成功的状态
const REJECTED = ‘rejected’ // 失败的状态
/*
Promise构造函数
*/
function Promise(excutor) {
const self = this // Promise的实例对象
self.status = PENDING // 状态属性, 初始值为pending, 代表初始未确定的状态
self.data = undefined // 用来存储结果数据的属性, 初始值为undefined

最后

==
就答题情况而言,第一问100%都可以回答正确,第二问大概只有50%正确率,第三问能回答正确的就不多了,第四问再正确就非常非常少了。其实此题并没有太多刁钻匪夷所思的用法,都是一些可能会遇到的场景,而大多数人但凡有1年到2年的工作经验都应该完全正确才对。
只能说有一些人太急躁太轻视了,希望大家通过此文了解js一些特性。

并祝愿大家在新的一年找工作面试中胆大心细,发挥出最好的水平,找到一份理想的工作。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
self.data = undefined // 用来存储结果数据的属性, 初始值为undefined

最后

==
就答题情况而言,第一问100%都可以回答正确,第二问大概只有50%正确率,第三问能回答正确的就不多了,第四问再正确就非常非常少了。其实此题并没有太多刁钻匪夷所思的用法,都是一些可能会遇到的场景,而大多数人但凡有1年到2年的工作经验都应该完全正确才对。
只能说有一些人太急躁太轻视了,希望大家通过此文了解js一些特性。

并祝愿大家在新的一年找工作面试中胆大心细,发挥出最好的水平,找到一份理想的工作。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-weCKS8Bo-1713046429706)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值