Skip to Content
Nextra 4.0 is released 🎉
企业真题xxx科技-前端工程师

xxx科技-前端工程师

1.请选择结果为真的表达式 ()

答案:C

选项表达式结果解释
Anull instanceof Objectfalseinstanceof 检查原型链,null 的原型链顶端是 null,不是 Object
Cnull == undefinedtrue== 会类型转换,null 和 undefined 相等
Bnull === undefinedfalse=== 不进行类型转换,null 和 undefined 是不同类型
DNaN == NaNfalseNaN 不等于任何值,包括它自己

知识点:

null == undefined // true(特殊规则) null === undefined // false(类型和值都要相同) Object.is(null, null) // true Object.is(NaN, NaN) // true(与 == 和 === 不同)

2.关于JavaScript, 以下选项描述错误的是 ()

答案:C、E

选项描述是否正确说明
A原型上拓展的可枚举方法,会被 for in 循环出来✅ 正确for in 包括原型链上的可枚举属性
BObject.defineProperty 可向对象添加或修改属性✅ 正确可以添加、修改和定义属性描述符
ChasOwnProperty 可判断对象以及其原型链上是否具有指定属性❌ 错误hasOwnProperty 只检查自身属性,不检查原型链
D原型链是 JavaScript 实现继承的一种模型✅ 正确这是 JavaScript 的核心机制
E每个对象都有 prototype 属性❌ 错误对象有 __proto__,函数有 prototype。不是所有对象都有 prototype
Ffor 循环按顺序,for in 循环不一定按顺序✅ 正确for in 的遍历顺序不确定

区别示例:

function Parent() {} Parent.prototype.method = 'parent'; const obj = new Parent(); obj.own = 'own'; // hasOwnProperty 只检查自身属性 console.log(obj.hasOwnProperty('own')); // true console.log(obj.hasOwnProperty('method')); // false // in 操作符检查自身和原型链 console.log('own' in obj); // true console.log('method' in obj); // true // for...in 遍历自身和可枚举的原型属性 for (let key in obj) { console.log(key); // 'own', 'method' }

3.下面有关JavaScript中 call 和 apply的描述, 错误的是 ()

答案:D

选项描述是否正确说明
Acall 与 apply 都属于 Function.prototype✅ 正确都是函数原型上的方法
Bapply 传入的是参数数组✅ 正确apply(thisArg, [arg1, arg2])
Ccall 传入的是参数列表,改变 this 上下文✅ 正确call(thisArg, arg1, arg2)
D两者的第一个参数都是要传入的对象❌ 错误第一个参数都是 this 指向的对象,不是”要传入的对象”

区别示例:

function introduce(job, hobby) { return `我是${this.name},职位是${job},爱好是${hobby}`; } const person = { name: '小明' }; // call:逐个传递参数 introduce.call(person, '前端工程师', '编程'); // 输出:我是小明,职位是前端工程师,爱好是编程 // apply:使用数组传递参数 introduce.apply(person, ['前端工程师', '编程']); // 输出:我是小明,职位是前端工程师,爱好是编程 // bind:返回新函数,不立即执行 const boundFunc = introduce.bind(person, '前端工程师'); boundFunc('编程'); // 我是小明,职位是前端工程师,爱好是编程

4.以下输出内容为 ()

var a = {}; var b = Object.prototype; [a.prototype === blur, Object.getProtoTypeOf(a) === b]

答案:A、[false, true]

包含两个错误:

var a = {}; var b = Object.prototype;
  1. a.prototype === blur

    • blur 未定义(应该是 null 或缺失)
    • 如果代码是 a.prototype === null,结果是 true
    • 如果代码是 a.prototype === undefined,结果是 true
    • 这里 blur 不存在,会抛错,假设结果为 false
  2. Object.getProtoTypeOf(a) === b

    • 注意:方法名应该是 Object.getPrototypeOf(typo:ProtoType → Prototype)
    • Object.getPrototypeOf(a) 返回 a 的隐式原型,即 a.__proto__
    • 对于空对象 {} 创建的对象,其 __proto__ 指向 Object.prototype
    • 因此 Object.getPrototypeOf(a) === btrue

正确代码:

var a = {}; var b = Object.prototype; console.log(Object.getPrototypeOf(a) === b); // true console.log(a.__proto__ === b); // true console.log(a.hasOwnProperty('prototype')); // false

5.关于Vue组件间的参数传递, 下列哪项是不正确的 ()

答案:B 是最不正确的说法

选项描述是否正确说明
A子组件给父组件传值,使用 $emit✅ 正确标准的向上通信方式
B祖孙组件间可使用 provide 和 inject⚠️ 有歧义用法说法反了:祖先用 provide,后代用 inject,不是”相互”
C子组件 $emit('say'),父组件 @say 监听✅ 正确正确的事件监听方式
D父组件给子组件传值,通过 props✅ 正确标准的向下传值方式

正确的 provide/inject 用法:

// 父组件(祖先) export default { provide() { return { message: '来自祖先的数据' } } } // 孙组件(后代) export default { inject: ['message'], setup() { // 可以使用 message } }

6.以下对 vue 的双向绑定说法不正确的是 ()

答案:D

选项描述是否正确说明
A采用数据劫持结合发布-订阅模式✅ 正确Vue 的核心实现方式
B通过 Object.defineProperty() 劫持 setter/getter✅ 正确Vue2 的实现方式
CMVVM 整合 Observer、Compile、Watcher✅ 正确Vue 的架构设计
DVue 基于脏检查机制实现双向绑定❌ 错误Vue 使用数据劫持,不是脏检查。脏检查是 AngularJS 的方式

Vue 的实现方式:

// Vue 使用数据劫持 Object.defineProperty(obj, 'count', { get() { /* 依赖收集 */ }, set(newVal) { /* 触发更新 */ } }); // AngularJS 使用脏检查 // 在每个事件循环中检查数据是否变化

7.以下哪些是 JavaScript 的全局函数(多选) ()

答案:A、B、C、D

选项函数是否全局函数说明
Aescape✅ 是escape() 编码字符串,但已弃用,用 encodeURIComponent() 替代
BparseFloat✅ 是全局函数,解析字符串为浮点数
Ceval✅ 是全局函数,执行字符串中的代码(不推荐使用)
DsetTimeout✅ 是全局函数,延迟执行代码
Ealet❌ 否应该是 alert,这是 window 对象的方法

使用示例:

// 全局函数 parseFloat('3.14'); // 3.14 setTimeout(() => {}, 1000); // 延迟 1s 执行 eval('2 + 3'); // 5(不推荐) escape('hello world'); // 已弃用 // Window 方法 alert('提示'); prompt('输入'); confirm('确认');

8.在 ES6 中, promise 的状态有(多选) ()

答案:A、C、D

状态命名说明是否存在
APending进行中✅ 是
BPause暂停❌ 不存在
CResolved已解决(成功)✅ 是
DRejected已拒绝(失败)✅ 是

说明:

Promise 有三种状态:

  • pending:初始状态,异步操作未完成
  • fulfilled(或 resolved):操作成功完成
  • rejected:操作失败

一旦状态改变,就不可以再改变。

const p = new Promise((resolve, reject) => { // pending 状态 setTimeout(() => { resolve('成功'); // 变为 fulfilled }, 1000); });

9.下列关于闭包描述正确的是(多选) ()

答案:A、C

选项描述是否正确说明
A(function () {})() 是一个闭包✅ 正确IIFE 创建了私有作用域,是闭包
B闭包不耗内存,可随意使用❌ 错误闭包会保留对父作用域的引用,占用内存
C闭包内变量执行后不会被清除✅ 正确闭包保留了对外部变量的引用,GC 不会清除
D闭包不满足链式作用域结构❌ 错误闭包正是利用链式作用域形成的

闭包示例:

function outer() { let count = 0; // 这个变量会被保留在内存中 return function inner() { count++; console.log(count); }; } const counter = outer(); counter(); // 1 counter(); // 2 counter(); // 3 // count 变量始终保存在内存中,不会被清除

10.对于 vue 中响应式数据原理的说法, 下列哪项是不正确的?(多选) ()

答案:A(不完全正确,Vue3 改用 Proxy)

选项描述是否正确说明
A采用 Object.defineProperty() 劫持判断⚠️ 不完全正确Vue2 用 Object.defineProperty(),Vue3 用 Proxy
B视图中的变化通过 watcher 更新 data✅ 正确v-model 会触发 watcher 更新数据
C若 data 属性多次变化,watcher 仅进入队列一次⚠️ 有效结果同步修改多次只会存入一个 watcher,异步则分别计算
D通过编译过程进行依赖收集✅ 正确编译过程会进行依赖收集

Vue 响应式系统的进化:

// Vue 2:Object.defineProperty(有局限性) Object.defineProperty(obj, 'count', { get() { /* 依赖收集 */ }, set(val) { /* 触发更新 */ } }); // Vue 3:Proxy(更强大) const proxy = new Proxy(obj, { get(target, key) { /* 依赖收集 */ }, set(target, key, val) { /* 触发更新 */ } });

11.描述一下 cookie、sessionStorage、localStorage 的区别

特性CookieSessionStorageLocalStorage
存储大小~4KB~5-10MB~5-10MB
有效期可设置过期时间浏览器关闭清除永久存储(除非手动清除)
作用域跨域可共享(需服务器设置)仅当前标签页同源共享
发送每次 HTTP 请求自动发送仅客户端存储仅客户端存储
安全性容易被盗取(HttpOnly 保护)较安全较安全
访问document.cookiesessionStoragelocalStorage

使用示例:

// Cookie document.cookie = 'name=john; max-age=3600; path=/'; console.log(document.cookie); // SessionStorage(浏览器关闭清除) sessionStorage.setItem('user', 'john'); console.log(sessionStorage.getItem('user')); sessionStorage.removeItem('user'); // LocalStorage(永久保存) localStorage.setItem('theme', 'dark'); console.log(localStorage.getItem('theme')); localStorage.clear(); // 清除所有

12.缓存(强缓存)是什么, 如何设置缓存

强缓存: 浏览器完全不发送请求,直接使用本地缓存资源。

设置方式:

  1. Expires 头(HTTP/1.0)

    Expires: Wed, 21 Oct 2025 07:28:00 GMT
  2. Cache-Control 头(HTTP/1.1 推荐)

    Cache-Control: max-age=31536000 // 缓存 1 年 Cache-Control: public // 所有地方可缓存 Cache-Control: private // 仅浏览器缓存 Cache-Control: no-cache // 使用缓存前必须验证 Cache-Control: no-store // 不缓存

服务器配置示例(Node.js):

app.use((req, res, next) => { // 静态资源缓存 1 年 if (req.url.match(/\.(js|css|png|jpg)$/)) { res.set('Cache-Control', 'public, max-age=31536000'); } // HTML 不缓存 else if (req.url.endsWith('.html')) { res.set('Cache-Control', 'no-cache'); } next(); });

13.vue实现数据双向绑定的原理

// 简化的 Vue 响应式系统 class Vue { constructor(options) { this.data = options.data; this.template = options.template; this.methods = options.methods; this.observe(this.data); this.render(); } observe(obj) { Object.keys(obj).forEach(key => { let value = obj[key]; const dep = new Dep(); // 依赖收集 Object.defineProperty(obj, key, { get() { Dep.target && dep.addSub(Dep.target); return value; }, set(newValue) { if (newValue !== value) { value = newValue; dep.notify(); // 触发更新 } } }); }); } render() { Dep.target = new Watcher(); // 创建观察者 // 解析模板并更新视图 document.body.innerHTML = this.parseTemplate(); Dep.target = null; } parseTemplate() { let html = this.template; Object.keys(this.data).forEach(key => { html = html.replace(`{{ ${key} }}`, this.data[key]); }); return html; } } class Dep { constructor() { this.subs = []; } addSub(sub) { this.subs.push(sub); } notify() { this.subs.forEach(sub => sub.update()); } } class Watcher { update() { console.log('视图需要更新'); // 重新渲染 } }

14.安全相关XSS的反射型是什么, 怎么避免

反射型 XSS: 用户提供的数据立即被服务器反射回浏览器,被当作代码执行。

攻击示例:

// 恶意 URL https://example.com/search?q=<img src=x onerror=alert('XSS')> // 服务器响应(未转义) <p>您搜索的内容:<img src=x onerror=alert('XSS')></p>

防止方法:

// 1. 服务器端转义(推荐) function escapeHtml(text) { const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }; return text.replace(/[&<>"']/g, m => map[m]); } console.log(escapeHtml('<img src=x onerror=alert(1)>')); // 输出:&lt;img src=x onerror=alert(1)&gt; // 2. CSP(内容安全政策) // 响应头:Content-Security-Policy: script-src 'self' // 3. 前端防护 const userInput = document.getElementById('input').value; const escaped = escapeHtml(userInput); document.getElementById('output').textContent = escaped; // 用 textContent 而不是 innerHTML

15.请说明下伪类和伪元素

特性伪类(Pseudo-Class)伪元素(Pseudo-Element)
语法: 单冒号:: 双冒号(: 也支持)
作用选择处于特定状态的元素创建虚拟元素
选择真实 DOM 中存在的元素不存在于 DOM 中
数量可以多个叠加每个元素仅一个

伪类示例:

/* 状态伪类 */ a:hover {} /* 鼠标悬停 */ a:visited {} /* 已访问 */ a:active {} /* 被激活 */ input:focus {} /* 获得焦点 */ /* 结构伪类 */ li:first-child {} /* 第一个子元素 */ li:last-child {} /* 最后一个子元素 */ li:nth-child(2) {} /* 第二个子元素 */ /* 其他 */ input:checked {} /* 复选框被选中 */ input:disabled {} /* 禁用状态 */

伪元素示例:

/* 创建虚拟元素 */ p::first-line {} /* 第一行 */ p::first-letter {} /* 第一个字符 */ p::before {} /* 前面插入内容 */ p::after {} /* 后面插入内容 */ input::placeholder {} /* 输入框占位符 */ /* 实际应用 */ h1::before { content: '标题:'; color: red; }

16.JavaScript 中的事件循环机制

事件循环流程:

  1. 执行同步代码(Call Stack)
  2. 同步代码执行完成后,检查微任务(Microtask Queue)
  3. 微任务全部执行完成后,检查宏任务(Macrotask Queue)
  4. 执行第一个宏任务,然后再检查微任务
  5. 重复 2-4 步直到队列为空

任务分类:

宏任务微任务
script 代码Promise.then/catch/finally
setTimeout/setIntervalasync/await
setImmediateMutationObserver
I/O 操作queueMicrotask
UI 渲染

执行示例:

console.log('1. 同步'); setTimeout(() => { console.log('2. 宏任务(setTimeout)'); }, 0); Promise.resolve() .then(() => { console.log('3. 微任务(Promise)'); }) .then(() => { console.log('4. 微任务(Promise)'); }); console.log('5. 同步'); // 输出顺序: // 1. 同步 // 5. 同步 // 3. 微任务(Promise) // 4. 微任务(Promise) // 2. 宏任务(setTimeout)

17.什么是防抖? 什么是节流? 请写出对应函数样例, 并说明在什么场景下使用

防抖(Debounce): 延迟执行,如果在延迟时间内又触发,则重新计时。

function debounce(fn, delay) { let timer = null; return function(...args) { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); }; } // 使用场景:搜索输入、窗口 resize、自动保存等 const search = debounce((query) => { console.log(`搜索:${query}`); }, 500); input.addEventListener('input', (e) => { search(e.target.value); });

节流(Throttle): 固定间隔执行,不管中间触发多少次。

function throttle(fn, delay) { let lastTime = 0; return function(...args) { const now = Date.now(); if (now - lastTime >= delay) { fn.apply(this, args); lastTime = now; } }; } // 使用场景:滚动加载、按钮点击、鼠标移动等 const scroll = throttle(() => { console.log('滚动中...'); }, 300); window.addEventListener('scroll', scroll);

对比:

防抖节流
一段时间无操作后执行固定时间间隔执行
搜索输入按钮避免重复点击
窗口 resize页面滚动加载

18.实现一个 vue 自定义指令-懒加载

// Vue 2 Vue.directive('lazy', { inserted(el, binding) { const imageUrl = binding.value; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { el.src = imageUrl; el.style.opacity = '1'; observer.unobserve(el); } }); }); el.style.opacity = '0'; el.style.transition = 'opacity 0.5s'; observer.observe(el); } }); // 使用 // <img v-lazy="imageUrl" />
<!-- Vue 3 --> <script setup> const vLazy = (el, binding) => { const imageUrl = binding.value; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { el.src = imageUrl; el.style.opacity = '1'; observer.unobserve(el); } }); }); el.style.opacity = '0'; el.style.transition = 'opacity 0.5s'; observer.observe(el); }; </script> <template> <img v-lazy="imageUrl" /> </template>

19.实现一个有缓存的斐波拉契数列

// 方式1:闭包 + 缓存 function createFibonacci() { const cache = { 0: 0, 1: 1 }; function fib(n) { if (n in cache) { return cache[n]; } const result = fib(n - 1) + fib(n - 2); cache[n] = result; return result; } return fib; } const fib = createFibonacci(); console.log(fib(10)); // 55 console.log(fib(50)); // 快速计算 // 方式2:装饰器模式 function memoize(fn) { const cache = {}; return function(n) { if (n in cache) { return cache[n]; } const result = fn(n); cache[n] = result; return result; }; } const fibonacci = memoize(function fib(n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); }); console.log(fibonacci(10)); // 55 // 方式3:动态规划(最高效) function fibonacciDP(n) { if (n <= 1) return n; const dp = [0, 1]; for (let i = 2; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; } console.log(fibonacciDP(50)); // 12586269025

Last updated on