大厂前端面试题解析

1. hash模式和history模式


解析:在前端路由中, hash 模式通过 URL 中的 # 及后面内容来标识路由,改变 hash 不会重新加载页面,兼容性好,例如 https://example.com/#home 。 history 模式借助 HTML5 的 history API (如 pushState 、 replaceState 等)实现,URL 更简洁美观,如 https://example.com/home ,但需要后端配合处理刷新时的路由匹配,防止出现 404 错误。


2. ref和reactive


解析:在 Vue3 中, ref 用于创建响应式数据,既可以处理基本类型数据,也能处理对象类型数据。对于基本类型,通过 .value 来访问和修改值;对于对象类型,会自动将其转换为 reactive 形式的响应式数据。 reactive 主要用于创建对象类型的响应式数据,基于 Proxy 实现,直接操作对象属性即可实现响应式更新。


3. ref可以代理复杂对象吗?为什么


解析: ref 可以代理复杂对象。当向 ref 传入一个对象时,Vue3 内部会使用 reactive 对该对象进行包装,利用 Proxy 实现响应式代理,使得对该对象的属性访问和修改都能触发响应式更新。


4. 类型保护


解析:在 TypeScript 中,类型保护是一种机制,用于在特定代码块中缩小变量的类型范围。常见方式有:


- typeof 类型保护,例如 if (typeof value === "string") { // 这里 value 被保护为 string 类型 } 。


- instanceof 类型保护,如 if (obj instanceof Date) { // 这里 obj 被保护为 Date 类型 } 。


- 自定义类型谓词,通过定义函数返回值类型为 T is U 的形式,如 function isStringValue(arg: unknown): arg is string { return typeof arg === "string"; } 。


5. 如何有选择性的继承公共类型


解析:在 TypeScript 中,可以使用以下方式:


- 接口继承:定义一个接口继承另一个接口的部分属性。例如 interface Base { a: number; b: string; } interface Derived extends Base { c: boolean; } , Derived 接口继承了 Base 接口的属性,还添加了自己的属性 。


- 类型别名和交叉类型:使用 Pick 工具类型从一个类型中选择部分属性创建新类型,如 type Base = { a: number; b: string; c: boolean; } type PartialBase = Pick ;也可以通过交叉类型 & 来合并类型,有选择地继承属性 。


6. 微前端


解析:微前端是一种将多个可独立开发、测试、部署的小型前端应用组合成一个整体应用的架构风格。它解决了单体前端应用在代码维护、技术栈升级、团队协作等方面的难题。常见的实现方案有 single - spa ,通过管理各个微应用的生命周期来实现集成;还有基于 iframe 的方案,利用 iframe 的隔离性来加载不同的微应用 。


7. 为什么不用frame


解析: frame 是早期 HTML 用于在页面中嵌入其他页面的标签,已被废弃。原因包括:


- 它的兼容性存在问题,在现代浏览器中支持度逐渐降低。


- frame 标签的功能有限,在样式控制、脚本交互等方面存在诸多不便。


- 它的安全性较差,容易引发跨站脚本攻击等安全问题。现在一般使用 iframe 来替代实现类似功能, iframe 有更灵活的 API 和更好的隔离性 。


8. this指向 + 输出题


解析:在 JavaScript 中, this 的指向取决于函数的调用方式:


- 作为对象方法调用: this 指向该对象,例如 const obj = { name: 'test', say: function() { console.log(this.name); } }; obj.say(); // 输出 'test' 。


- 作为普通函数调用: this 指向全局对象(在浏览器中是 window ,在 Node.js 中是 global ),例如 function say() { console.log(this); } say(); // 在浏览器中输出 window 。


- 使用 call 、 apply 、 bind 调用:可以显式指定 this 的指向,例如 const obj = { name: 'test' }; function say() { console.log(this.name); } say.call(obj); // 输出 'test' 。


- 箭头函数:箭头函数没有自己的 this ,它的 this 继承自外层作用域,例如 const obj = { name: 'test', say: () => { console.log(this.name); } }; obj.say(); // 这里 this 指向 window ,输出 undefined(假设全局未定义 name 变量) 。


9. call、apply、bind的区别


解析:


- 功能:三者都可以改变函数执行时的 this 指向。


- 调用方式: call 和 apply 会立即执行函数, call 传递参数是逐个列出,如 func.call(thisArg, arg1, arg2) ; apply 传递参数是一个数组,如 func.apply(thisArg, [arg1, arg2]) 。 bind 不会立即执行函数,而是返回一个新函数,新函数的 this 被绑定为指定的值,如 const newFunc = func.bind(thisArg); newFunc(arg1, arg2); 。


10. 箭头函数可以使用这些方法改变指向吗


解析:箭头函数不能使用 call 、 apply 、 bind 改变 this 指向。因为箭头函数没有自己独立的 this 绑定,它的 this 由外层作用域决定,在定义时就已经确定,无法通过这些方法来改变。


11. 基础类型与引用类型的区别 + 输出题


解析:


- 存储方式:基础类型(如 number 、 string 、 boolean 、 null 、 undefined )的值直接存储在栈内存中;引用类型(如 Object 、 Array 、 Function 等)的值存储在堆内存中,栈内存中存储的是指向堆内存的引用地址。


- 复制方式:基础类型复制时是值的复制,新变量和原变量相互独立;引用类型复制时是引用地址的复制,多个变量指向同一个堆内存地址,修改一个变量会影响其他变量 。


- 比较方式:基础类型比较的是值是否相等;引用类型比较的是引用地址是否相等 。


12. 有哪些类型判断方法


解析:


- typeof:可以判断基本数据类型(除 null ),返回值为 "number" 、 "string" 、 "boolean" 、 "undefined" 、 "function" 、 "object" 。例如 typeof 1 // "number" ,但 typeof null // "object" 存在历史遗留问题 。


- instanceof:用于判断对象是否为某个构造函数的实例,例如 const arr = []; arr instanceof Array // true 。


-
Object.prototype.toString.call():可以准确判断各种数据类型,例如
Object.prototype.toString.call(1) // "[object Number]" ,
Object.prototype.toString.call([]) // "[object Array]" 。


13. 判断对象属于哪个类


解析:可以使用 instanceof 操作符来判断对象是否属于某个类。例如:


javascript


class A {}

class B {}

const objA = new A();

const objB = new B();

function handleObject(obj) {

if (obj instanceof A) {

// 执行 A 类相关操作

console.log('This is an instance of A');

} else if (obj instanceof B) {

// 执行 B 类相关操作

console.log('This is an instance of B');

}

}

handleObject(objA); // 输出 'This is an instance of A'

handleObject(objB); // 输出 'This is an instance of B'


14. 代码题:任务队列间隔150ms


解析:


javascript


function taskQueue() {

const queue = [];

let timer;

function addTask(task) {

queue.push(task);

if (!timer) {

runTask();

}

}

function runTask() {

if (queue.length === 0) {

timer = null;

return;

}

const task = queue.shift();

task();

timer = setTimeout(runTask, 150);

}

return {

addTask

};

}

// 使用示例

const taskQueueInstance = taskQueue();

taskQueueInstance.addTask(() => console.log('Task 1'));

taskQueueInstance.addTask(() => console.log('Task 2'));


15. 代码题:对象改树型结构


解析:假设数据结构如下:


javascript


const data = [

{ node: 'A', tree: null },

{ node: 'B', tree: 'A' },

{ node: 'C', tree: 'A' },

{ node: 'D', tree: 'B' }

];

function toTree(data) {

const map = {};

data.forEach(item => {

map[item.node] = { ...item, children: [] };

});

const roots = [];

data.forEach(item => {

if (!item.tree) {

roots.push(map[item.node]);

} else {

if (!map[item.tree].children) {

map[item.tree].children = [];

}

map[item.tree].children.push(map[item.node]);

}

});

return roots;

}

const tree = toTree(data);

console.log(tree);


16. csrf攻击与预防


解析:


- CSRF攻击(跨站请求伪造):攻击者诱导受害者进入第三方网站,在该网站中向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的 。


- 预防措施:


- 验证 Referer 字段:在 HTTP 头中有一个 Referer 字段,它记录了该 HTTP 请求的来源地址。通过检查 Referer 判断请求是否来自合法源 。


- 添加 CSRF Token :在表单中添加一个随机生成的 Token ,后端验证请求中的 Token 是否合法。


- 设置 Cookie 的 SameSite 属性:将 Cookie 的 SameSite 属性设置为 Strict 或 Lax ,限制 Cookie 在跨站请求时的发送 。

原文链接:,转发请注明来源!