常见的基础前端面试题
最近离职了,经验不足
文章目录
前言
最近遇到的一些前端基础面试题,我选了一些经典的题目
以下是本篇文章正文内容,下面案例可供参考
一、常用web浏览器缓存机制有哪几种?分别有什么区别
- cookie、localStorage(本地持久化存储)、sessionStorage(本地会话级存储)
-
相同点
1、都是浏览器的缓存机制
2、都是前端操作
3、都只能存储字符串 -
不同点:
1、操作api不同
2、cookie是以前的缓存机制,localStorage、sessionStorage是h5的缓存机制,浏览器的兼容性不一样
3、存储大小不一样,cookie可以存储4kb,20条(chrome),localStorage是5mb,不限制条数
4、存储时间不一样,cookie如果不设置限制时间,关闭浏览器后就会消失,localStorage不手动删除的情况下,永久存储,sessionStorage关闭消失
5、localStorage可以在当前浏览器的多个页面卡存储,sessionStorage只会在当前页面存储
二、事件对象的currentTarget和target有什么区别
currentTarget 是返回其事件监听器触发该事件的元素/返回绑定该事件的元素
Target 返回触发该事件的元素
三、函数柯里化实现add(1)(2)(3)等于6
代码如下(示例):
function curry(fn,args=[]){
return function(){
let arr = [...args,...arguments]
if(arr.length < fn.length){//判断是否获取到了全部的数据
return curry.call(this,fn,arr)//没有就继续调用自己
}else{
return fn.apply(this,arr)
}
}
}
let add = curry(function(a,b,c){
let arr = Array.from(arguments)
return arr.reduce((a,b)=>{
return a + b
})
})
console.log(add(1)(2)(3))
四、优雅降级和渐进增强
优雅降级 先写最高版本的代码,然后向下兼容
渐进增强 先写最低版本的代码,然后向上兼容
五、浅克隆和深克隆
1)克隆指的是复制数据,浅克隆指的是复制第一层数据,深克隆是复制所有层的数据 let obj = {name:“小明”,obj:{}}
2)浅克隆可直接使用Object原型上的 Object.assign() 方法
3)深克隆 数据结构较简单可使用 JSON.parse(JSON.stringify())
代码如下(示例):
/*
* 数据结构复杂可以使用递归等方式手动实现,也可使用新的api
* structuredClone(value, { transfer })
* 该方法接收两个参数
* value 是被克隆的对象。可以是任何结构化克隆支持的类型。
* transfer 是一个可转移对象的数组,里面的 值 并没有被克隆,而是被转移到被拷贝对象上。
*/
const obj1 = {
a: {
b: 'c'
}
}
const obj2 = structuredClone(obj1)
console.log(obj1 == obj2); // false
console.log(obj1 === obj2); // false
/*
*递归实现
*/
function deepClone(target) {
const map = new WeakMap()
function isObject(target) {
return (typeof target === 'object' && target) || typeof target === 'function'
}
function clone(data) {
if (!isObject(data)) {
return data
}
if ([Date, RegExp].includes(data.constructor)) {
return new data.constructor(data)
}
if (typeof data === 'function') {
return new Function('return ' + data.toString())()
}
const exist = map.get(data)
if (exist) {
return exist
}
if (data instanceof Map) {
const result = new Map()
map.set(data, result)
data.forEach((val, key) => {
if (isObject(val)) {
result.set(key, clone(val))
} else {
result.set(key, val)
}
})
return result
}
if (data instanceof Set) {
const result = new Set()
map.set(data, result)
data.forEach(val => {
if (isObject(val)) {
result.add(clone(val))
} else {
result.add(val)
}
})
return result
}
const keys = Reflect.ownKeys(data)
const allDesc = Object.getOwnPropertyDescriptors(data)
const result = Object.create(Object.getPrototypeOf(data), allDesc)
map.set(data, result)
keys.forEach(key => {
const val = data[key]
if (isObject(val)) {
result[key] = clone(val)
} else {
result[key] = val
}
})
return result
}
return clone(target)
}
六、call,bind,apply的区别
相同点:
1、都可以更改函数的this指针,都可以调用函数
2、都可以传参数
不同点:
1、参数类型不同,call和bind的参数都是选项形式的,apply的参数是数组
2、call和apply都是直接返回值,bind是返回函数,需要手动调用
七、js事件传播有哪几个阶段
js事件的三个阶段分别为:捕获阶段、事件派发阶段、冒泡阶段
1.捕获阶段:事件由页面元素接收,逐级向下,到具体的元素
2.事件派发阶段
3.冒泡阶段:跟捕获相反,具体元素本身,逐级向上,到页面元素
事件捕获:当使用事件捕获时,父级元素先触发,子元素后触发
事件冒泡:当使用事件冒泡时,子级元素先触发,父元素后触发
八、响应状态码1xx-5xx有什么区别?
1xx 是 响应中
2xx 是 响应成功
3xx 是 重定向
304是 请求的资源有问题
4xx 是 客户端出错
400是无效信息
403是请求的资源不被允许访问
404是请求的资源不存在 405是请求方法不被服务器允许
5xx 是 服务端出错
500是服务器内部错误
九、请写出ajax的流程以及相应的代码
//先写兼容性 兼容ie5 ie6
if (window.XMLHttpRequest) {
var xmr = new XMLHttpRequest()
} else { //ie5 ie6
var mar = new ActiveXObject("Microsoft.XMLHTTP");
}
// // 与后端建立连接
// //发送链接
xmr.open("GET/POST", "http://vt.ossjk.com/goods/getIndexInfo", true)
//设置请求头 时json文件还是form文件
//json 格式
xhr.setRequestHeader("content-type", "application/json");
//form 格式
xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
//发送请求给后端
xmr.send()
//后端返回
xmr.onreadystatechange = function() {
if (xmr.readyState === 4 && xmr.status === 200) {
console.log(JSON.parse(xmr.response)) //response 获得响应数据
}
}
十、跨域的解决方案
1.跨域是什么?
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
2. 有哪几种解决方案?
1)jsonp:只支持 GET,不支持 POST 请求,不安全 XSS 兼容性好
2)cors:需要后端配置相关的代码 可能造成安全隐患
3)postMessage:配合使用 iframe,需要兼容 IE6、7、8、9
4)document.domain:仅限于同一域名下的子域
5)websocket:需要后台配合修改协议,不兼容
6)proxy:使用代理去避开跨域请求,需要修改 nginx、apache 等的配置
十一、节流和防抖函数的区别
相同点:都是防止事件高频率的重复执行
不同点:节流函数是看第一次 防抖函数是看最后一次
代码如下(示例):
//节流函数
function throttle(fn,time){
let lastTime = 0
return function(){
let nowTime = +new Date()
if(nowTime - lastTime > time){
lastTime = nowTime
fn.apply(this,arguments)
}
}
}
let throttleHandler = throttle(function(){
console.log(123)
},3000)
let div = document.querySelector("div")
div.addEventListener("click",throttleHandler)
//防抖函数
function debounce(fn,time){
let endTime = 0
let timeHandler
return function(){
let nowTime = +new Date()
if(nowTime - endTime <= time){
clearTimeout(timeHandler)
timeHandler = setTimeout(function(){
endTime = nowTime
fn.apply(this,arguments)
},time)
}else{
clearTimeout(timeHandler)
endTime = nowTime
fn.apply(this,arguments)
}
}
}
let debounceHandler = debounce(function(){
console.log(222)
},3000)
let div = document.querySelector("div")
div.addEventListener("click",debounceHandler)
十二、js模块化有哪些标准!!!
amd、cmd、commonJs(nodejs)、ES6
amd和cmd是比较老旧的了,我也没怎么用过,所以就不过多介绍,这里我们主要说一下现在常用的CommonJS ,和ES6
1.CommonJS 规范—module.exports
前端浏览器还不支持,Nodejs中使用的是这个规范
2.ES6—export/import
在ES6中,我们可以使用 import 关键字引入模块,通过 exprot 关键字导出模块,功能较之于前几个方案更为强大,也是我们所推崇的,但是由于浏览器还不支持ES6目语法,所以,我们只能通过babel将不被支持的import编译为当前受到广泛支持的 require。
十三、聊一聊什么是闭包?
闭包就是让开发者可以从内部函数访问外部函数的作用域 ---- MDN社区
优点:
- 变量的持久化
- 变量的私有化
- 避免变量污染全局
缺点:
- 对内存消耗有影响。因内部函数保存了对外部变量的引用,频繁的引用闭包内的变量可能导致无法被垃圾回收,增大内存使用量,所以使用不当会导致内存泄漏
十四、数组去重
要求:数组内值相同的对象也是重
let a = { a: 1, b: undefined, };
let b = { b: undefined, a: { ac: 1 }, };
let ccc = { c: undefined, a: { ac: 1 }, };
let e = { b: undefined, a: 2, ss: null, };
let cc = { a: { ac: 1 }, b: undefined, };
let dd = { b: undefined, a: { ac: 1 }, };
let arr = [e, 1, a, b, '1', 2, 1, 2, cc, dd, ccc, { b: undefined, a: { ac: 1 }, }]
//实现
function uniqueArray(array) {
const rest = [];
ossa: for (const element of array) {
for (const r of rest) {
if (isObj(element, r)) {
continue ossa;
}
}
rest.push(element)
}
return rest;
}
function isPrimitive(value) { //判断是否为原始值
return value !== Object(value)
};
function isObj(value1, value2) {
if (isPrimitive(value1) || isPrimitive(value2)) {
return Object.is(value1, value2)
}
// 对象去重
const entries1 = Object.entries(value1);
const entries2 = Object.entries(value2)
if (entries1.length !== entries2.length) return false;
const bool = new Array();
for (const [key, value] of entries1) {
if (!(key in value2)) { //判断两个对象中是否有相同的key
return false
}
if (isObj(value, value2[key])) {
bool.push(true)
}
}
if (bool.length !== entries2.length) {
return false;
}
return true;
};
console.log(uniqueArray(arr));