2022-2023 六年前端中大厂面试总结(仅题目,无答案)的答案参考

问题文章的链接:2022-2023 六年前端中大厂面试总结(仅题目,无答案)本人是个伤心🦐仔,从去年年底被裁员后断断续续面试了三个月, - 掘金 JavaScript

  1. js的事件循环机制,几个api是宏任务还是微任务 / 说一下事件循环机制 / 说一下事件循环
    JS 里面事件循环的过程,浏览器和 Node 的 / 怎么理解 JS 的异步?
    **答:浏览器的事件循环是js处理异步任务的核心,允许非阻塞i/o操作。js是单线程的,在事件循环下可以处理多个任务,比如定时器、网络请求。会交给对应的线程处理,对应线程处理完成以后,会把回调放进任务队列。当主线程处理完当前所有任务以后,会去任务队列获取任务执行。
    **在浏览器事件循环中,任务分宏任务和微任务。执行一个宏任务会情况当前所有的微任务,包括新产生的微任务。宏任务主要有: setTimeout, setInterval, I/O, 网络请求,dom事件, script脚本, MessageChannel **。
    微任务: Promise, MutationObserver
    **node的事件循环机制:

六个核心阶段

      1. Timers :执行 setTimeoutsetInterval 回调。
      2. Pending Callbacks:处理系统操作(如 TCP 错误)的回调。
      3. Idle/Prepare:内部使用。
      4. Poll:检索新的 I/O 事件(如文件读取、网络请求)。
      5. Check :执行 setImmediate 回调。
      6. Close Callbacks :处理关闭事件的回调(如 socket.on('close'))。
  1. 垃圾回收机制 / 说一下V8的垃圾回收机制 / 从垃圾回收的标记清除原理来理解一下闭包 (什么是GO,AO,比如在 setTimeout 里引用函数变量)

    **答:垃圾回收机制是管理内存的机制,防止内存泄漏。该机制分代回收,因为对象存活的时间长短不一,大部分对象存活时间短,分为新生代,存活时间长的为老生代。

    ****新生代使用Scavenge算法:

    ****内存布局分为: From-space 和 To-space

    ****过程:新的对象分配至From-space,当From-space满时,触发垃圾回收:第一步复制存活对象,从根节点出发,标记存活的对象,并将它们复制到To-space,同时整理内存碎片。

    ****第二步:交换空间,复制完成以后,From-space和To-space角色互换

    ****晋升:在经过上面几次过程以后,对象仍然存活,或者To-space超过25%,则晋升为老生代。

    ****老生代使用标记清除和标记压缩算法:

    ****从根节点出发,遍历所有可达对象标记为存活,回收未被标记的块,产生内存碎片。存活对象会一直内存两外一端,保证剩余空间连续。

    ****从垃圾回收机制看闭包:

    **标记清除算法在遇到闭包的时候是怎么处理的呢?当一个外部函数内部声明了变量同时声明了一个函数,内部函数访问了其变量,同时内部函数被setTimeout引用,此时当外部函数执行完成以后,按理应该会销毁其上下文,但是因为内部函数被setTimeout引用,形成闭包,而不会释放内存

javascript 复制代码
function outer() {
  var x = 10;
  setTimeout(function inner() {
    console.log(x);
  }, 1000);
}
outer(); // outer执行完毕,但inner仍引用x

**这种场景只要等待setTimeout执行完成,就会释放内存。
当内部函数被全局变量引用的话就会存在内存泄漏的场景。

**

javascript 复制代码
function createHeavyClosure() {
  var largeData = new Array(1000000).fill("*"); // 占用大量内存
  return function() {
    // 未直接使用largeData,但闭包仍持有引用
  };
}
var leak = createHeavyClosure();

**这种场景虽然largeData没有直接被使用,但是其放回的函数被全局变量leak引用,形成闭包,闭包的作用域链保留了其上下文

****全局对象(GO):所有的全局变量挂在在GO上

**活动对象(AO):函数执行时创建的上下文,包括其变量和参数。闭包访问其内部变量时,被闭包作用与链保留,知道闭包不可达。

  1. 为什么在2^53范围内是安全的?
    答:js 是用的双精度浮点数 总共存储63位,有效位为52位,有一位默认为1,总共53位。二进制表示2^52 + 2^51 .... + 2^1 + 2^0 等比求和 为2^53 - 1位
  2. 介绍一下函数节流,什么时候会用到,和防抖的区别。/ 防抖和节流的区别 / 说一下防抖节流定义,写其中一个函数 / 如果防抖在首次触发怎么写
    防抖:函数不会直接执行,会等待一段时间才执行,这段时间内如果再次执行,则会重置前端函数,让最新的函数执行,知道时间满足无新的函数执行。比较适合用户交互的场景,比如输入框输入
    **节流:设置一个时间段,在这个时间段内函数只会执行一次。当函数执行时这个等待时间得到重置。节流适合用在高频场景例如滚动事件。
    **
javascript 复制代码
Function.prototype.debounce = function (fn, delay) {
    let timer = null
    return function (...args) {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, delay)
    }
}

Function.prototype.throllte = function (fn, delay) {
    let timer = 0
    return function (...args) {
        const t = new Date().getTime()
        if (t - timer < delay) return
        fn.apply(this, args)
        timer = t
    }
}
// func.call(obj, arg1, arg2, arg3)
// func.apply(obj, [arg1, arg2, arg3])
// ...arg => [1, 2, 4]
  1. 如果有多个异步函数,怎么串行执行?回答 async/await / 如果不使用 async/await 怎么实现?写一下 / 如何处理多个 .then 导致代码一大坨很丑怎么解决?(回答了用 async/await
javascript 复制代码
const functions = [func1, func2]; // 支持任意数量的异步函数

functions.reduce((prevPromise, currentFunc) => {
  return prevPromise.then(currentFunc);
}, Promise.resolve()) // 初始 Promise
  .catch(error => console.error("Error:", error));
  1. 能说说你对 Promise 的理解吗?
    **三种状态:
    ****Pending
    ****Fulfilled
    ****Rejected
    ****不可变性:状态一旦确定不会改变
    **链式调用:可以同then,catch 链式调用
  2. generator 函数和 async 函数有什么区别
  3. Promise.all 如果有报错,是在 then 还是 catch 接收数据?接收的是什么样的数据?
javascript 复制代码
Promise.all([
    Promise.resolve(1),
    Promise.reject(2),
    Promise.resolve(3),
])
.then((res) => console.log('then...', res))
.catch((err) => console.log('catch...', err))

Promise.allSettled([
    Promise.resolve(1),
    Promise.reject(2),
    Promise.resolve(3),
])
.then((res) => console.log('then...', res))
.catch((err) => console.log('catch...', err))


Promise.race([
    Promise.reject(2),
    Promise.resolve(1),
    Promise.resolve(3),
])
.then((res) => console.log('race then...', res))
.catch((err) => console.log('race catch...', err))

打印如下:
then... [
  { status: 'fulfilled', value: 1 },
  { status: 'rejected', reason: 2 },
  { status: 'fulfilled', value: 3 }
]
catch... 2
race catch... 2
  1. promise其中一个then报错,中间的then会执行吗?catch后面的then会执行吗
    不会 , 会
  2. script标签,defer 和 async 什么区别? / async 和 defer 区别,使用 async 需要注意什么? 那么 Webpack 是如何解决这个问题的?
    **答:区别简单说就是 async / defer都可以异步加载资源。
    ****但是async会在加载完成以后立刻执行,所以有可能阻塞dom树的构建
    **而defer会等待渲染树完成以后执行,不会阻塞html渲染。
  3. async/awaitPromise 什么区别? / async/await 怎么实现的?

**区别:

****promise在创建以后代码会继续同步执行,当Promise被解析或者拒绝时,附加的回调函数添加到微任务队列中,等待主线程调用执行

****Async / await中的 await关键字会导致Javascript引擎暂停async函数的执行,直到promise被解析或者拒绝。当async函数等待Promise解析时,它不会阻止调用栈,并且可以执行其他同步代码。

****promise解决了传统callback函数导致的地狱回调问题。aysnc/awiat则是异步代码看起来像同步代码更加简洁。


**async/await实现:

javascript 复制代码
async function example() {
    const res = await somePromise
    console.log(res)
}


function example() {
    return spawn(function *() {
        const result = yield somePromise
        console.log(result)
    })
}

function spawn(gen) {
    return new Promise((resolve, reject) => {
        const g = gen()
        const step = (nextFn) => {
            try {
                const { value, done } = nextFn()
                if (done) return resolve(value)
                Promise.resolve(value)
                .then(
                    (v) => step(g.next(v)),
                    (e) => step(g.throw(e))
                )
            } catch (error) {
                reject(error)
            }
        }
        step(() => g.next())        
    })
}
  1. 异步的一些 API,比如 setTimeoutsetIntervalrequestIdleCallbackrequestAnimationFrame 还有 Promise,这几个有什么区别?

答: setTimeout, setInterval 是宏任务,用来延迟执行某个函数或者方法。setInterval会一直重复执行,执行时间过长会有间隔不准确的情况。requestAnmationFrame是和浏览器渲染周期下相关,通常用于动画,保证每次重绘前执行,避免卡顿。requestIdleCallback是浏览器空闲时间时会执行。适合处理不紧急的任务,比如日志上报等。Promise是微任务,上面大部分是宏任务,会在下个宏任务执行前执行完所有微任务。

  1. 数据获取是用的哪个 API? 回答 Axios。Axios 中有做哪些全局处理吗?
    答:
  2. 说一下link和script标签的加载过程中会对DOM树构建有什么影响?
    **答: 这个设计到浏览器的渲染机制,浏览器在渲染HTML文档时,会变下载边解析,遇到没有defer和async属性的Script标签时会停止解析等待script脚本下载完成执行完成。这是因为脚本可能修改dom,如果script标签带了async或者defer标签,async 会下载完成以后立刻执行,同样会阻塞页面的渲染,如果是defer标签,下载完成以后等到页面解析完成才执行,不会阻塞DOM渲染。
    **对于link标签,通常是用于加载外部css,css文件下载是和解析并行执行,构建CSSDOM也是在DOM构建完成以后进行一般不会阻塞页面。有种情况是当link标签后面跟了script标签时,由于script标签可能会访问css样式信息,所有要等css下载完成,script标签执行得到阻塞,DOM构建也被阻塞。
  3. JS的基本数据类型。
    基本类型: undefined、null、boolean、number、string、symbol、bigint
    使用Object.prototype.toString.call()进行判断:
javascript 复制代码
Object.prototype.toString.call(undefined);   // [object Undefined]
Object.prototype.toString.call(null);        // [object Null]
Object.prototype.toString.call(true);        // [object Boolean]
Object.prototype.toString.call(42);          // [object Number]
Object.prototype.toString.call("hello");     // [object String]
Object.prototype.toString.call(Symbol());    // [object Symbol]
Object.prototype.toString.call(123n);        // [object BigInt]
Object.prototype.toString.call({});          // [object Object]
Object.prototype.toString.call([]);          // [object Array]
Object.prototype.toString.call(new Date());  // [object Date]

value === null; // 直接全等判断
Array.isArray([]); // true
Number.isNaN(NaN); // true(注意:NaN !== NaN)
还有typeOf和 instanceof
  1. 怎么理解值类型和引用类型
    答: **值类型有:undefined、null、boolean、number、string、symbol、bigint
    ****值类型存储在栈内存中,赋值时是赋值值的副本。引用类型值是存储在堆内存中,变量保存的是内存地址。
    ****值类型传值,引用类型传址;
    **修改引用类型,处处跟着变!
  2. CommonJS 和 ES Module 有什么区别? / CommonJS 和 ES Module 有什么区别 / 为什么 ES Module 需要把 import 放在顶部,CommonJS 不需要? / 说一下 CommonJS 和 ES Module 的差异。
    **区别:
    ****加载方式: commonjs 运行时动态加载,es module编译时静态解析
    ****导出值类型: commonjs是值的拷贝,es module是值的引用
    ****顶层this指向:commonjs指向当前exports对象,es module是undefined
    ****处理循环依赖处理: commonjs可能得到未导出模块, 静态分析保证引用完整性
    ****require可以放在代码任何位置,import必须位于模块顶层
    **为什么放在顶部?ESM设计目标之一就是静态分析,引擎可以确定所有依赖关系从而可以进行优化(tree-shaking)。另外一点就是解析阶段建立引用链接,形成图谱,确保循环引用的问题。
  3. 0.1+0.2是否等于0.3 / 0.1 + 0.2 是否等于 0.3,如何解决?
    因为0.1 和 0.2 转二进制以后相加 的数是一个重复无限循环二级制数,转成十进制会丢失精度导致不会0.3.
  4. ES6 新增了哪些数据类型,说一下用法
    Symbol
ini 复制代码
const sym1 = Symbol('key');
const sym2 = Symbol('key');
console.log(sym1 === sym2); // false

Bigint

ini 复制代码
const big1 = 123456789012345678901234567890n;
const big2 = BigInt("12345678901234567890");
  1. JS 里面如果实现拖拽的功能。
javascript 复制代码
const dragElement = document.getElementById('dragElement');

dragElement.addEventListener('dragstart', (e) => {
  e.dataTransfer.setData('text/plain', e.target.id); // 保存数据
});

// 放置区域
const dropZone = document.getElementById('dropZone');
dropZone.addEventListener('dragover', (e) => {
  e.preventDefault(); // 必须阻止默认行为
});

dropZone.addEventListener('drop', (e) => {
  e.preventDefault();
  const id = e.dataTransfer.getData('text/plain');
  const draggedElement = document.getElementById(id);
  e.target.appendChild(draggedElement); // 将元素移动到放置区域
});
  1. CORS 如何实现跨域?

    **答:CORS是指跨域资源共享,它是通过指定服务器返回特定的头来实现跨域请求。

    ****基本流程是当浏览器检测到是跨域请求时,会附加Origin头(表明请求头),根据服务器返回的CORS头来决定是否允许跨域。

    **当非简单请求时,会先发一个OPTIONS预请求,来确认服务器是否支持实际的请求。

  2. 简单请求和非简单请求的区别。

    **答:从触发条件、流程、请求头等对比

    ****触发条件区别:

    ****简单请求触发的条件:特定的方法,比如GET POST HEAD,简单的头。

    ****复杂请求触发条件: 其他方法(PUT, DELETE, PATCH), 复杂的头,自定义头等。

    ****简单请求会直接发送真实的请求, 复杂请求会先发送OPTIONS预检请求,确认后再发送真实的请求

    ****仅允许简单头:

    **Accept Accept-Language Content-Language Content-Type (仅限 text/plain multipart/form-data application/x-www-form-urlencoded **)

    ****复杂请求头:

    **包含自定义头(如 Authorization )或非简单 Content-Type (如 application/json

  3. 如何处理跨域? iframe中,外面如何获取里面的真实高度?跨 iframe 通信是否了解过?

    **CORS, JSONP,代理服务器

    ****iframe + domain浏览器已经弃用,存在安全性问题

    ****iframe + postmessage

    **获取子iframe 都可以通过postmessage + 父message事件完成。

  4. 同一个请求发送多次,如何保证获取的是最后一次的结果

    **取消前面的请求(推荐)

    **序号法标记

scss 复制代码
let controller = null;

async function sendRequest() {
  // 取消之前的请求
  if (controller) controller.abort();
  
  // 创建新的 AbortController
  controller = new AbortController();
  
  try {
    const response = await fetch('https://api.example.com/data', {
      signal: controller.signal,
    });
    const data = await response.json();
    // 处理最终结果
    console.log('Latest data:', data);
  } catch (error) {
    // 忽略被中止的请求错误
    if (error.name !== 'AbortError') {
      console.error('Request failed:', error);
    }
  }
}

  // axios的取消
    if (cancelTokenSource) cancelTokenSource.cancel();
  // 创建新的 CancelToken
  cancelTokenSource = axios.CancelToken.source();

  // 结合react
  useEffect(() => {
      // 创建新的 AbortController
      controller = new AbortController();
    return () => {
      //	取消请求
      controller.abort();
    }
  }, [])
  1. 浏览器内存泄漏是否可以通过开发者工具看到,如何看到?

    **查看内存是否持续增长且只高不降

    **打开 Performance,点击memory,点击录制一段时间结束即可

  2. JS 严格模式有什么了解?有什么不一样的地方?

  3. 有什么方法可以改变 this 指针 / this 有哪几种指向

    **隐式绑定 : obj.method()

    ****显式绑定: apply, call, bind

    ****new

    **箭头函数

  4. 数组操作 mapforEach 有什么区别,是否可以打断循环?

    **map会返回一个新数组,forEach不会返回值

    ****map适用于基于原数组生成新数组的情况,foreach只是执行数组每一项

    ****都不能终止整个循环,foreach的return只会终止当前的循环,map每个元素被处理也无法终止。

    **foreach性能略好一点,不用开辟新的内存。

  5. filterfind 返回值有什么区别?
    filter返回数组 find返回item

  6. 十万个数组取第1万和第6万个元素速度有什么区别吗?为什么?

    **时间在同一级别,基本没啥区别

    ****获取时间复杂度O(1)

    ****问元素时,直接通过索引计算内存偏移量,一步定位到地址:

    **内存地址 = 基地址 + 索引 X 元素大小

  7. 数组的 sort 默认是按什么排序的?使用的什么算法

    **以前是二分,现在是TimSort排序

    **TimSort排序大概是:分块处理,合并策略,插入排序优化

  8. ES6 中的 SetMap 有什么区别?可以遍历吗,如何遍历?

    **数据结构:set是值的集合(类似数组), map是键值对的集合(类似对象)

    ****set集合的值唯一不会重复,map的键可以是任意类型( 数组,函数)

    **都可以用for ...of, foreach遍历

  9. 真实数组和伪数组有什么区别?
    类型不同,都有length属性,类数组是一个含有length属性的对象

  10. WeakSetWeakMap 有什么区别?是否可以遍历?
    WeakSet值必须是对象,WeakMap键必须是对象,其都是对值的弱引用(无其他对象引用会被回收)
    **WeakSet支持方法: add()
    ,** has() , delete()**

    ****WeakMap支持方法:get, set ,has ,delete

    **都不可遍历,无引用时都会自动被回收,不可以遍历

  11. 订阅发布和观察者模式有什么区别

    **观察者模式耦合度高,目标直接通知观察者,Vue响应系统就是

    **订阅者发布模式耦合度低,发布者向事件中心发布,Vue的EventBus

  12. 单例模式和工厂函数有什么区别|

    答: **单例模式:确保仅有一个实例

    **工厂模式:封装对象创建过程,根据输入返回不同实例。

  13. 说一下你用过 meta 标签

ini 复制代码
 // 声明字符编码
<meta charset="UTF-8">
// 视口设置(移动端必选)
<meta name="viewport" content="width=device-width, initial-scale=1.0">
// 页面描述 SEO优化
<meta name="description" content="这是一个关于前端技术的教程网站">
// 关键词
<meta name="keywords" content="HTML,CSS,Javascript,前端开发">
// robots
<meta name="robot" content="noindex, nofollow">
// 主题颜色
<meta name="theme-color" content="#00000" media="(prefers-color-scheme:dark)">
// 作者
<meta name="author" content="Johb Doe">
// 自动刷新
<meta http-equiv="refresh" content="https://example.com" >
  1. 如果想实现一个吸顶功能怎么实现,回答 position: sticky; ,那么不用 CSS 怎么实现。
ini 复制代码
const header = document.getElementById('stickyHeader');
const placeholder = document.createElement('div');
let isSticky = false;

function checkSticky() {
  const headerTop = header.getBoundingClientRect().top;
  if (headerTop <= 0 && !isSticky) {
    header.classList.add('fixed');
    placeholder.style.height = header.offsetHeight + 'px';
    header.parentNode.insertBefore(placeholder, header);
    isSticky = true;
  } else if (headerTop > 0 && isSticky) {
    header.classList.remove('fixed');
    placeholder.remove();
    isSticky = false;
  }
}

window.addEventListener('scroll', throttle(checkSticky, 100));
  1. JS 数组去重怎么实现。
    **[...new Set(arr)]
    ****filter
    **reduce

TypeScript

  1. 有了解过 TypeScript 吗,讲一下泛型? / 讲一讲 TS 泛型
    泛型可以从名字去理解,指可以使用广泛的类型。ts中的泛型一般可以用创建可复用、类型安全灵活的组件的特性。可以在定义函数,接口,类的时候不指定具体类型,在使用的时候才指定具体的类型。
csharp 复制代码
// 函数泛型
function identity<T>(items: T[]): T[] {
    retrun items.reverse()
}
// 类泛型
class Box<T> {
    private content: T
    constructor(value: T) {
      this.content = value
    }
}
// 接口泛型
interface KeyValuePair(K, V) {
    key: K;
    value: V
}

泛型约束:
通过extends限制类型范围

typescript 复制代码
interface HasLength {
  length: number;
}
function logLength<T extends HasLength>(obj: T): void {}
  1. infer 关键字。
    infer功能可以提取前面出现过的类型。可以理解为占位。
typescript 复制代码
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Result = ReturnType<() => number;>; // number

返回类型被占位了,传入的函数返回值是numebr,所以提取最后类型是number

typescript 复制代码
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
type Params = Parameters<(a: string, b: number) => void;>; // [string, number]

**同理,传入的函数参数类型有string和number,infer占位了传入参数类型,所以最后提取出[string,number]

**

  1. type和interface的区别
    答:在定义一个对象类型且没有任何操作的时候两者皆可以。但是如果在定义类class时或者需要进行继承/实现 (extends / implements)的时候,或者是声明合并的时候优先使用interface
typescript 复制代码
// 定义类
interface Animal {
  name: string;
}
// 继承
interface Bear extends Animal {
  honey: boolean;
}

class PolarBear implements Bear {
  name = "Ice";
  honey = true;
}
interface User { name: string; }
interface User { age: number; }
// 合并为 { name: string; age: number; }

定义非对象类型的时候,比如联合类型,元数组,函数类型, 还有一些操作比如typeOf, in, keyOf等优先使用type

typescript 复制代码
type Result = Success | Failure;    // 联合类型
type Pair = [string, number];      // 元组
type Handler = () => Promise<void>; // 函数类型

type Keys = keyof SomeObject;      // 获取键的联合类型
type ID = string | number;
  1. voidnever 区别。
    **两者都可以表示函数没有返回值,但是void是表示函数没有return 或者返回undefined
    **never则是函数永远不会执行完成,或者正常执行
javascript 复制代码
// void
function logMessage(): void {
  console.log("Hello");
  // 没有 return,或 return undefined
}


// never
function throwError(): never {
  throw new Error("Something failed");
}

function infiniteLoop(): never {
  while (true) {}
}
  1. 是否用过 TS,用到什么功能
    这些题你都可以说
  2. anyunknow 的区别 / TS 中 any 和 unknow 的区别
    **两者都是顶级类型,any和unknow都代表不确定的类型
    ****但是any可以绕过TypeScript的类型检测,可以赋值给任何值也可以任何类型值复制给any,运行直接操作(调用方法,访问内部属性等)而引发错误。
    **unknow则会类型检测,操作/使用前必须类型断言,否则会抛出错误
ini 复制代码
let value: any = "hello";
value.toFixed(2); // 编译通过,但运行时报错(字符串没有 toFixed 方法)

let value: unknown = "hello";
value.toFixed(2); // 编译报错:Object is of type 'unknown'

// 正确用法:需显式类型断言
if (typeof value === "number") {
  value.toFixed(2); // 安全
}

也就是说你是any你可以任意操作,你是unknow,不知道你是不是number,所以不能赋值、调用函数等操作

  1. 定义了一个 interface 只用到之前的 interface 的某几个字段则怎么办
    **pick
    **也可以用omit 排除不是的属性
typescript 复制代码
interface UserObj {
  readonly name: string;
  age: number;
  id: number;
  sex: 0 | 1;
  address: string;
  weight: number;
}

// 使用 Pick 来创建新类型
type Person = Pick<UserObj, "name" | "id">;

// 现在 Person 类型等同于:
interface Person {
  readonly name: string;
  id: number;
}

// omit
interface Person {
  name: string;
  age: number;
  address: string;
}
type WithoutAddress = Omit<Person, "address">; // { name: string; age: number }
  1. Pick 怎么实现,写一下
scala 复制代码
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
}
  1. omit怎么实现的
    omit是通过TypeScript的内置对象Exclued和pick实现的
scala 复制代码
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
  1. TS中,如果声明了一个对象,又声明了一个对象和之前的对象大部分key是相同的,怎么做。
typescript 复制代码
interface Original {
  id: string;
  name: string;
  age: number;
}
type PickObject = Pick<Original, 'id' | 'name'>
// interface
interface ExtendedObject  extends  PickObject {
  other: string
}
// type 
type ExtendedObject = PickObject & {
  other: string
}

React

  1. React Router 如何实现权限拦截?
    可以分为路由级,组件级,API级
    1. 高阶组件的拦截
    2. 自定义路由组件****
    3. react的context封装组件拦截
ini 复制代码
// src/contexts/auth-context.tsx
import React, { createContext, useContext, useMemo, useState } from 'react';

type UserRole = 'admin' | 'editor' | 'viewer' | null;
type Permissions = {
  viewDashboard: boolean;
  editContent: boolean;
  deleteContent: boolean;
  manageUsers: boolean;
};

type AuthContextType = {
  user: string | null;
  role: UserRole;
  permissions: Permissions;
  login: (username: string, role: UserRole) => void;
  logout: () => void;
};

const AuthContext = createContext<AuthContextType>({
  user: null,
  role: null,
  permissions: {
    viewDashboard: false,
    editContent: false,
    deleteContent: false,
    manageUsers: false
  },
  login: () => {},
  logout: () => {}
});

export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
  const [user, setUser] = useState<string | null>(null);
  const [role, setRole] = useState<UserRole>(null);

  // 动态计算权限
  const permissions = useMemo<Permissions>(() => ({
    viewDashboard: !!role,
    editContent: role === 'admin' || role === 'editor',
    deleteContent: role === 'admin',
    manageUsers: role === 'admin'
  }), [role]);

  const login = (username: string, newRole: UserRole) => {
    setUser(username);
    setRole(newRole);
  };

  const logout = () => {
    setUser(null);
    setRole(null);
  };

  return (
    <AuthContext.Provider value={{ user, role, permissions, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);
typescript 复制代码
// src/components/route-guard.tsx
import { useAuth } from '../contexts/auth-context';
import { Navigate, Outlet } from 'react-router-dom';

type RouteGuardProps = {
  requireLogin?: boolean;
  requiredPermissions?: Array<keyof Permissions>;
};

export const RouteGuard = ({ 
  requireLogin = true,
  requiredPermissions = []
}: RouteGuardProps) => {
  const { user, permissions } = useAuth();

  if (requireLogin && !user) {
    return <Navigate to="/login" replace />;
  }

  const hasPermission = requiredPermissions.every(
    perm => permissions[perm]
  );

  if (!hasPermission) {
    return <Navigate to="/403" replace />;
  }

  return <Outlet />;
};


 <AuthProvider>
    <Routes>
      {/* 公开路由 */}
      <Route path="/login" element={<LoginPage />} />
      <Route path="/403" element={<ForbiddenPage />} />

      {/* 需要登录的路由 */}
      <Route element={<RouteGuard requireLogin />}>
        <Route path="/dashboard" element={<Dashboard />} />
      </Route>

      {/* 需要管理权限的路由 */}
      <Route element={<RouteGuard requiredPermissions={['manageUsers']} />}>
        <Route path="/admin/users" element={<UserManagement />} />
      </Route>

      {/* 需要编辑权限的路由 */}
      <Route element={<RouteGuard requiredPermissions={['editContent']} />}>
        <Route path="/content/edit" element={<ContentEditor />} />
      </Route>
    </Routes>
  </AuthProvider>
javascript 复制代码
// src/hocs/with-permission.tsx
import { useAuth } from '../contexts/auth-context';
import { ReactNode } from 'react';

export const withPermission = (
  requiredPermissions: Array<keyof Permissions>,
  FallbackComponent: ReactNode = null
) => {
  return ({ children }: { children: ReactNode }) => {
    const { permissions } = useAuth();
    
    const hasPermission = requiredPermissions.every(
      perm => permissions[perm]
    );

    return hasPermission ? children : FallbackComponent;
  };
};

// 使用示例
const AdminPanel = withPermission(['manageUsers'])(() => (
  <div>管理员面板</div>
));
  1. 你提到了 React Context,说一下它的原理。
ini 复制代码
const MyContext = React.createContext(defaultValue);
  1. **创建的组件包含Provider和Consumer组件
    **在Provider组件提供Context值,并将属性value存储到fiber节点的context属性中
xml 复制代码
<MyContext.Provider value={currentValue}>
  {children}
</MyContext.Provider>
  1. 当使用useContext或者Context.consumer时,React会把该组件订阅到provider中,内部的 readContext去执行的逻辑操作

  2. 当Priovde的值发生变化,React会触发更新流程,重新渲染订阅了该Context的组件。

  3. 怎么理解 React 中的受控组件和非受控组件?在平时怎么写一个组件?

    **受控组件是:将数据赋值给state交给react控制,符合单向数据流

    **非受控组件:用ref获取组件数据,数据有DOM自身管理

  4. 你是怎么理解 JSX 的?
    用js写html

  5. React 组件是怎么渲染为 DOM 元素到页面上的 / React 中 setState 调用以后会经历哪些流程?

  6. 为什么setdata要设计成异步的
    合并多次更新,保持状态一致性,支持并发模式

  7. 如何进行数据管理?

  8. redux内的状态是怎么传到组件内部的

    **1. react-redux的Proivder组件包裹最外层,将Store注册到整个应用中

    **2. 使用connect高阶组件

  9. 能说一下 Redux 的原理吗?

  10. 使用过哪些 React Hooks?

  11. useState 是怎么实现的?

  12. 说一下 React 中的 Diff 算法。

  13. 说一个 Fiber 架构。

    React 中 Fiber 有了解过吗

    对 React 的 Fiber 架构有什么了解吗?中间的节点怎么通过链表连接起来

  14. React 的 HOC 是什么
    高阶组件

  15. React 里面有一个 A 组件,里面包裹了 B 子组件,A 组件是 10 个属性,B 只引用了其中两三个,如何提升性能

  16. PureComponent

  17. useCallback 和 useMemo 有什么区别 / useMemo,useCallback,useRef 的作

  18. hooks 可以放在函数的内部吗?为什么

  19. 对 React Hooks 的理解

  20. useEffect 的返回值有什么作用,执行时机

  21. 有开发过高阶组件吗?它的使用场景是什么?

    **高阶组件是指一个函数组件,接受一个组件作为参数,返回一个新的组件,用于复用逻辑。

    **场景:鉴权 ,见第一个问题

kotlin 复制代码
const withAuth = (WrappedComponent) => {
  return (props) => {
    if (!isLoggedIn()) return <Redirect to="/login" />;
    return <WrappedComponent {...props} />;
  };
};

const withData = (WrappedComponent, fetchData) => {
  return class extends React.Component {
    state = { data: null };
    async componentDidMount() {
      const data = await fetchData();
      this.setState({ data });
    }
    render() {
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
};
  1. 父子组件渲染声明周期和更新生命周期的顺序
    **声明周期说的是class组件,因为函数组件没有生命周期说法。
    ****挂载时声明周期有:
    ********constructor -> getDerivedStateFromProps -> render -> componentDidMount
    ****更新时候声明周期有:
    ****getDerivedStateFromProps -> shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componetDidUpdate
    vue 和 react 父子组件的生命周期顺序,都是以render为分界线,类似洋葱模型,因为render会触发子组件执行,父组件挂载完成的时候,子组件也会完成挂载:
    ****挂载:
    ****父constructor → 父render → 子constructor → 子render → 子componentDidMount → 父componentDidMount
    ****更新:
    **父getDerivedStateFromProps → 父shouldComponentUpdate → 父render → 子getDerivedStateFromProps → 子shouldComponentUpdate → 子render → 子getSnapshotBeforeUpdate → 父getSnapshotBeforeUpdate → 子componentDidUpdate → 父componentDidUpdate。
  2. 什么是纯函数什么是副作用函数

Vue

  1. 介绍一下Vue的生命周期 / 父子执行规律
    **答:beforeCreated: 有this, 但是没有数据,方法等, Created: 有this, 有数据方法等,没有el, beforeMounted: 调用render生成了虚拟dom,但是没有真实的dom, Mounted: , beforeUnmounted(beforeDestroy): 卸载之前,此时实例仍然可以访问,用于释放内存,定时器,取消请求,解绑事件等操作, Unmounted(Destroy): 实例销毁,监听器被移除
    ****beforeUpdate:数据变化之前,打补丁之前, Updated: 虚拟dom重新渲染并且应用之后
    **规律: 类似洋葱模型
  2. 组件销毁的时候一般会进行什么样的操作
    答:清除定时器,取消网络请求,消除dom引用,移除事件监听器,第三方库的销毁,框架本身的操作比如解除数据绑定,销毁观察者
  3. Vue编写时有哪些优化手段 / 是否做过 Vue 相关的性能优化
    **答:数据优化: Object.freeze(), 合理使用computed()
    ****组件优化: 懒加载() => import(), keep-alive, 函数组件
    其他: 图片懒加载v-lazy,事件合理使用防抖截流。v-show / v-if
    **
  4. 是否用过 Vue3 的 hooks
ini 复制代码
import { ref } from 'vue';

export function useFetch(url) {
  const data = ref(null);
  const error = ref(null);
  const loading = ref(false);

  const fetchData = async () => {
    try {
      loading.value = true;
      const response = await fetch(url);
      data.value = await response.json();
    } catch (err) {
      error.value = err;
    } finally {
      loading.value = false;
    }
  };

  return { data, error, loading, fetchData };
}
  1. Vue2 中的 mixin 是否了解,原理是什么?
    答:初始化的时候合并data, 生命期钩子: 全局mixin -> 组件mixin -> 组件钩子

  2. 在响应式上有什么区别?如何拦截 for...in / Proxy 和 Object.defineProperty 区别

    **答:vue2使用的是Object.defineProperty实现的,它只能劫持现有的属性,对于新增的属性必须用Vue.set添加才具有响应式。对于数组直接修改数组元素和修改其长度时无法触发更新,必须通过重写的的方法进行修改(push, pop, unshift, shift, splice, sclie, concat)。

    **vue3使用proxy实现的响应式,它是对整个对象进行代理的,可以监听到属性的删除和修改。数组可以监听下标或者长度的改变。

  3. Vue2 和 Vue3 有哪些区别

    Vue2 和 Vue3 的区别

    Vue2 和 Vue3 的区别,如何考虑使用哪一个

    Vue3 和 Vue2 的本质区别 / Vue3 和 Vue2 有什么区别?怎么把 Vue2 项目升级到 Vue3

    Vue2 和 Vue3 响应式的性能有区别吗?为什么

    介绍一下 Vue3 的新特性,相对 Vue2 来说

    defineProperty 有什么问题?

    Vue3 有什么其他的优化,比如静态节点的优化?

    **答:响应式系统,组合api,性能优化,TypeScript支持,新特性: Fragment, Teleport, Suspense。

    ****tree-shaking的支持,允许工具移除没有使用的功能,如v-model,体积约为10kb。

    ****底层diff算法优化

    **编译与性能优化: 静态节点提升,补丁标记,块状树

  4. Vue2 和 Vue3 的 diff 用了什么算法库

    2.0 和 3.0 算法差异速度提高多少有了解过吗

    详细说一下 Vue2 和 Vue3 diff 算法的详细过程。这两个算法的复杂度

    在 Diff 过程中怎么做的,Diff 算法有了解过吗

    通过下标指定 key 的话会有什么问题 / 这个问题出现在 diff 算法的哪个节点

    **答:vue2 和 vue3 是通过自研实现的diff算法。

    **其中vue2的算法是参考 **Snabbdom改进而来。

    ****vue2的算法是双端比较算法(双指针算法),通过四个指针同时从新旧节点的2端向中间遍历,经可能复用多的节点。

    ****vue3是通过跳过相同的前缀和后缀,通过map记录可以复用的旧节点,记住需要移动的位置。然后使用最长递增子序列确定需要最少移动的dom次数

    ****vue优化点: 编译时标记静态节点,跳过diff。标记动态绑定的属性,仅更新变化部分。以块为范围跟踪动态节点,减少遍历范围。

    **vue3比vue2 diff快了1.3 ~2 倍。减少50 - 70%的dom对比。跳过90%+的静态节点比较

  5. Vue 双向绑定原理 / 介绍一下 Vue 的主要原理

    **答:聊浅一点就是:

    ****vue2在初始化的时候,内部会执行initData函数,将数据属性经过Object.defineProperty处理转化成getter 和 setter。并且每一个属性有一个Dep(依赖管理)实例,用于存储所有的Watcher(如渲染Watcher)。当组件初始化时,会访问data所有属性,触发getter,收集该属性的所有的Watcher到dep中。当数据修改时触发setter。dep通知所有watcher进行更新。

    ****聊深一点:

    ****在我们修改数据时,为什么组件中的computed值会变,watcher方法会变,template中的值会重新渲染呢,是因为全部收集到了dep的watcher实例中。

    ****(可以给出自己思考)在看这部分源码时候,思考过为什么需要加入watcher实例这一层呢,为什么不可以直接把所有更新方法加入到dep中,等触发setter时候直接调用即可呢?

    ****回答: 因为watcher实例会提供异步批量更新调度机制,通过watch.id 做到去重,避免重复调用,通过watch.id 做到排序,确保父组件优先于组件更新,用户自身watcher函数优先于渲染watcher(updateComponent),然后再通过nextTick下个事件循环批量更新

    ****(聊优化)所以我们在写组件的时候,尽量避免不必要的data出现在template减少依赖收集,多使用computed减少计算。

    ****vue3:
    在初始化的时候,把被ref或者reactive包裹的数据进行响应式处理,ref 如果是基本类型会包裹为{ value: ... }形式,最终都是经过reative函数处理成响应式。处理后会进行proxy代理,有getter和setter操作。并且仅在访问对象时创建响应式代理,避免了递归初始化带来的性能消耗。在getter时会调用track来收集依赖,在修改时触发setter通过trigger来更新依赖。

    **

scss 复制代码
function ref(value) {
  const wrapper = {
    get value() {
      track(wrapper, 'value'); // 收集依赖
      return isObject(value) ? reactive(value) : value;
    },
    set value(newVal) {
      value = newVal;
      trigger(wrapper, 'value'); // 触发更新
    }
  };
  return wrapper;
}
  1. Proxy 和 Reflect 有什么关系

    **答:没有直接关系,只是Proxy的handle参数和Reflect一样的。vue3使用Reflect有几个好处:

    ****1. Reflect执行有返回结果

    ****2. Reflect执行报错不会影响后续代码执行

    **3. receiver参数具有不可替代性Reflect的receiver配合Proxy能更准确的获取目标对象的值而非代理的值

  2. Vue3 的 hook 和 React 的差别

    React hooks 和 Vue3 的 compose api 有什么区别

    **答: React的Hook是根据初始化时候形成的链表顺序来确定副作用的来源的,所以给出了以下限制:

    ****a. 不能在循环,条件,嵌套函数中调用HOOK

    ****b. 必须保证hook在顶层被调用

    ****c. useEffect等必须手动确定依赖关系

    ****而Compose Api是基于vue响应式系统实现的,与react hook相比

    ****a. 可以在条件, 循环, 嵌套函数中实现

    ****b. react hook需要手动触发更新,compose api可以自动依赖更新。

    **c. vue 是定向更新,react更新则是全量对比,GC比vue压力更大

  3. 处理一个对象 a.b. 是怎么处理的, Proxy 是怎么处理的。React 没有响应式,是怎么实现的 / Vue 或 React 中我们连续三次赋值,比如 this.a,会怎么实现

  4. KeepAlive 有用过吗?实现机制 / 如果一个组件既在 exclude 又在 include 会被缓存吗?为什么这么设计

    **答: keeplive实现原理: 获取组件vnode和key。vue2使用keys缓存组件的key,cache来缓存key对应的组件。当缓存组件个数大于max时,使用LRU策略来移除最近未使用的组件。

    ****vue3中使用keys数据结构为set, cache数据结构为map

    **当组件同时存在于exclude和include时,组件不会被缓存。因为exclude优先级高于include。这样设计更灵活,可以在包括诸多组件中排除特别的组件。

  5. Vue 的 nextTick 是用来做什么的?它的原理是什么 / nextTick 实现原理

    **答:用于dom更新之后执行延迟回调。

    ****原理是用:promise如果不支持使用MuationObserver, setImmedite, 最后是Settimeout.

    **vue3只用了promise,并没有做降级处理。

javascript 复制代码
const callbacks = []
let pending = false

function flushCallbacks() {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

let timerFunc
if (typeof Promise !== 'undefined') {
  const p = Promise.resolve()
  timerFunc = () => p.then(flushCallbacks)
} else if (typeof MutationObserver !== 'undefined') {
  // 降级处理...
} else {
  timerFunc = () => setTimeout(flushCallbacks, 0)
}

export function nextTick(cb, ctx) {
  callbacks.push(() => { cb.call(ctx) })
  if (!pending) {
    pending = true
    timerFunc()
  }
}
  1. Vue 中 $set 是做什么的。写一个 $set 的伪代码(写代码的时候用了 Object.create(null) ,问了一下它是做什么的)
    **答:当对象添加一个新的属性的时候,直接赋值的话,Vue无法检测到这个属性的变化,即没有响应式。使用$set可以确保该属性具有响应式,修改这个属性的值会触发试图更新
    **伪代码:
vbnet 复制代码
function $set(targe, key, value) {
    // 数组 不管是否是响应式数据 直接用重写方法修改数据
    if (Array.isArray(targe) && typeof key === 'number') {
        return targe.splice(key, 1, value)
    }
    if (key in targe) {
        target[key] = value
        return  
    }

    // 是否是响应式
    const ob = target.__ob__
    // 如果不是响应式 直接修改数据
    if (!ob) {
        target[key] = value
        return
    }
    
    defineReactive(toolbar.value, key, value)
    ob.dep.notify()
}

Object.create(null)创建的对象没有原型链

  1. Vue 中的数据状态管理用的什么?Vuex 的作用和用法 / pinia的作用和用法|
    **vue2 + Vuex
    ****vue3 + pina
    ****Vuex的作用:
    ****集中式管理状态,提供全局的store, 管理跨组件的共享状态。
    ****数据流的规范,通过state、mutation、actions确保状态变更的可预测性
    ****模块化:通过modules拆分状态逻辑
    ****Pinia的作用:
    ****代替Vuex,支持Compiose api,支持Typescript,模块化,简化api
    ****用法:


javascript 复制代码
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: { count: 0 },
  mutations: {
    increment(state) { state.count++ }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => commit('increment'), 1000)
    }
  },
  getters: {
    doubleCount: state => state.count * 2
  }
})

export default store
javascript 复制代码
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() { this.count++ },
    async incrementAsync() {
      setTimeout(() => this.increment(), 1000)
    }
  },
    getters: {
    doubleCount: (state) => state.count * 2
  },
})

// 使用composition API模式定义store
export const useCounterStoreForSetup = defineStore('counterForSetup', () => {
  const count = ref<number>(1);
  const doubleCount = computed(() => count.value * 2);
  function increment() {
    count.value++;
  }

  return { count, doubleCount, increment };
});
  1. Vue.extend 有用过吗?什么原理
    Vue.extend返回vue的子类,需要进行实例化。实例化以后就是该组件(传入extend的参数)的实例,需要进行挂载。将该实例放在Vue原型链上可以全局调用该实例上的方法。
php 复制代码
// 创建构造器
const MyComponent = Vue.extend({
  template: '<div>{{ message }}</div>',
  data() {
    return { message: 'Hello Vue!' };
  }
});

// 实例化并挂载
const instance = new MyComponent().$mount('#app');

原理:

ini 复制代码
Vue.extend = function (extendOptions) {
  const Super = this; // Vue 基类
  const Sub = function (options) {
    this._init(options);
  };
  Sub.prototype = Object.create(Super.prototype);
  Sub.prototype.constructor = Sub;
  // 合并选项(如 mixins、全局组件等)
  Sub.options = mergeOptions(Super.options, extendOptions);
  return Sub;
};
  1. Vue2 里面 data 为什么是一个 function
    答: 避免组件被多处引用data是引用同一个地址,导致数据混乱,使用function 可以每次生成不一样的地址引用
  2. Vue Router 的原理,hash 和 history 有什么区别 / 前端路由的实现方式 / Vue 的路由模式哪几种,实现原理。history 服务端需要做哪些配置
    **hash变化不会触发浏览器请求,路由完全由前端控制。通过window.onhashchange 监听哈希变化,触发组件渲染。内部通过维护一个历史栈,通过push和replace方法更新路由。hash模式不利于seo。
    ****history模式url改变会触发浏览器发送请求到后台,需要后端配合配置所有路由返回index.html交给前端控制路由。基于pushState,replaceState和popstate事件。window.location.pathname匹配React路由。
    **
  3. 你怎么看 React 和 Vue / 怎么看 Vue 和 React?

**答:(个人理解回答)React没有响应式,靠手动触发视图更新,每次从根节点遍历effect,进行更新。react有fiber架构,双缓存机制,时间分片,任务调度等

**vue具有响应式,颗粒度更细。

工程化

  1. Webpack 有做过 Loader 和 Plugin 吗? / Webpack 是否写过 Plugin 和 Loader / 有哪些loader

  2. 打包规则。首页加载少的js,splitChunks的规则。

  3. monorepo相关

  4. 说一下使用 Webpack 的优化

  5. Webpack 和 Vite,Vite 为什么快?

  6. Webpack 和 Vite 的构建流程有什么差异

    Webpack 和 Vite 热更新有什么区别

    Webpack 和 Vite 热更新区别

    Webpack 和 Vite 区别

  7. Eslint 代码检查的过程

  8. husky 的配置,配置过什么

  9. pm2 原理

  10. 微服务是否有了解过

  11. 模块联邦有什么优缺点?

  12. Webpack 如何实现热更新

  13. Babel 中的 stage0,1,2,3 是指什么

  14. Webpack 项目中通过 script 标签引入资源,在项目中如何处理

  15. 果打包100个文件,只有某个文件发生变化,有办法单独做更新吗

  16. Tree Shaking 的原理,CommonJS能用吗,Tree Shaking 有什么副作用吗

  17. Webpack 和 Vite 的特点和区别

  18. Vite 中你们怎么使用的模块联邦 / 有没有什么样式冲突的问题

  19. Webpack的初始化流程

  20. module,chunk,bundle 是什么意思

  21. 你可以说一下对 Webpack,Vite,EsBuild,SWC的理解,如何进行选择

  22. 打包速度慢怎么解决

Node.js

  1. NodeJS 里面的模块化是什么?
  2. 对 Node 是否有了解?如何读取一个 20G 大小的文件
  3. Node 中 master 和 slave 是否了解

Css

  1. CSS display 有哪些值、哪些用法?
    block, inline, inline-block, flex, inline-flex, grid, inline-flex
  2. 其中 inlineinline-block 什么区别?
    **inline被显示为内联元素,前后没有换行符,设置marign,padding,width,height不生效
    ****block会显示为块级元素,前后会有换行符
    ****inline-block是上面2者结合,前后没有换行符,设置margin,padding....会生效
    ****设置外部类型: inline 、block
    **内部显示类型: flex、grid
  3. 介绍一下 flex 布局 / flex 都有什么相关属性?flex 居中怎么实现?/ flex 实现两个元素垂直方向居中怎么实现?
    **display:flex设置容器内部子元素为flex布局
    ****作用在上面的属性有:
    ****flex-direction
    ****flex-wrap: 如何换行 wrap nowrap wrap-reverse
    ****flex-flow: 上面属性结合所以可以忽略
    ****justify-content: 主轴对齐方式 (主轴居中center)
    ****align-items: 交叉轴对齐方式 (交叉轴居中center)
    ****align-content: 与交叉轴对齐方式 flex-start flex-end center space-between space-around stretch
    ****作用在项目上的属性:
    ****order: 子元素排列顺序
    ****flex-grow: 子元素占据空间 默认0 不放大 都设置为1 等分剩余空间(grow: 扩大, 有剩余空间起作用)
    ****flex-shrink: 子元素缩小比例 默认是1 不缩放 (shrink: 缩小, 剩余空间不足起作用)
    ****flex-basis: 子元素占据的尺寸 默认auto 自身尺寸 (basis: 基础)
    **flex: 前面三个的结合
  4. flex: 1 代表什么?flex-basis可以设置哪些值,和 width 哪个优先级更高? / flex-basis是做什么的,和 width 哪个优先高 / flex:1 的含义
    **答: flex: flex-grow flex-shrink flex-basis
    ****flex: 1 表示 flex-grow: 1 flex-shrink: 1(默认值) flex-basis: 0%()
    ****flex:1 可以自动分配空间
    **flex如果第一个值是没有单位的数值则是flex-grow的值,如果是具体的单位值(px rem em ...)则是basis的值
  5. space-betweenspace-around 区别,是否能调整他们间隙的值?
    **space-between和space-around都是作用于主轴上的排列方式
    ****space-between让子元素均匀排列在主轴上,子元素的首位会紧贴父容器边缘。
    ****space-around也会让子元素均匀排列,每个元素间隙相同,导致中间间隙是首页间隙的2倍
    ****可以使用gap属性调整间隙,但是有兼容性问题
    **也可以使用margin
css 复制代码
.flex-container {
  display: flex;
  justify-content: space-between; /* 或 space-around */
  gap: 20px; /* 强制项目间固定间隙 */
}

.flex-item {
  margin-right: 20px; /* 自定义项目右侧间隙 */
}
  1. align-contentjustify-content 区别?
    **justify-content定义了主轴的对齐方式
    ****align-content定义了多根轴线(交叉轴)对齐方式,一根主线不会生效
    **一根轴线使用align-items 多根使用align-content
  2. position 定位的方式。说一下 sticky。
    **position定位: static, relative, position, fixed, sticky
    **sticky是粘性布局
css 复制代码
#element {
  position: sticky;
  top: 0; /* 当页面滚动到这个元素顶部与视口顶部重合时,它将被"粘住" */
}
  1. less 和 sass 什么区别?
    less更接近css,sass更快
less 复制代码
// less
.border-radius(@radius) {
  border-radius: @radius;
}
.box { .border-radius(5px); }
less 复制代码
// sass 支持参数、默认值、条件判断(@if)和循环(@for, @each),功能更强大。
@mixin border-radius($radius, $color: red) {
  border-radius: $radius;
  @if $radius > 10px { border-color: $color; }
}
.box { @include border-radius(15px, blue); }
  1. flex 中如果有剩余元素或者空间不够会怎么办

**有剩余空间会根据flex-basis / width + flex-grow计算出的扩大量进行扩大

**如果空间不够,如果设置换行flex-wrap: wrap会换行,如果不换行,会根据flex-shrink比例进行缩放,如果flex-shrink为0 可能会超出元素

Http

  1. HTTP 缓存,协商缓存,强缓存。 / 协商缓存如何判断命不命中?/ 和缓存相关的有哪些?强缓存和协商缓存同时存在哪个优先级高
    **答: http缓存分强缓存和协商缓存
    ****首先强缓存主要是本地缓存,不会像服务器发送请求。它的实现方式涉及有Expires 和 Cache-Control这2个http字段。
    **Expires是旧的实现方式,格式: Wed, 21 Oct 2025 07:28:00 GMT **,会存在客服端和服务端时间不一致的问题。
    ****Cache-Control是http 1.1引入的,可以用max-age设置相对值,比如max-age=3600 就是一个小时后过期。Cache-Control有如下字段: max-age,资源在相对时间内有效,no-cache: 跳过强缓存直接进入协商缓存。
    ****no-store: 禁止任何缓存,包括协商缓存。public: 允许进行代理缓存。private:仅允许客户端进行缓存
    ****然后就是协商缓存,当强缓存失效以后,客服端会像服务端发送一个请求,验证资源是否可用。这个时候会验证2个字段Last-Modified和Etag。
    ****Last-Modified是资源的最后修改时间,而Etag是资源的唯一标识。
    ****Last-Modified会存在精度不够的问题,比如秒级,同一秒多次修改则无法识别。
    ****Etag问题在于会增加服务器负担,特别是大文件
    ****策略:
    **静态资源(如JS/CSS/图片):
    • 文件名添加哈希(如 app.a1b2c3.js ),设置 Cache-Control: max-age=31536000 (一年)。
    • 内容变化后哈希改变,URL不同,自然触发更新。
    1. HTML文件
      • 设置 Cache-Control: no-cache max-age=0 ,确保及时获取最新版本。
    1. API请求
      • 对数据实时性要求高的接口,使用 Cache-Control: no-store ****
  1. HTTP1.1和HTTP2的区别?(我回答提到了哈夫曼编码的方式实现首部压缩)/ HTTP1.1和HTTP2的区别 / HTTP1.1 和 HTTP2 做了什么?队头阻塞有没有了解过,在 HTTP2 中有这个问题吗 / 浏览器对队头阻塞有什么优化?

    **答:window.chrome.loadTimes(), npnNegotiatedProtocol 查看http的版本

    ****HTTP1.1和HTTP2的本质区别在于协议格式的区别导致。

    ****HTTP1.1协议格式是基于文本,HTTP2.0是基于二进制帧进行传输。这导致处理传输数据方式不一样,HTTP1.1会不断接受一个请求的字节数据,知道遇到clrf分隔符停止,这导致HTTP1.1必须要按顺序去处理每一个请求。如果一个请求时间过长就会导致后面请求卡住,导致队头阻塞问题。

    ****HTTP2.0则是允许在同一个tcp连接中同时发起多个请求,请求和响应的数据会分解成帧,每一帧都有流标识。这样流可以交错传输,互不干扰。即可多路复用。解决了队头阻塞问题。

    ****HTTP2.0还进行了头部压缩优化,采用了HPACK算法压缩头部(静态表,动态表,哈夫曼编码进行压缩)

    ****服务器端还能主动推送消息。还可以对请求进行优先级、流量控制。

    **队头阻塞:HTTP2.0只是解决了应用层的阻塞,传输层还是按照顺序进行数据传输存队头阻塞问题。

  2. 哈夫曼编码的原理。

    **答:基于HTTP2的哈夫曼编码原理。哈夫曼编码分动态和静态哈夫曼编码,HTTP2哈夫曼是基于静态哈夫曼编码,即根据预先统计的频率构建一颗树,同时存在编码器和解码器2端。则无需在http中传输。

    ****然而哈夫曼算法原理就是会根据字符串出现的频率进行统计,然后构建一颗按频率生序排列的队列。重复取出频率最小的2个节点,可以看出是树的左右节点,依次合并成一个根节点,最终可以形成一颗哈夫曼树。

    ****然后就是分配编码,节点左侧为0 右侧为1, 编码规律就是从根节点到某一个节点的路径就是其编码。
    一个字符8位,现在进行编码以后成1位或者3位。到达压缩的效果。

    **

scss 复制代码
       根(11)
     /       \
   A(5)    新(6)
           /   \
       新(4)   新(2)
      /   \     / \
    B(2) R(2) C(1) D(1)
  1. 浏览器接收到HTML到渲染页面的过程是什么? / 输入url到渲染页面背后发生了什么事情?
    **答: 一,首先是将HTML转化为dom树,与此同时预解析线程会扫描字符串html,把发现的静态资源如css,图片等资源交给网络线程进行并行下载,并行下载操作可以加快整体的速度。在解析dom过程中,如遇到script标签会停止解析,等到script下载完成并且执行完成以后再继续进行解析,这是由于js 可能动态操作dom元素。
    ****二,dom树解析完成以后接下来是样式计算最终结果称为compted style,这个过程会解析默认样式、外部样式、行内样式,很多预设值会变成绝对值,比如red会变成rgb格式,rem em变成px。最后生成一颗带有样式的dom树
    ****三,接着是进行布局layout,遍历每一个dom节点根据其盒子模型计算几何信息,包过节点的宽高,和位置信息,完成后会生成一颗layout tree。
    四,接着会进行分层,根据z-index, transform, opacity等属性创建图层树,这样做的好处是将来某一个图层改变,只需要修改该图层,提高效率。分层完成以后是进行绘制,生成各个图层的绘制指令,记录绘制顺序。( 分层隔离机制减少重绘范围)
    ****接下来是合成线程完成步骤:
    ****五,分块,将每个图层进行分块,多个分块线程同时进行,分成256 * 256 或者 512 * 521的块,优先绘制可视区域的图块。
    ****六,分块完成以后是光栅化, GPU开启多个线程光栅化,结果就是多个块的页面。
    **七, 接着就是合成,完整的网页
  2. 域名解析之后如何找到目标主机
  3. 输入url到渲染页面背后发生了什么事情?
  4. HTTP 103 / 301 / 302 / 204 / 206是什么意思?
    **答:103表示服务收到部分请求,等待剩余部分,通常用于分块请求,比喻http1.1的分块传输编码
    ****301永久重定向
    ****302临时重定向
    ****204表示服务器收到请求不需要返回内容,比如发送邮件、删除资源
    **206表示服务器成功处理了部分请求,比如:视频请求一部分内容
  5. HTTP的请求头你知道的有哪些。
    • 基础控制HostUser-AgentAcceptAccept-Encoding
    • 缓存管理Cache-ControlIf-Modified-SinceIf-None-Match
    • 内容协商Content-TypeContent-Length
    • 身份认证AuthorizationCookie
    • 跨域处理OriginAccess-Control-Request-*
    • 安全与代理X-Forwarded-ForSec-Fetch-*
  1. 使用 HTTPS 一定是安全的吗

    HTTPS如何保证安全性

    HTTP 和 HTTPS有什么区别?

    **答:非对称加密连接,对称加密进行内容传输。

    ****客户端会发送一个请求给服务端支持哪些hash算法.

    ****服务端会把信息以数字信息格式返回给客户端,包含:证书内容有公钥,网站地址,证书颁布机构,失效日期。

    ****客服端会验证证书的合法性,是否过期,请求的地址是否正确。

    ****验证通过后客服端会随机生成一个随机的对称密钥,并用公钥进行加密。服务端用对应的私钥进行解密。拿到随机密钥,用于之后信息对成加密

    ****也不是绝对安全:


  2. 有了解过 HTTP3 吗?为'用 UDP

    HTTP3的协议一定是TCP

    UDP 协议有什么优点?

  3. 大文件断点下载。
    答: 核心在于记录文件的下载进度,并通过HTTP协议的 Range**头部实现对文件指定范围的请求

    **Range: bytes=200-1000

编程题

  1. 限制函数并发数的题目,大概说一下,具体题目记不清了。

// countLimit 是一个函数,执行fn,执行的并发度是 2,返回一个 Promise

let countLimit = pLimit(fn, 2)

countLimit(a) // 立即执行

countLimit(b) // 立即执行

countLimit(c) // 前两个函数执行完再执行

// 求实现函数 pLimit

  1. 编程题:扁平结构转嵌套结构
  2. 编程题:判断一个只包含左右括号的字符串中,括号是否匹配?
  3. 编程题:一个数组中找到和等于指定值target的两个元素,并输出他们的下标
  4. 编程题:实现防抖和节流,平时我写的都是很简单的,面试官特意要求的加上对上下文的处理
  5. 编程题:给定由 []{}() 组成的字符串,判断括号是否正确匹配。
  6. 构造两个以整型数字为值的链表,其中的值是单调递增的。将两个链表合并,保持递增。要求空间复杂度 O ( 1 )
  7. 实现一个深拷贝,拷贝对象,要考虑循环引用
  8. 生成一个随机数,4-6,保留两位有效数字
  9. 代码题,已知数据格式,实现一个函数 fn ,给一个 id 找出链条中其对应的所有的父级 name (用DFS写了一遍)能用广度优先写一遍吗
  10. 实现一个获取一个对象嵌套属性的函数
  11. 每次获取一个对象的属性都会打印 获取对象xxx的xxx,比如获取 obj.a.b,怎么实现
  12. juejin.cn/post/746364...

算法

  1. 算法题,股票买卖求最大收益,只买卖一次
  2. 找一个字符串中最长的不含重复字符的子串

性能优化

  1. 如何优化一个网站的性能
  2. 如何在前一个页面对下一个页面进行优化
  3. 项目中请求的错误处理是怎么做的?
  4. 如果有多个错误如何保证只触发一次弹窗?(防抖)
  5. 上线后怎么提示用户刷新当前页面
  6. 项目有什么监控,比如性能类的。(回答了sentry)
  7. sentry推送信息是什么方式?
  8. 页面上的性能优化

其他

  1. 问了一个小项目的具体做了什么。
  2. 怎么做技术选型
  3. 页面数据,表格特别多的时候,是怎么解决的。
  4. 编辑表格更新是怎么实现的
  5. 介绍项目,问了一些项目细节,项目是如何推进的。
  6. 项目排期的管控。
  7. 排期和手头工作冲突怎么做?平时主要工作内容
  8. 面试中有遇到过什么比较好的问题?为什么?(emmm)
  9. 发版是怎么做的?
  10. 有做过什么基建工具?
  11. 最近有了解哪些新技术吗
  12. 介绍下项目A
  • 项目有什么效果,如何判断效率提升率?(本地测还是统计数据发送到服务端?)
  • 项目后续规划,现在有什么问题?风险怎么处理?
  • 项目中有什么是收获很多的?
  • 推广过程中遇到什么困难?
  • 重新做一遍你觉得有哪些你觉得可以做得做好的事情?
  • 用到一些插件的原理
  1. 平时怎么学习前端技术?
  2. 如何保证项目的正确性,稳定性
  3. 你认为一个五年的前端工程师应该需要哪些能力?你哪方面做的好或不好
  4. 说一个挑战比较大的事情和项目。然后具体问了项目的事情。
  • 还有什么有亮点的项目。
  • 如果用户白屏但是你的电脑是正常的,你要怎么处理?
  • 项目是怎么部署的?怎么监听服务,怎么灰度上线。
相关推荐
逆袭的小黄鸭1 分钟前
理解 JavaScript 的 this:核心概念、常见误区与改变指向的方法
前端·javascript·面试
lemonzoey6 分钟前
用Web Worker优化大文件上传——告别页面卡顿!
前端·javascript
Json_8 分钟前
Vue 内置组件 -slot讲解
前端·vue.js·深度学习
祯民18 分钟前
《生成式 AI 应用开发:基于 OpenAI API 开发》实体书上架
前端·aigc·openai
bigyoung23 分钟前
ts在运行时校验数据类型的探索
前端·javascript·typescript
独立开阀者_FwtCoder27 分钟前
深入解密Node共享内存:这个原生模块让你的多进程应用性能翻倍
前端·javascript·后端
Json_29 分钟前
使用JS写一个用鼠标拖动DIV到任意地方
前端·javascript·深度学习
祯民33 分钟前
阿民解锁了"入职 30 天跑路"新成就
前端·面试
昌平第一王昭君35 分钟前
一个简单的虚拟滚动
前端
Json_36 分钟前
jQuery选项卡小练习
前端·深度学习·jquery