面试题
开场题
let [a, b = 2, c = 3] = [1, undefined, null]
console.log(a, b, c)
setTimeout(() => {
console.log(1)
}, 0)
let p = new Promise((resolve, reject) => {
console.log(2)
reject()
resolve()
})
p.then(() => {
console.log(3)
}, () => {
console.log(4)
})
console.log(5)
function main() {
try {
setTimeout(() => {
throw new Error("async error");
}, 1000);
} catch (e) {
console.log(e, "err");
}
console.log("continue...");
}
main();
CSS
- 盒模型,标准盒模型和怪异盒模型,如何设置盒模型
box-sizing: content-box | border-box - CSS 实现自适应正方形
- padding 撑高,padding 的百分比是相对于父元素宽度
- vw 和 vh
- 微元素
margin-top: 100%:margin 的百分比值参照其包含块的宽度进行计算
- 浮动和定位,文档流,脱离文档流,z-index 使用注意事项
- BFC 概念理解、使用
- CSS 样式优先级、伪类伪元素的区别
- rem 布局
- 居中实现方案
- vertical-align 对齐,baseline 不同类型元素位置,数值百分比
- 横向滑动实现,flex-wrap
- 页面水印实现方案:
pointer-events: none;
三栏布局实现方案
- 弹性布局
- 定位或浮动
- grid 布局
<!-- 三栏布局:整体宽度占满屏幕,left 宽度 100px, right 宽度100px, middle宽度自适应 -->
<div id="left"></div>
<div id="middle"></div>
<div id="right"></div>
开放综合问题
前端动画实现
- CSS 动画、过渡,CSS 动画为什么性能更好
- JS 动画
- requestAnimationFrame
- 为什么定时器会卡顿、requestAnimationFrame 不会,任务队列执行机制问题
- 使用定时器做动画间隔时间多少合适,为什么
- 如何获取屏幕的刷新频率
- 浏览器渲染流程
Long Task 导致主线程被长期占用如何优化
- 性能分析工具使用
- 方案一:使用 web worker
- 方案二:任务分割,requestIdleCallback
工具&基础
- git 分支集成方式
- 代理抓包工具使用
- typescript
- 如何约束对象的属性名,联合类型
- any、never、unknown 区别和使用场景
- 跨域理解
- HTTP 缓存机制及使用
- 如何正确的使用图片,有哪些替代方案,可以从性能优化聊起
- webpack 的使用,loader plugin 区别,webpack 构建优化
- cookie localStorage sessionStorage 区别和使用场景
- 前端安全
JS
- 数据类型判断:null object
- promise 三种状态,微任务宏任务,event loop
- 数组和对象属性遍历方法,for in 和 for of 区别
- 数组原生方法,数值去重
- call,bind,apply,this
性能优化
- 加载优化
- 图片、懒加载
- 缓存
- 渲染优化
- 重绘重排
框架使用
React
以下代码有问题吗
// React Hook实现一个计数器,每秒+1
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, 1000);
return () => clearInterval(id);
}, []);
return <h1>{count}</h1>;
}
Vue
- vue 有哪些生命周期,对应的钩子和执行顺序
- 哪个时机适合做数据请求,哪个时机可以访问到 DOM
- vue 业务代码复用,函数式编程的优缺点
- 如何正确的做组件封装:UI 组件和业务组件
- 自定义表单组件,如何实现双向数据绑定
- computed 和 watch 区别,以及使用场景
- keep-alive 的理解
- vue2 和 vue3 的区别
- 为什么要使用 proxy
H5&小程序
- H5 页面如何吊起 APP
- URL Schema
- Universal Link
- 微信 wx-open-launch-app
编程题
数组元素求和尽量多方法&实现 Array.prototype.reduce polyfill
- 遍历
- 递归
- reduce
删除数组指定位置元素
arr.splice(2, 1)
arr.slice(0, 2).concat(arr.slice(3,))
实现 retry 方法,可以执行异步函数,失败后会重试,可以设置重试次数和重试缓冲时间,返回值是 Promise 实例
const retry = (promiseFn, times, delay) {
}
/*
* @param {function} promiseFn 异步任务函数,返回Promise
* @param {number} times 重试次数
* @param {number} delay 任务失败后缓冲时间
* @returns
*/
const retry = (promiseFn, times, delay) => {
let time = 0;
return new MyPromise((resolve, reject) => {
const run = () => {
console.log(`第${++time}次`);
promiseFn().then(
(value) => {
resolve(value);
},
(error) => {
if (time < times) {
setTimeout(() => {
run();
}, delay || 0);
} else {
reject("超过次数");
}
}
);
};
run();
});
实现一个可以设置超时时间的异步函数的执行方法,返回值为一个 Promise 对象
const timeoutRunner = (promiseFn, timeout) => {};
const timeoutRunner = (promiseFn, timeout) => {
// return new MyPromise((resolve, reject) => {
// promiseFn().then((value) => {
// resolve(value);
// }, reject);
// setTimeout(() => {
// reject("timeout");
// }, timeout);
// });
const timeoutPromise = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject("timeout race");
}, timeout);
});
return MyPromise.race([timeoutPromise, promiseFn()]);
};
发布订阅模式 & eventEmitter 实现,on off emit once
深拷贝
- 递归实现深拷贝:适用于大多数情况,但需要注意循环引用。
- JSON 序列化和反序列化:简单快速,但有一些限制。
- 使用 Lodash 库的
_.cloneDeep方法:功能强大,适用于各种复杂情况。
function deepClone(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
}
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof Array) {
var arrCopy = [];
obj.forEach(function (item, index) {
arrCopy[index] = deepClone(item);
});
return arrCopy;
}
if (obj instanceof Object) {
var objCopy = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
objCopy[key] = deepClone(obj[key]);
}
}
return objCopy;
}
throw new Error("Unable to copy object! Its type isn't supported.");
}
// 示例用法
var original = {
name: "John",
age: 30,
date: new Date(),
hobbies: ["reading", "travelling"],
address: {
city: "New York",
zip: "10001",
},
};
var copy = deepClone(original);
console.log(copy);
防抖节流
- 防抖 Debounce,将多次执行变为最后一次执行,当触发事件时,会延迟一定时间去执行,如果在这段时间内再次触发事件,则重新计算延迟时间,适用于用户输入校验处理
- 节流 Throttle,限制函数在一定时间内只能执行一次,适用于浏览器滚动处理
JS 数组去重
- 使用 Set:最简洁和高效的方法。
- 使用 forEach 和对象:利用对象属性名唯一性。
- 手动遍历:最基本的方法,适合理解去重的原理。
- 使用 filter 和 indexOf:适合理解和学习数组方法。
- 使用 reduce 和 includes:适合理解和学习 reduce 方法。
分别使用 ES5 和 ES6 语法定义一个常量
ES5 语法:使用 Object.defineProperty 方法来定义常量,通过设置 writable 属性为 false 来确保常量不可修改。
Object.defineProperty(window, "MY_CONSTANT", {
value: 42,
writable: false,
enumerable: true,
configurable: false,
});
实现方案思路
- Vue 命令式组件实现,注册全局弹窗
- 多环境适配实现:策略模式和适配器模式
- 商品列表信息卡片实现:UI 组件和业务组件分层实现