真实面试记录按知识架构分类记录

80 阅读41分钟

HTML

IE盒子和普通盒子有什么区别

IE盒模型(又称"怪异盒模型")与标准盒模型(W3C盒模型)在计算元素总宽度和高度时有显著差异。

image.png

a标签的合法target属性?

_self _blank _parent _top target 属性还可以使用自定义名称

CSS

Css引入的方式有哪些?

<!-- 内联样式 -->
<p style="color: red; font-size: 16px;">这是一个使用内联样式的段落。</p>

 <!-- 内部样式表 -->
<style>
    p {
        color: blue;
        font-size: 18px;
    }
</style>
<!-- 外部样式表 -->
<link rel="stylesheet" href="styles.css">

style.css文件

<style>
        /* 导入样式表 */
        @import url('imported.css');
    </style>

inset代表什么属性的缩写?

用于定位,可以用px或百分比等设置定位的4个偏移量。可应用于任何writing-mode属性。
用法:inset:length|percentage|auto|inherit继承|initial默认|unset;
顺序同padding:也是上右下左

.child{
    position:absolute;             
    inset:10px 40px 30px 0px;
}

flex:1;代表哪几个属性的缩写?

flex: 1; 主要是用来告诉 Flex 容器中的项目,如果有剩余空间,它们应该等比例地放大,如果空间不足,它们也应该等比例地缩小,而它们的初始大小(在没有额外空间或需要缩小的情况下)是 0%(尽管这通常不是预期的行为,特别是当你想让项目根据其内容大小开始时)。为了更精确地控制项目的大小和伸缩行为,你可能会想使用更明确的 flex 属性值,比如 flex: 1 1 auto;
等同于:
flex-grow:1; 放大比例
flex-shrink:1; 缩小比例
flex-basis:0%;平分剩余空间%,

Flex: auto 与下列哪项效果相同?

Flex: 1 1 auto

css优先级排序?伪类排哪里 ?

  1. !important最高(ie6不支持)
  2. 内联样式,标签加style
  3. ID选择器
  4. 类.class、伪类:hover、属性[title]-[title^=a]
  5. 元素,伪元素::before
  6. 通配符*

 css设置100%,是相对什么?

是相对父元素的,浮动后也是相对自身的;如果有soblute定位就是相对第一个定位父级;fixed定位是相对窗口;margin是相对自身。

css实现一个容器,高度超出视窗就固定高度满屏,不超出视窗则500px固定高度

//方法1
.container {
  min-height: 500px; /* 最小高度 500px */
  max-height: 100vh; /* 最大高度为视窗高度 */
  height: auto; /* 自动调整 */
  overflow-y: auto; /* 超出时滚动 */
}

svg和canvas的区别

都是H5支持的2种可视化技术,都可以在画布上绘制和放入图片。

  1. canvas是h5标签,可用number设置标签的宽高,用js的getContext('2d')作为画笔进行绘制。
  2. svg可以缩放矢量图,做标签用时stroke可修改颜色。
canvasSVG
绘制格式getContext绘制依赖分辨率,能以.png和.jpg保存为图片,可以说是位图h5中直接绘制矢量图
事件处理器不支持,图像是canvas的一部分,不能用js获取图形支持
工作机制围逐像素渲染,绘制完就结束svg通过DOM操作显示
适用范围有许多对象要被频繁重绘的图形密集型游戏有大型渲染区域的,如地图

如何写一个0.5px的线

  1. transform scaleY
.half-px-line {
        transform: scaleY(0.5);
      }

2. 使用svg矢量图

<svg height="1" width="100%">
  <line x1="0" y1="0.5" x2="100%" y2="0.5" stroke="#000" stroke-width="0.5" />
</svg>

display有哪些属性

  • block 块级元素 将元素显示为块级元素,元素会独占一行,并且可以设置宽度和高度。常见的块级元素有 <div><p><h1> - <h6> 等。
  • inline-block
    元素会像内联元素一样排列在一行,但又可以设置宽度和高度,同时保留块级元素的部分特性。
  • list-item
    将元素显示为列表项,通常会带有列表项标记(如圆点、数字等)。
  • inline
    元素会以内联元素的形式显示,不会独占一行,宽度和高度由内容决定,并且无法设置宽度和高度。常见的内联元素有 <span><a><img> 等。
  • table
    将元素显示为表格,类似于 <table> 标签。
  • table-row
    将元素显示为表格的行,类似于 <tr> 标签。
  • table-cell
    将元素显示为表格的单元格,类似于 <td> 标签。
  • flex
    将元素显示为弹性容器,其子元素会成为弹性项目,可以使用弹性布局的相关属性来控制子元素的排列和对齐方式。
  • inline-flex
    类似于 flex,但元素会以内联元素的形式显示。
  • grid
    将元素显示为网格容器,其子元素会成为网格项目,可以使用网格布局的相关属性来控制子元素的排列和对齐方式。
div {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 10px;
}
  • inline-grid
    类似于 grid,但元素会以内联元素的形式显示。
div {
    display: inline-grid;
    grid-template-rows: repeat(2, 1fr);
}
  • none
    元素不会在页面中显示,并且不会占用页面空间。
  • contents
    元素本身不会产生任何框,但其子元素会正常显示,就好像该元素不存在一样。
  • run-in
    元素会根据上下文和周围元素的情况,以块级元素或内联元素的形式显示。不过该属性的兼容性较差,使用时需谨慎。

如何写个居中的样式

在父元素上设置 text-align: center 来实现行内元素或行内块级元素的水平居中。

块元素设置左右外边距为 auto 来实现块级元素的水平居中。

单行文本的垂直居中,可以通过设置元素的 line-height 等于元素的 height 。

display: flex;
            align-items: center;
justify-content: center;
.child {
            width: 200px;
            height: 100px;
            position: absolute;
            top: 50%;
            left: 50%;
            margin-top: -50px;
            margin-left: -100px;
            background-color: lightblue;
        }

css函数有哪些

1. 值与单位函数
  • calc()  - 动态计算值 width: calc(100% - 50px);

  • min()  - 取最小值 width: min(100vw, 1200px);

  • max()  - 取最大值 font-size: max(1rem, 12px);

  • clamp()  - 限制在范围内 font-size: clamp(1rem, 2.5vw, 2rem);

2. 颜色函数
  • rgb()/rgba()  - RGB颜色 color: rgb(255, 0, 0); background: rgba(0, 0, 255, 0.5);

  • hsl()/hsla()  - HSL颜色 color: hsl(120, 100%, 50%);

  • color-mix()  - 颜色混合 color: color-mix(in srgb, red 30%, blue 70%);

3. 渐变函数
  • linear-gradient()  - 线性渐变 background: linear-gradient(45deg, red, blue);

  • radial-gradient()  - 径向渐变 background: radial-gradient(circle, red, yellow, green);

  • conic-gradient()  - 锥形渐变

    background: conic-gradient(red, yellow, lime, aqua, blue, magenta, red);
    
4. 图形变换函数
  • rotate()  - 旋转 transform: rotate(45deg);

  • scale()  - 缩放 transform: scale(1.2);

  • translate()  - 位移 transform: translate(50px, 100px);

  • matrix()  - 矩阵变换 transform: matrix(1, 2, 3, 4, 5, 6);

5. 滤镜函数
  • blur()  - 模糊 filter: blur(5px);

  • brightness()  - 亮度 filter: brightness(0.8);

  • contrast()  - 对比度 filter: contrast(200%);

10. 其他实用函数
  • var()  - 使用CSS变量 color: var(--main-color);

  • attr()  - 获取HTML属性 content: attr(data-tooltip);

  • url()  - 引用资源 background-image: url("image.jpg");

image.png

rem和px有什么区别

  • px(像素) :它属于固定的物理单位,一个 px 就代表屏幕上的一个物理像素点。在不同设备上,px 所代表的实际尺寸是固定的。比如,设置一个元素的宽度为 100px,那在任何设备上它都会占据 100 个物理像素的宽度。
  • rem(根元素字体大小的倍数)rem 是相对单位,它基于 HTML 根元素(<html>)的字体大小来计算。要是根元素的字体大小为 16px,那么 1rem 就等同于 16px;若根元素字体大小变为 20px1rem 就等于 20px

浏览器

页面渲染经历了哪些过程

  1. 解析阶段(Parsing)
    HTML解析:

浏览器从上到下解析HTML文档

构建DOM(Document Object Model)树

遇到

CSS解析:

解析所有CSS文件(外部、内部、行内样式)

构建CSSOM(CSS Object Model)树

解析过程会阻塞渲染(CSS是渲染阻塞资源)

  1. 样式计算(Style Calculation) 将CSS规则应用到DOM节点上

计算每个DOM节点的最终样式(继承、层叠、优先级)

生成带有样式的DOM树(Render Tree的基础)

  1. 布局/回流(Layout/Reflow) 计算每个可见元素在视口中的确切位置和大小

从根节点开始递归计算几何信息

创建布局树(Layout Tree),包含位置、尺寸等信息

首次布局称为"布局",后续改变称为"回流"

  1. 分层(Layer) 浏览器将页面分为多个图层(Layer)

特定元素会创建新图层(如transform、z-index元素)

分层可以提高渲染效率(单独绘制和合成)

  1. 绘制(Paint) 将每个图层的节点转换为屏幕上的实际像素

执行栅格化(Rasterization)过程

生成绘制指令列表(Paint Records)

  1. 合成与显示(Composite & Display) 将各图层按正确顺序合成(Compositing)

通过GPU加速合成过程

最终显示在屏幕上

JavaScript

js数据类型

  • 基本数据类型:string、number、boolean、undefined、null、symbol、bigint
  • 引用数据类型:Object[array、object、function、dateRegExp]
  • “undefined”X“not defined” undefined表示变量未赋值、对象的属性不存在,是js数据类型之一,合理存在。 not defined表示变量未声明就被使用了,属于报错信息。

判断数据类型的方法有哪些?

  1. typeof,直接返回基础类型 可区分 null之外的原始类型,例:typeof null //object
  2. instanceof,返回布尔值 不能判断基础类型,(结果不可靠,因为symbol可以直接修改Symbol.hasInstance覆盖默认性能为)。例:
var num = new Number() 
num innstanceof Number // true
  1. Object.prototype.toString.call()
  2. Array.isArray()判断是否数组

typeOf

// 1. 数值类型 (number)
// 整数
const integerNumber = 42;
console.log(`typeof ${integerNumber} 结果: ${typeof integerNumber}`); // 输出: typeof 42 结果: number

// 浮点数
const floatNumber = 3.14;
console.log(`typeof ${floatNumber} 结果: ${typeof floatNumber}`); // 输出: typeof 3.14 结果: number

// NaN
const notANumber = NaN;
console.log(`typeof ${notANumber} 结果: ${typeof notANumber}`); // 输出: typeof NaN 结果: number

// Infinity
const infinityNumber = Infinity;
console.log(`typeof ${infinityNumber} 结果: ${typeof infinityNumber}`); // 输出: typeof Infinity 结果: number

// 2. 字符串类型 (string)
const stringValue = 'Hello, World!';
console.log(`typeof ${stringValue} 结果: ${typeof stringValue}`); // 输出: typeof Hello, World! 结果: string

// 3. 布尔类型 (boolean)
const trueValue = true;
const falseValue = false;
console.log(`typeof ${trueValue} 结果: ${typeof trueValue}`); // 输出: typeof true 结果: boolean
console.log(`typeof ${falseValue} 结果: ${typeof falseValue}`); // 输出: typeof false 结果: boolean

// 4. 未定义类型 (undefined)
let undefinedVariable;
console.log(`typeof ${undefinedVariable} 结果: ${typeof undefinedVariable}`); // 输出: typeof undefined 结果: undefined

// 5. 空值类型 (null)
const nullValue = null;
// 注意:typeof null 返回 'object',这是 JavaScript 语言的一个历史遗留问题
console.log(`typeof ${nullValue} 结果: ${typeof nullValue}`); // 输出: typeof null 结果: object

// 6. 符号类型 (symbol)
const symbolValue = Symbol('example');
console.log(`typeof ${symbolValue.toString()} 结果: ${typeof symbolValue}`); // 输出: typeof Symbol(example) 结果: symbol

// 7. 引用数据类型
// 对象
const objectValue = { key: 'value' };
console.log(`typeof ${JSON.stringify(objectValue)} 结果: ${typeof objectValue}`); // 输出: typeof {"key":"value"} 结果: object

// 数组
const arrayValue = [1, 2, 3];
console.log(`typeof ${arrayValue} 结果: ${typeof arrayValue}`); // 输出: typeof 1,2,3 结果: object

// 函数
function exampleFunction() {
    return 'Function result';
}
console.log(`typeof ${exampleFunction.name} 结果: ${typeof exampleFunction}`); // 输出: typeof exampleFunction 结果: function
    

如何判断一个字符串中是否包含某个字符?

// 返回 `true` 或 `false`。
str.includes(searchString, position?);  

// 返回查找到的index下标,或者-1
str.indexOf(searchString, position?);

// search正则,类似indexOf,返回index下标或-1
str.search(/world/i)

regex.test(str);  //正则匹配,返回布尔值
str.match(regexp);  //正则匹配,返回匹配正则表达式的子串数组,未找到则返回 `null`

写个方法取0~10随机整数

  • Math.random() 生成 [0, 1) 的随机浮点数(包含 0,不包含 1)
  • 【0,10】要向下取整才有0,那么*11才能取到10
  • Math.floor() 向下取整,得到 0, 1, 2, ..., 10
Math.floor(Math.random() * 11); // 0~10

数组的方法有哪些?增删改查、循环等

菜鸟官网有个列表,很全www.runoob.com/jsref/jsref…

  • 伪数组:有数组的结构,没有push、pop等属性
  • 伪数组->数组:Array.from(arr)
  • 改变原数组有9个: pop, push, shift, unshift, reverse, splice, sort, copywithin, fill
  • 不改变原数组有8个: join, toLowcaleString, toString, slice, cancat, indexOf, lastIndexOf, includs
  • 遍历12个: forEach遍历, map操作数组每一个元素并返回新数组, every是否都符合, some判断有无符合的, filter过滤符合的,find查询符合条件的元素, reduce累加, reduceRight, 查询符合条件的元素的下标:findIndex, keys, values, entries
    一. 改变原数组的9个
// 增&删&改
1. push增尾 --- 2. pop删尾
3. unshift增头 --- 4. shift删头

5. reverse --- 颠倒数组中元素的顺序(arr.reverse();)
6. splice --- splice(index, length, 替换的item1, item2...)删除并返回删除元素是数组,未删除则返回空数组[]
7. sort --- 排序,按字母或数字
    sort(function(a, b) {
        return a - b
    }) //数组排序
    sort(function(a, b) {
        return b - a 
    }) //数组排序
8. copyWithin --- 从数组的指定位置拷贝元素到数组的另一个指定位置中。 
    myArr.copyWithin(targetIndex, start, end):  (修改原数组内容,length不变)
9. fill --- 使用一个固定值来填充数组。Array.fill(value, [start, end])  修改数组,用value替换原数组内容

二. 不改变原数组有8个

  1. join
    定义:将数组的所有元素连接成一个字符串,可以指定分隔符。
    语法arr.join([separator])
const fruits = ['Apple', 'Banana', 'Orange'];

console.log(fruits.join());      // "Apple,Banana,Orange"
console.log(fruits.join('-'));  // "Apple-Banana-Orange"
console.log(fruits.join(' '));  // "Apple Banana Orange"
console.log(fruits.join(''));   // "AppleBananaOrange"
  1. toLowcaleString
    定义:返回一个表示数组元素的字符串,元素使用各自的 toLocaleString 方法转换为字符串。
    语法arr.toLocaleString([locales[, options]])
const numbers = [123456.789, new Date()];
const prices = [1234.56, 7890.12];

console.log(numbers.toLocaleString('en-US'));
// "123,456.789,5/10/2023, 3:25:45 PM"

console.log(prices.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' }));
// "1.234,56 €,7.890,12 €"
  1. toString
    定义:返回一个表示数组的字符串,元素用逗号分隔。
    语法arr.toString()
const arr = [1, 2, 'a', '1a'];
console.log(arr.toString());  // "1,2,a,1a"
  1. slice
    定义:返回一个新的数组对象,包含从开始到结束(不包括结束)的浅拷贝。
    语法arr.slice([begin[, end]])
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
console.log(animals.slice(2));    // ["camel", "duck", "elephant"]
console.log(animals.slice(2, 4)); // ["camel", "duck"]
console.log(animals.slice(1, 5)); // ["bison", "camel", "duck", "elephant"]
console.log(animals.slice(-2));   // ["duck", "elephant"]
console.log(animals.slice(2, -1));// ["camel", "duck"]
  1. cancat
    定义:用于合并两个或多个数组,返回一个新数组。
    语法arr.concat(value1[, value2[, ...[, valueN]]])
const arr1 = ['a', 'b', 'c'];
const arr2 = ['d', 'e', 'f'];
const arr3 = arr1.concat(arr2);
console.log(arr3);  // ["a", "b", "c", "d", "e", "f"]

// 也可以合并值
console.log(arr1.concat(1, [2, 3]));  // ["a", "b", "c", 1, 2, 3]
  1. indexOf
    定义:返回在数组中可以找到给定元素的第一个索引,如果不存在则返回-1。
    语法arr.indexOf(searchElement[, fromIndex])
const beasts = ['ant', 'bison', 'camel', 'duck', 'bison'];

console.log(beasts.indexOf('bison'));      // 1
console.log(beasts.indexOf('bison', 2));   // 4
console.log(beasts.indexOf('giraffe'));    // -1
  1. lastIndexOf
    定义:返回指定元素在数组中的最后一个索引,如果不存在则返回-1。
    语法arr.lastIndexOf(searchElement[, fromIndex])
const animals = ['Dodo', 'Tiger', 'Penguin', 'Dodo'];

console.log(animals.lastIndexOf('Dodo'));    // 3
console.log(animals.lastIndexOf('Tiger'));   // 1
console.log(animals.lastIndexOf('Giraffe')); // -1
  1. includs
    定义:判断数组是否包含某个元素,返回布尔值。
    语法arr.includes(searchElement[, fromIndex])
const array1 = [1, 2, 3];

console.log(array1.includes(2));      // true
console.log(array1.includes(4));      // false
console.log([1, 2, NaN].includes(NaN)); // true

// 从索引2开始查找
console.log([1, 2, 3].includes(1, 1));  // false

三、 遍历数组12个

  1. forEach
    定义:对数组的每个元素执行一次提供的函数。
    语法arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
const fruits = ['apple', 'banana', 'orange'];

fruits.forEach((fruit, index) => {
  console.log(`${index}: ${fruit}`);
});
// 输出:
// 0: apple
// 1: banana
// 2: orange
  1. map
    定义:创建一个新数组,其结果是该数组中的每个元素调用一次提供的函数后的返回值。
    语法arr.map(callback(currentValue [, index [, array]])[, thisArg])
const numbers = [1, 2, 3];
const doubled = numbers.map(num => num * 2);

console.log(doubled); // [2, 4, 6]

3.filter
定义:创建一个新数组,包含通过函数要求的所有元素。
语法arr.filter(callback(element [, index [, array]])[, thisArg])

const numbers = [1, 2, 3, 4, 5];
const evens = numbers.filter(num => num % 2 === 0);

console.log(evens); // [2, 4]
  1. reduce累加
    定义:返回单个值,对数组中的每个元素执行一个reducer函数,将其结果汇总为单个返回值。
    语法arr.reduce(callback(accumulator, currentValue [, index [, array]])[, initialValue])
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((acc, curr) => acc + curr, 0);

console.log(sum); // 10
  1. reduceRight
    定义:与reduce()类似,但从右向左执行。
    语法arr.reduceRight(callback(accumulator, currentValue [, index [, array]])[, initialValue])
const numbers = [1, 2, 3, 4];
const result = numbers.reduceRight((acc, curr) => acc - curr);

console.log(result); // -2 (计算方式: 4 - 3 - 2 - 1)
  1. find
    定义:返回数组中满足提供函数要求的第一个元素的值,否则返回undefined。
    语法arr.find(callback(element [, index [, array]])[, thisArg])
const numbers = [5, 12, 8, 130, 44];
const found = numbers.find(num => num > 10);

console.log(found); // 12

7. findIndex
定义:返回数组中满足提供的测试函数的第一个元素的索引,否则返回-1。
语法arr.findIndex(callback(element [, index [, array]])[, thisArg])

const numbers = [5, 12, 8, 130, 44];
const index = numbers.findIndex(num => num > 10);

console.log(index); // 1

8. some
定义:返回布尔值,数组中是否至少有一个元素符合函数的要求。
语法arr.some(callback(element [, index [, array]])[, thisArg])

const numbers = [1, 2, 3, 4, 5];
const hasEven = numbers.some(num => num % 2 === 0);

console.log(hasEven); // true
  1. every
    定义:返回布尔值,数组中是否所有元素都通过了函数的测试。
    语法arr.every(callback(element [, index [, array]])[, thisArg])
const numbers = [2, 4, 6, 8, 10];
const allEven = numbers.every(num => num % 2 === 0);

console.log(allEven); // true

10. for...of
定义:创建一个循环来迭代可迭代对象(包括数组)。
语法for (variable of iterable) { statement }

const fruits = ['apple', 'banana', 'orange'];

for (const fruit of fruits) {
  console.log(fruit);
}
// 输出:
// apple
// banana
// orange

11. entries
定义:返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对。
语法arr.entries()

const fruits = ['apple', 'banana', 'orange'];
const iterator = fruits.entries();

for (const [index, fruit] of iterator) {
  console.log(`${index}: ${fruit}`);
}
// 输出:
// 0: apple
// 1: banana
// 2: orange

12. keys
定义:返回一个新的Array Iterator对象,它包含数组中每个索引的键。
语法arr.keys()

const fruits = ['apple', 'banana', 'orange'];
const keys = fruits.keys();

for (const key of keys) {
  console.log(key);
}
// 输出:
// 0
// 1
// 2

13. values 定义:返回一个新的Array Iterator对象,该对象包含数组每个索引的值。 语法arr.values()

const fruits = ['apple', 'banana', 'orange'];
const iterator = fruits.values();

for (const value of iterator) {
  console.log(value);
}
// 输出:
// apple
// banana
// orange

类数组或伪数组转数组

  • 创建数组
// 创建:
[];
new Array()
Array.of(1,2) == [1,2]
Array.from()  // 伪数组转数组

类数组或伪数组转数组:

  1. Array.from
  2. Array.prototype.slice.call(obj)
    这么使用是改变数组方法slice的作用域,使其能作用域非数组对象。slice方法是截取数组的一部分并返回一个新数组;而call方法用于在函数调用时把它内部的this指针绑定到另一个对象上。
    Array.prototype.slice.call就是把Array原型链上的方法slice,其内部的this指针绑定到()里的对象上,使对象可以使用slice方法,也就是把对象转换为数组。
  3. 扩展运算符
  4. Array.prototype.map().call(obj)
//类数组转数组:
// 1. Array.from
const pseudoArray = {0: "a", 1: "b", 2: "c", length: 3};
const array = Array.from(pseudoArray);
console.log(array); // ["a", "b", "c"]

//2. 使用Array.prototype.slice.call()方法:
const array = Array.prototype.slice.call(pseudoArray);  
console.log(array); // ["a", "b", "c"]

//3. 扩展运算符
const array = [...pseudoArray];  
console.log(array); // ["a", "b", "c"]

//4.  使用Array.prototype.map().call方法:
const array = Array.prototype.map.call(pseudoArray, function(value) {  
return value;  
});
console.log(array); // ["a", "b", "c"]

什么是this指针?严格模式下呢?

this跟执行环境、声明环境、class有关。

  • 普通函数内指向调用者,如obj.fuc,fuc的this指向obj。
  • 对象内,this指向方法所在对象的name属性。
  • function函数无调用者,指向window
  • new构造函数--指向new的对象,class同。
  • call,apply指向调用对象
  • 箭头函数,外层函数的this就是内层函数的this==自身没有this,从外层找到后继承,如果就一层指向window,

严格模式,指向undefined
箭头函数没有自己的this,它的this是继承来的。默认指向定义时的宿主。

高阶函数有什么?

高阶函数是指:如果函数的参数,或者返回值,也是函数,那就是高阶函数。
举例:promise, 计时器, map, filter, reduce等

call,apply,bind

  • 相同:
    这3种是function的prototype上的方法,所以谁都可以用。作用都一样:改变this的指向,说白了就是用一个obj调用一个函数,补足函数没有值的参数。
  • 不同:
  1. apply第二个参数是数组形式;
  2. bind需要再小括号调用()一次。
  3. bind参数类似apply也有数组,会拼接在callback参数之前。
  4. ES6+ 使用展开运算符替代apply
    Math.max(...[1, 2, 3]) // 替代 Math.max.apply(null, [1, 2, 3])
  5. ES6+ 使用箭头函数替代 bind
    button.addEventListener('click', () => this.handleClick())
function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}
// call
const person = { name: 'Alice' };
greet.call(person, 'Hello', '!'); // 输出: "Hello, Alice!"


// apply
const person = { name: 'Bob' };
const args = ['Hi', '!!!'];
greet.apply(person, args); // 输出: "Hi, Bob!!!"


// bind 
const person = { name: 'Charlie' };
const greetCharlie = greet.bind(person, 'Hello');
greetCharlie('...'); // 输出: "Hello, Charlie..."

适用场景:

  1. Object.prototype.toString.call(obj)实现类型检测。
    首先,我们知道js全员皆对象,都是从Object继承而来,而Object原型上的toString是可以输出对象类型的,这个toString方法也被继承给了每一个类,但是他们在继承时都做了改写。所以,使用Object最原始的也就是prototype上的toString方法是可以判断类型的,只要把this指向需要判断的元素,就可以直接输出类型了。
  2. Math.max.call(Math, arr)计算数组最大值
    效果等同于Math.max(...arr)解构数组
  3. 实现继承,如下:

function peaple(name) {
  this.name = name;
  this.showName = function () {
    console.log(this.name);
  }
  }
function student(name) {
  peaple.call(this, name); 
  }
var xiaoming = new student('xiaoming');

xiaoming.showName();

  1. 数组追加:

var array1 = [1 , 2 , 3, 5];
var array2 = ["xie" , "li" , "qun" , "tsrot"];
Array.prototype.push.apply(array1, array2);
console.log(array1);
类似于 let c = array1.concat(array2)

call把原本people的this指向了student,所以student继承了原来的所有。
用现有的函数去处理call的值。

  1. 保存this变量:

var foo = {
    bar : 1,
    eventBind: function(){
        var _this = this ;
        $('.someClass').on('click',function(event) {
            console.log(_this.bar);     
        });
    }
}
var foo = {
    bar : 1,
    eventBind: function(){
        $('.someClass').on('click',function(event) {
            console.log(this.bar);      
        }.bind(this));
    }
}

  1. 如何实现一个.call
// 函数作用:调用函数,传参,改变this指针
// 步骤: 1.this,context; 
// 2.把调用的函数设置为对象的方法;
// 3.调用函数,得到返回值并返回。
Function.prototype.myCall = function(context, ...args) {
  // 1. 处理context为null或undefined的情况(非严格模式下会指向window/global)
  context = context || window;
  
  // 2. 确保context是对象(原始值会被包装)
  context = Object(context);
  
  // 3. 创建一个唯一的属性键,避免覆盖原有属性
  const fnKey = Symbol('fn');
  
  // 4. 将当前函数(this)赋值给context的fnKey属性
  context[fnKey] = this;
  
  // 5. 执行函数,并传入参数
  const result = context[fnKey](...args);
  
  // 6. 删除临时添加的属性
  delete context[fnKey];
  
  // 7. 返回函数执行结果
  return result;
};

// 使用示例
function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}

const person = { name: 'Alice' };

// 使用原生call
greet.call(person, 'Hello', '!'); // 输出: "Hello, Alice!"

// 使用自定义myCall
greet.myCall(person, 'Hi', '!!!'); // 输出: "Hi, Alice!!!"

说说对原型的理解?什么是原型链

ES6 的 class 本质上是原型继承的语法糖

  1. 什么是原型
    在 JavaScript 中,每个对象都有一个隐藏的 [[Prototype]] 属性(可以通过 __proto__ 访问),这个属性指向该对象的原型对象。原型对象也是一个普通对象,它包含可以被其他对象共享的属性和方法。
  2. 构造函数与原型
  • 每个函数都有一个 prototype 属性(注意:这是函数特有的属性)
  • 当使用 new 操作符调用构造函数创建实例时,实例的 __proto__ 会指向构造函数的 prototype
  1. 原型链的定义 当访问一个对象的属性时,JavaScript 会:
    1. 先在对象自身属性中查找
    1. 如果找不到,就去它的原型对象上查找
    1. 如果还找不到,就去原型对象的原型上查找
    1. 依此类推,直到找到属性或到达原型链顶端(null
      这种层层向上的查找机制就形成了原型链
// 原型链:
alice -> Person.prototype -> Object.prototype -> null

代码中创建一个function,浏览器会对应生成一个object(js全员皆对象),每个对象都初始化一个prototype内部属性,就是该对象的原型。除了Object.prototype对象以外的对象都是通过其他对象的原型创建的。
访问一个对象的属性或方法时,如果没找到就会向上继续找,去对象的Prototype里找,这个prototype又有自己的prototype,这样一直找下去就形成了原型链。这种查找机制就是原型链。
js的继承常见的原型链继承,构造函数继承,原型+构造,寄生,寄生组合。

总结:

  1. 原型是 JavaScript 实现继承和属性共享的机制
  2. 原型链是通过 __proto__ 连接起来的对象链,用于属性查找
  3. 每个对象都有原型(除了 Object.create(null) 创建的对象)
  4. 函数的 prototype 属性决定了用它创建的实例的原型
  5. 原型链的终点是 Object.prototype,其 __proto__ 为 null

说一下防抖和节流

resize、onscroll、input内容校验onkeydown,类似这样的操作如果无限制触发调用函数,js-css-layout-paint-composite,会加重浏览器负担,debounce防抖和throttle节流就应运而生了。 不同:

  • 防抖--抖动是没有必要的,只要最后一次就行。在delay之前触发会重新计时,最终只执行一次。(多次执行变最后一次执行)
  • 节流--类似王者技能的触发,有CD时间,不能无限制发动技能。实时更新用这个,在1分钟内只触发一次,多次触发无效。1分钟后重新计时再执行下一次。(多次执行变每隔一段时间触发一次)
    工作中,可以原生js实现,lodash插件有提供现成的方法。

介绍async和await

  1. async是声明function是异步的,awaite是等待一个异步执行完。
  2. 规则:
  • awaite只能在async里使用,不可单独使用。
  • async返回的是一个promise对象,awaite就是等待这个promise的结果,再继续执行。
  • awaite等待的是一个promise,后面必须跟一个promise对象,但是不必写then(),直接得到返回值。
  1. async/awaite的特点
  • async放在函数前,函数变为异步函数。
  • 在函数前加async,打印函数调用,是一个Promise对象,而不是函数体内容。
  • async配合awai使用。
  • 调用依次发生
  • Promis.then是链式调用,一直...,符合书写代码习惯。

只有async没有await时,函数返回什么:一个Promise

浏览器关闭前,怎么确保发出请求

方法1:
使用 Fetch 的keepalive标志
方法2:
使用Navigator.sendBeacon()

axios和ajax的区别,axios怎么发同步请求

ajax基于XMLHttpRequest

axios
  1. axios是基于Promise的HTTP客户端,用于浏览器和node.js,内部判断环境是前端还是后台,选择用XMLHttpRequest还是Http对象。它提供了一个简洁的 API 来发送 HTTP 请求,并支持拦截请求和响应、转换请求和响应数据、取消请求、自动转换 JSON 数据等功能。
  2. axios有个拦截器叫intercepters,它有2对象分别是requestresponse,俩对象都有.use方法,每次调用都向callback里push{res1,rej1,res2,rej2}。
  3. 所有的回调都要储存起来吧,那么就有一个chain,用来存储所有回调的数组,它是给Promise用的,有2个初始值为dispatchRequest和undefined,再继续存储所有回调,存储顺序是什么样呢?Request的{res1,rej1,res2,rej2...}按顺序逐个unshift插入到chain最前面(注意:所以intercapters的request顺序看起来是前后颠倒了),Response的{r1,j1,r2,j2...}按顺序逐个push到chain最后面,所以chain完整列表就是{rej2, res2, rej1, res1, dispatchRequest, undefined,r1,j1,r2,j2 }
  • dispatchRequest,是用来执行axios的request,dispatchRequest里有个adapter,可以判断当前是前端还是后台,如果是后台就用HTTP对象,前端就用XMLHttpRequest,调用后adapter会对返回值进行处理,就是我们看到的response响应对象。
  1. 那么,Promise开始用chain了,Promise.resolve(config).then(fn1, fn2)。当config里结果是fullfilled(成功),就把config里的值传给fn1执行;如果config里结果是reject(失败),就把错误结果传给fn2执行。也就是说,前面的等同于Promise.resolve(config).then(chain[0],chain[1])。chain[0]是成功回调,chain[1]是失败回调。config里配置项很多,可能是一个string或者方法等。

WX20231014-034208@2x.png

axios怎么发送同步请求?

axios默认发送异步请求,这是因为它基于promise。如果需要发送同步请求,可以使用async/await和者Promise.resolve来模拟同步。

async function fetchData() {  
    try {  
        const response = await axios.get('/api/data');  
        // 处理响应数据  
        console.log(response.data);  
    } catch (error) {  
        // 处理错误  
        console.error(error);  
    }  
}

前端开发的内存溢出怎样处理

一、内存溢出常见表现

  1. 页面卡顿、响应迟缓
  2. 浏览器标签页崩溃或自动刷新
  3. Chrome任务管理器显示内存持续增长不释放
  4. 控制台出现"Out of Memory"错误

诊断步骤

1. 打开无痕窗口(排除插件干扰)
2. 记录初始Heap Snapshot
3. 执行可疑操作多次
4. 记录操作后Heap Snapshot
5. 对比两次快照,查找Retained Size大的对象

前端的浅拷贝,深拷贝,举例说明

浅拷贝:Object.assign(), ...扩展运算符

// 浅拷贝对象
const originalObj = {
    name: 'John',
    age: 30,
    hobbies: ['reading', 'swimming']
};

// 使用 Object.assign() 进行浅拷贝
const shallowCopyObj = Object.assign({}, originalObj);

// 修改浅拷贝对象的引用数据类型属性
shallowCopyObj.hobbies.push('running');
console.log(originalObj.hobbies); // 输出: ['reading', 'swimming', 'running']
console.log(shallowCopyObj.hobbies); // 输出: ['reading', 'swimming', 'running']

// 浅拷贝数组
const originalArr = [1, [2, 3], 4];

// 使用扩展运算符进行浅拷贝
const shallowCopyArr = [...originalArr];

// 修改浅拷贝数组的引用数据类型元素
shallowCopyArr[1].push(6);
console.log(originalArr[1]); // 输出: [2, 3, 6]
console.log(shallowCopyArr[1]); // 输出: [2, 3, 6]

深拷贝:lodash.cloneDeep, JSON.parse(JSON.stringify())无法处理特殊数据类型,structuredClone兼容性不好,手写deepCopy函数如下:

// 深拷贝函数
function deepCopy(obj) {
    if (typeof obj!== 'object' || obj === null) {
        return obj;
    }
    let newObj = Array.isArray(obj)? [] : {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = deepCopy(obj[key]);
        }
    }
    return newObj;
}

const deepCopyObj = deepCopy(originalObj);

JS阻塞和非阻塞执行调整的意图

阻塞行为:如网络请求、文件读取等。当耗时操作完成后,会通过回调函数、Promise 或 async/await 等方式通知主线程处理结

获取日期年月日怎么写

  1. new Date(),可封装函数
// 获取当前日期
const now = new Date();

// 获取年月日
const year = now.getFullYear();  // 2023(四位数年份)
const month = now.getMonth() + 1; // 0-11 → 需要+1
const day = now.getDate();       // 1-31
console.log(`${year}-${month}-${day}`); // 输出如:2023-5-15

// 使用padStart补零
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0'); // 05
const day = String(now.getDate()).padStart(2, '0');        // 15
console.log(`${year}-${month}-${day}`); // 2023-05-15

2. 第三方插件:moment.js或date-fns

上传下载?多个文件同时上传怎样处理?上传失败了怎么找出是哪个文件

  1. formData
const formData = new FormData();
  formData.append('file', file);

2. 大文件断点续传

const formData = new FormData();
formData.append('file', chunk);
formData.append('chunkIndex', i);
formData.append('totalChunks', chunks);
formData.append('uploadId', uploadId);
formData.append('originalName', file.name);
  1. 上传失败处理与问题定位
    方法一:使用Promise.allSettled
async function handleUploads(files) {
  const results = await Promise.allSettled(
    files.map(file => uploadFile(file))
  );
  
  const failedFiles = results
    .filter(result => result.status === 'rejected')
    .map((result, index) => ({
      file: files[index].name,
      reason: result.reason.message
    }));
  
  if (failedFiles.length > 0) {
    console.error('以下文件上传失败:', failedFiles);
    // 显示失败文件列表给用户
  }
}

最佳实践:文件校验+并发控制

性能优化自己常用的方法

JavaScript输出题

闭包

function foo() {
    var a = 0;
    return function () {
        console.log(a++)
    }
}
var f1 = foo()  //0
var f1 = foo()  //1
var f2 = foo()  //0

es6

Promise

1.是什么
js中处理异步操作的对象。

2.为什么用它
传统解决方案是callback回调,多层嵌套,容易形成回调地狱。

getData(function(data1) {
  getMoreData(data1, function(data2) {
    getMoreData(data2, function(data3) {
      // 层层嵌套,难以阅读和维护
    });
  });
});

Promise 的优势
✅ 链式调用.then().catch()),避免回调地狱。
✅ 更好的错误处理.catch() 统一捕获错误)。
✅ 支持并发控制Promise.allPromise.race)。

3. Promise 的三种状态
状态一旦改变,就不可逆

  1. Pending(等待中) :初始状态,既未完成也未失败。
  2. Fulfilled(已完成) :操作成功完成,返回结果(resolve 被调用)。
  3. Rejected(已失败) :操作失败,返回错误(reject 被调用)。

4. new Promise
创建

const promise = new Promise((resolve, reject) => {
  // 异步操作(如 AJAX、定时器等)
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve("操作成功!"); // 状态变为 Fulfilled
    } else {
      reject("操作失败!"); // 状态变为 Rejected
    }
  }, 1000);
});

使用 .then() 和 .catch() 处理结果,.finally()总是执行。可以链式调用.then.then

promise
  .then((result) => {
    console.log(result); // "1操作成功!"
  })
  .then((result) => {
    console.log(result); // "2操作成功!"
  })
  .catch((error) => {
    console.error(error); // "操作失败!"
  });
  .finally(() => 
      console.log("操作结束!")
  );

捕捉错误可以用try/catch + async+await

async function loadData() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}
loadData();

5. 多任务执行时

  1. Promise.all()(全部成功才返回)
    当其中一个任务执行错误时,会立即返回,被.catch捕捉。其他任务会继续执行,所以.then和.catch都会捕捉到。
const p1 = Promise.resolve("任务1");
const p2 = Promise.resolve("任务2");

Promise.all([p1, p2])
  .then((results) => {
    console.log(results); // ["任务1", "任务2"]
  })
  .catch((error) => {
    console.error(error); // 如果有一个失败,立即返回错误
  });

2. Promise.race()(谁先完成就返回谁)

const p1 = new Promise((resolve) => setTimeout(resolve, 500, "快的任务"));
const p2 = new Promise((resolve) => setTimeout(resolve, 1000, "慢的任务"));

Promise.race([p1, p2])
  .then((result) => {
    console.log(result); // "快的任务"
  });

3. Promise.allSettled()(等所有 Promise 完成,无论成功/失败

const p1 = Promise.resolve("成功");
const p2 = Promise.reject("失败");

Promise.allSettled([p1, p2])
  .then((results) => {
    console.log(results);
    // [
    //   { status: "fulfilled", value: "成功" },
    //   { status: "rejected", reason: "失败" }
    // ]
  });

4. Promise.any()(只要有一个成功就返回,全部失败才报错)

const p1 = Promise.reject("错误1");
const p2 = Promise.resolve("成功");

Promise.any([p1, p2])
  .then((result) => {
    console.log(result); // "成功"
  })
  .catch((errors) => {
    console.error(errors); // 如果全部失败,返回 AggregateError
  });

箭头函数的this问题

image.png

1. 静态绑定(Lexical this)
箭头函数的 this 在函数定义时就确定了,而不是在调用时确定。它会捕获其所在上下文的 this 值。

const obj = {
  name: 'Alice',
  regularFunc: function() {
    console.log(this.name); // this指向obj
  },
  arrowFunc: () => {
    console.log(this.name); // this指向外层作用域(通常是window)
  }
};

obj.regularFunc(); // 输出 "Alice"
obj.arrowFunc();   // 输出 undefined(严格模式下可能是undefined)

2. 解决回调函数中的 this 问题

// 普通函数需要bind或保存this
function Timer() {
  this.seconds = 0;
  setInterval(function() {
    this.seconds++; // 错误!这里的this指向window
  }, 1000);
}

// 箭头函数自动绑定外层this
function Timer() {
  this.seconds = 0;
  setInterval(() => {
    this.seconds++; // 正确!this指向Timer实例
  }, 1000);
}

3. 不适合使用箭头函数的场景

  • 对象方法:
const person = {
  name: 'Bob',
  // 错误写法
  greet: () => console.log(`Hello, ${this.name}`), // this指向window
  
  // 正确写法
  greet() {
    console.log(`Hello, ${this.name}`) // this指向person
  }
}
  • 原型方法:
function Person(name) {
  this.name = name
}

// 错误写法
Person.prototype.sayHi = () => console.log(`Hi, ${this.name}`) // this指向window

// 正确写法
Person.prototype.sayHi = function() {
  console.log(`Hi, ${this.name}`) // this指向实例
}

4. 如何确定箭头函数的 this
可以通过以下步骤判断:

  1. 找到箭头函数定义的位置(不是调用的位置)
  2. 查看箭头函数外层的第一个普通函数的this指向
  3. 如果外层没有普通函数,this将指向全局对象(严格模式下是undefined)

forEach和map区别

image.png

es6更新内容?

  1. 块级作用域
  2. 箭头函数
  3. 模板字符串
  4. 解构赋值
  5. 默认参数
  6. 拓展运算符
  7. 类和继承
  8. Promise对象
  9. 模块化:import、export
  10. 迭代器和生成器:Iterator迭代对象,Generator生成函数

ts

TS的interface和type的使用场景

框架

vue

vue获取子组件的数据

$refs: this.$refs.childRef.childData

子组件触发:this.$emit('childDataEvent', this.childData);

使用 Vuex

v-for用于哪些数据

数组、对象,数字范围,字符串

v-for="n in 3"    //1,2,3

说说vue3的proxy

Vue 3 使用 JavaScript 的 Proxy 替代 Vue2的 Object.defineProperty 来实现响应式系统

一、Proxy 基本概念

Proxy 是 ES6 引入的新特性,可以创建一个对象的代理,用于拦截和自定义对象的基本操作。

const target = {}
const handler = {
  get(target, key) {
    console.log(`读取 ${key}`)
    return target[key]
  },
  set(target, key, value) {
    console.log(`设置 ${key}${value}`)
    target[key] = value
    return true
  }
}

const proxy = new Proxy(target, handler)
proxy.name = 'Vue'  // 输出: 设置 name 为 Vue
console.log(proxy.name)  // 输出: 读取 name → Vue
二、Vue 3 如何使用 Proxy

Vue 3 的 reactive() 函数就是基于 Proxy 实现的:

三、相比 Vue 2 的优势

image.png

四、Proxy 实现响应式的核心原理
  1. Getter 拦截

    • 追踪依赖(track)
    • 返回属性值
  2. Setter 拦截

    • 设置新值
    • 触发更新(trigger)
unction reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      track(target, key)  // 追踪依赖
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver)
      trigger(target, key)  // 触发更新
      return true
    }
    // 还有has、deleteProperty等其他拦截器
  })
}
五、Proxy 的独特能力
  1. 检测所有属性变化 const obj = reactive({}) obj.newProp = 'value' // 自动响应,无需Vue.set delete obj.someProp // 自动响应,无需Vue.delete

  2. 完美支持数组 const arr = reactive([]) arr.push('item') // 正常工作 arr.length = 0 // 正常工作 arr[3] = 'new' // 正常工作

  3. 支持Map/Set等集合类型 const map = reactive(new Map()) map.set('key', 'value') // 响应式更新

六、局限性
  1. 浏览器兼容性

    • 不支持IE11及以下浏览器
    • 现代浏览器全面支持
  2. 性能考量

    • 对于简单小对象,Proxy 可能比 defineProperty 稍慢
    • 对于大型复杂对象,Proxy 性能更好
七、与Reflect的配合

Vue 3 使用 Reflect API 与 Proxy 配合:

const handler = {
  get(target, key, receiver) {
    // 使用Reflect保证正确的this指向
    return Reflect.get(target, key, receiver)
  }
}

Vue2和Vue3的区别

1.核心区别

image.png

2.生命周期

image.png

3. Vue3的优势
(1. 更高效的响应式系统
  • Proxy 替代 Object.defineProperty
    • Vue 2 需递归遍历对象属性进行劫持,且无法检测新增/删除属性(需 Vue.set)。
    • Vue 3 的 Proxy 直接代理整个对象,支持动态属性变化监听。
(2. Composition API(组合式 API),结合setup函数
(3. 新特性与兼容性改进

<Teleport> :将组件渲染到 DOM 任意位置(如全局弹窗)。

<Teleport to="body">
  <Modal /> <!-- 渲染到 body 而非当前组件 -->
</Teleport>

<Suspense> :优雅处理异步组件加载状态。 在 Vue 3 里,Suspense 是一个内置组件,用于处理异步依赖,在等待异步操作(像异步组件加载、数据获取这类操作)完成时,显示加载状态。它具备两个插槽:

  • #default:这里放置的是包含异步依赖的组件。
  • #fallback:当异步依赖还未完成时,会显示这个插槽里的内容。
<Suspense>
  <template #default><AsyncComponent /></template>
  <template #fallback><LoadingSpinner /></template>
</Suspense>

多 `v-model:支持多个双向绑定。

<ChildComponent v-model:title="title" v-model:content="content" />
4、迁移注意事项
  1. 破坏性变化
    • v-model 语法变更(.sync 被移除)。
    • 事件总线($on/$off)废弃,推荐使用 mitt 或 Pinia/Vuex。
  2. IE11 不支持:Vue 3 依赖 Proxy,无法兼容 IE11(需用 Vue 2)。
  3. 生态适配:Vue Router 4.x、Vuex 4.x 为 Vue 3 专用版本。

Vue 2X3的生命周期

在上一条区别里

Vue3 生命周期钩子

Vue3 的生命周期钩子相比 Vue2 有一些变化,主要区别在于 Composition API 中的命名和 beforeDestroy/destroyed 被重命名为 beforeUnmount/unmounted:

  1. setup()  - Composition API 的入口,在 beforeCreate 之前调用
  2. onBeforeMount()  - 组件挂载之前
  3. onMounted()  - 组件挂载完成后
  4. onBeforeUpdate()  - 数据变化,DOM 更新之前
  5. onUpdated()  - 数据变化,DOM 更新完成后
  6. onBeforeUnmount()  - 组件卸载之前 (Vue2 的 beforeDestroy)
  7. onUnmounted()  - 组件卸载完成后 (Vue2 的 destroyed)
  8. onActivated()  - 被 keep-alive 缓存的组件激活时
  9. onDeactivated()  - 被 keep-alive 缓存的组件停用时
  10. onErrorCaptured()  - 捕获子孙组件错误时

vue组件如何传值

  1. 父:obj + 子props
  2. 父@handle + 子emit
  3. bus+bus + emit + on+on + off
  4. vueX和Pinia,状态管理
  5. 插槽

vue常用指令有哪些

  1. v-text:更新元素的文本内容
  2. v-html:渲染原始 HTML(注意XSS风险)
  3. v-bind(缩写 :):动态绑定属性
  4. v-if / v-else-if / v-else:条件渲染
  5. v-show:通过CSS控制显示/隐藏(频繁切换时性能更好)
  6. v-for:列表渲染
  7. v-on(缩写 @):绑定事件监听器
  8. v-model:双向数据绑定(表单输入)
  9. v-slot(缩写 #):定义插槽内容
  10. v-once:只渲染一次,后续数据变化不再更新
  11. v-bind:[attr]  / v-on:[event] :动态参数

vue的修饰符

  1. 事件修饰符
<!-- 阻止默认行为 -->
<form @submit.prevent="onSubmit"></form>

<!-- 阻止事件冒泡 -->
<div @click.stop="doThis"></div>

<!-- 事件只触发一次 -->
<button @click.once="doThis"></button>

2. 键盘修饰符

<!-- 回车键触发 -->
<input @keyup.enter="submit">

<!-- 特定键触发 -->
<input @keyup.page-down="onPageDown">

3. 表单修饰符

<!-- 输入时去除首尾空格 -->
<input v-model.trim="msg">

<!-- 自动将输入转为数字 -->
<input v-model.number="age" type="number">

<!-- 惰性更新(change事件后更新而非input事件) -->
<input v-model.lazy="msg">

vue自定义指令

全局自定义指令,可在整个应用中使用
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);

// 定义全局自定义指令
app.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  mounted(el) {
    // 聚焦元素
    el.focus();
  }
});

app.mount('#app');
局部自定义指令,在本组件内使用
<template>
  <input v-focus />
</template>

<script setup>
// 定义局部自定义指令
const vFocus = {
  mounted(el) {
    el.focus();
  }
};
</script>
指令钩子
app.directive('example', {
  // 指令绑定到元素时调用
  beforeMount(el, binding, vnode, prevVnode) {
    // 可以进行初始化操作
    console.log('beforeMount');
  },
  // 元素插入到 DOM 中时调用
  mounted(el, binding, vnode, prevVnode) {
    // 可以进行 DOM 操作
    console.log('mounted');
  },
  // 在包含组件的 VNode 更新之前调用
  beforeUpdate(el, binding, vnode, prevVnode) {
    console.log('beforeUpdate');
  },
  // 在包含组件的 VNode 及其子 VNode 更新之后调用
  updated(el, binding, vnode, prevVnode) {
    console.log('updated');
  },
  // 在绑定元素的父组件卸载之前调用
  beforeUnmount(el, binding, vnode, prevVnode) {
    console.log('beforeUnmount');
  },
  // 绑定元素的父组件卸载之后调用
  unmounted(el, binding, vnode, prevVnode) {
    console.log('unmounted');
  }
});
指令参数

自定义指令可以接收参数,通过 binding 对象传递。

<template>
  <div v-color="'red'">This text will be red</div>
</template>

<script setup>
const vColor = {
  mounted(el, binding) {
    el.style.color = binding.value;
  }
};
</script>

vue中,父子组件加载时的生命顺序,传值时的呢?

  1. 父创建,子创建,子挂载,父挂载==》
父 beforeCreate → 
父 created → 
父 beforeMount → 
  子 beforeCreate → 
  子 created → 
  子 beforeMount → 
  子 mounted → 
父 mounted

父组件销毁过程:

父 beforeDestroy → 
  子 beforeDestroy → 
  子 destroyed → 
父 destroyed

当父组件数据变化触发更新时:

父 beforeUpdate → 
  子 beforeUpdate → 
  子 updated → 
父 updated

2. 因为子mounted在前,所以,组件数据回显时,在父组件mounted里获取值,子组件的mounted是拿不到的,所以无法直接回显。按道理可以在父created里取值,但是因为请求是异步的,所以这样也不保险。项目中怎么解决呢?

  • 子组件加v-if="data",这样请求到数据后再加载子组件。
  • 子组件watch,监听props值。

nextick是什么原理

利用js的时间循环机制来实现异步执行。一个组件中多个nextTick会合并处理,协调宏任务和微任务放到合适的地方。Vue数据修改后并非马上立刻修改DOM,而是经过策略计算后修改,所以要在nextTick里执行。

keep-alive是做什么的,原理呢?

一、keep-alive 的作用

keep-alive 是 Vue 内置的一个抽象组件,用于缓存不活动的组件实例,避免重复渲染和销毁带来的性能损耗。主要功能包括:

  1. 组件缓存:当组件被切换时(如路由切换或动态组件切换),默认会被销毁,而使用 keep-alive 可以保留组件状态
  2. 状态保持:保留组件的所有状态(包括数据、DOM 状态等)
  3. 避免重复渲染:再次激活时直接使用缓存实例,无需重新创建

二、基本使用方式

<keep-alive>
  <component :is="currentComponent"></component>
</keep-alive>

<!-- 或用于路由视图 -->
<keep-alive>
  <router-view></router-view>
</keep-alive>

三、 实现流程

  1. 渲染阶段
    • keep-alive 组件本身不会渲染任何额外DOM元素
    • 通过插槽(slot)获取第一个子组件
  2. 缓存判断
    • 根据组件名(name)和key生成唯一标识
    • 检查缓存中是否已有该组件实例
  3. 缓存处理
    • 如果存在缓存:直接取出实例,不执行组件的创建过程
    • 如果不存在缓存:正常创建组件实例,并存入缓存
  4. 生命周期处理
    • 缓存组件时会触发 deactivated 钩子
    • 恢复组件时会触发 activated 钩子
  5. 钩子:activateddeactivated
  6. includeexcludemax

四、 适合场景

  • 表单内容缓存(如多步骤表单)
  • 列表页到详情页的返回保持
  • 频繁切换但需要保持状态的组件(如标签页)

五、 面试题:怎么保存孙子组件
keep-alive 默认只能直接缓存其直接子组件,而不会自动缓存孙子组件(子组件的子组件)

    1. 直接嵌套:
// 1. 直接嵌套
<keep-alive>
  <parent-component>
    <keep-alive>
      <grandchild-component />
    </keep-alive>
  </parent-component>
</keep-alive>
    1. 【推荐】使用include 属性指定需要缓存的孙子组件名:
<!-- 父组件中 -->
<template>
  <keep-alive :include="cachedComponents">
    <child-component />
  </keep-alive>
</template>

<script>
export default {
  data() {
    return {
      cachedComponents: ['grandchild-a', 'grandchild-b']
    }
  }
}
</script>

<!-- 孙子组件中 -->
<script>
export default {
  name: 'grandchild-a', // 必须设置明确的name
  activated() {
    // 激活时的数据刷新逻辑
  },
  deactivated() {
    // 停用时的清理逻辑
  }
}
</script>
    1. 将孙子组件作为动态组件使用:
<keep-alive>
  <component :is="currentComponent" />
</keep-alive>
    1. 如果使用 Vue Router,可以在路由配置中设置,然后在路由视图中判断meta信息
{
  path: '/parent',
  component: ParentComponent,
  children: [
    {
      path: 'grandchild',
      component: GrandchildComponent,
      meta: { keepAlive: true }
    }
  ]
}

<keep-alive>
  <router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
    1. 高级实现原理:深度控制孙子组件的缓存,可以基于以下原理实现 (不明白,求解)
1.  递归查找:修改 keep-alive 源码,使其能递归查找所有子孙组件
2.  自定义缓存键:为每个需要缓存的组件生成唯一标识
3.  手动缓存管理:使用 Vue 的缓存机制手动管理组件实例

有什么封装的组件?

对element组件的二次封装
  1. 统一分页组件:封装el-pagination,统一分页样式和逻辑
  2. 增强型表格:在el-table基础上添加通用功能如导出、列配置等
  3. 表单生成器:基于el-form实现动态表单生成
  4. 对话框封装:统一对话框的打开/关闭逻辑和样式
  5. 上传组件增强:在el-upload基础上添加更多业务逻辑
封装注意事项
  1. 正确处理attrs和listeners:使用v-bind="$attrs"v-on="$listeners"(Vue2)或直接使用$attrs(Vue3)
  2. 保持插槽透传:使用<slot></slot>或具名插槽<slot name="xxx"></slot>
  3. 类型定义:为TypeScript项目提供良好的类型支持

项目中的技术难点?

gridstack
借助AI实现国际化

vue的data为什么必须是function

核心原理:保证组件实例数据的独立性
  1. 避免数据共享问题

    • 如果 data 是一个对象,所有组件实例将共享同一个数据对象
    • 修改一个组件实例的数据会影响到其他所有实例
    • 通过函数返回对象,每个组件实例都会获得独立的数据副本
底层原理:
  1. 组件复用机制

    • Vue 组件是可复用的
    • 当多次使用同一个组件时,会创建多个组件实例
    • 如果使用对象形式,所有实例将引用同一个数据对象
  2. 工厂函数模式

    • 通过函数每次返回新对象,实现了类似工厂模式的效果
    • 每个组件实例调用 data 函数得到全新的数据对象

vuex有哪些属性,怎么同步和异步修改数据

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,它包含以下主要属性:state, getters, mutations, actions, modules。
完整使用示例:

// store.js
import { createStore } from 'vuex';

export default createStore({
  state: () => ({ count: 0 }),
  mutations: {
    increment(state) {
      state.count++
    }
  },
  actions: {
    async incrementAsync({ commit }) {
      setTimeout(() => commit('increment'), 1000)
    }
  },
  getters: {
    doubleCount: state => state.count * 2
  }
})

// 组件中使用
this.$store.commit('increment')
this.$store.dispatch('incrementAsync')

下面逐个解释:

  1. Modules - 将 store 分割成模块 const moduleA = { state: () => ({ ... }), getters: {...} mutations: { ... }, actions: { ... } }

  2. State - 存储应用状态数据 state: { count: 0, user: null }

  3. Getters - 从 state 派生的计算属性 getters: { doubleCount: state => state.count * 2 }

  4. Mutations - 同步修改 state 的方法 mutations: { increment(state, payload) { state.count += payload.amount } }

  5. Actions - 包含异步操作,提交 mutations actions: { incrementAsync({ commit }, payload) { setTimeout(() => { commit('increment', payload) }, 1000) } }

vuex 和pinia的区别

image.png

Pinia使用示例:

// stores/counter.js
import { defineStore } from 'pinia'

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

// 组件中使用
const counter = useCounterStore()
counter.increment()
counter.incrementAsync()

vuex在刷新页面数据会丢失怎么解决

  1. 使用 localStorage/sessionStorage
  2. 使用 vuex-persistedstate 插件
  3. 使用 IndexedDB (适合大量数据)
  4. 存储到后台数据库

vue路由模式有哪些

image.png

1. Hash 模式 (默认模式)

特点

  • 使用 URL 的 hash (#) 来模拟完整 URL
  • 改变 hash 不会触发页面刷新
  • 兼容性最好,支持所有浏览器
  • 示例URL:http://example.com/#/home

优点

  • 兼容性好,支持 IE9+
  • 部署简单,不需要服务器额外配置
  • 不会导致404错误,因为hash部分的变化不会发送到服务器

缺点

  • URL 中有 #,不够美观
  • 不利于SEO优化(虽然现代搜索引擎已能抓取hash内容)
2. History 模式

特点

  • 使用 HTML5 History API (pushState, replaceState)
  • URL 更美观,没有 #
  • 示例URL:http://example.com/home

优点

  • URL 更简洁美观
  • 对SEO更友好
  • 可以利用服务器端渲染(SSR)

缺点

  • 需要服务器端支持,避免刷新出现404
  • 兼容性稍差(IE10+)
  • 需要正确的服务器配置
服务器配置示例(History模式)- Nginx 配置
location / {
  try_files $uri $uri/ /index.html;
}

vue路由如何传值

  1. 通过路由参数传值,params
const routes = [
  {
    path: '/detail/:id',
    name: 'Detail',
    component: Detail
  }
];

//跳转
const goToDetail = (id) => {
  router.push(`/detail/${id}`);
};

//接收
<script setup>
import { useRoute } from 'vue-router';
const id = useRoute().params.id;
</script>

2. 通过查询参数传值,query

<script setup>
import { useRouter } from 'vue-router';
useRouter().push({ name: 'Detail', query: { id, name } });
</script>

//接收
$route.query.name

3. 通过路由元信息传值

const routes = [
  {
    path: '/detail',
    name: 'Detail',
    component: Detail,
    meta: {
      title: 'Detail Page',
      description: 'This is a detail page.'
    }
  }
];

// 接收
route.meta.title

介绍vue的路由守卫

graph TD
开始路由 --> 触发组件离开beforeRouteLeave --> 全局路由守卫beforeEach --> 路由独享守卫beforeEnter --> 组件进入beforeRouteEnter -->全局解析守卫beforeResolve -->完成导航 -->全局后置afterEach

beforeEach 通常用于执行一些在进入路由之前必须完成的逻辑。这些逻辑可能包括但不限于:

  1. 登录验证:检查用户是否已经登录,如果没有登录,则重定向到登录页面。这是最常见的全局路由守卫使用场景之一。
  2. 权限验证:验证用户是否有权限访问某个路由。这通常涉及到检查用户的角色或权限列表,并决定是否允许其访问该路由。
  3. 获取数据:在进入路由之前,可能需要从服务器获取一些数据。这可以是用户数据、配置数据或其他任何需要的数据。
  4. 设置全局变量或状态:根据路由的变化,可能需要设置或更新一些全局变量或状态。
  5. 错误处理:在路由切换过程中,如果发生错误,可以在全局路由守卫中进行捕获和处理。
  6. 标题或面包屑更新:根据当前路由的信息,更新页面的标题或面包屑导航。
  7. 滚动位置管理:控制页面在路由切换时的滚动位置,例如,每次进入新页面时滚动到顶部。

怎么动态添加和修改路由

  • 动态添加:router.addRoutes([arr])
  • 修改:$router.options.routes是数组,直接改数组,但是不推荐,可能导致不匹配等问题。

React

react生命周期

image.png

react 常用的hooks有哪些

基础hooks
useState
const [count, setCount] = useState(0);
useEffect - 处理副作用(替代生命周期)
useEffect(() => {
  // 相当于 componentDidMount 和 componentDidUpdate
  document.title = `You clicked ${count} times`;
  
  return () => {
    // 相当于 componentWillUnmount
    console.log('Clean up');
  };
}, [count]); // 仅在 count 变化时更新
useContext - 访问上下文
const value = useContext(MyContext);
 额外 Hooks

useReducer - 更复杂的状态逻辑

const [state, dispatch] = useReducer(reducer, initialState);

useCallback - 缓存函数

const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

useMemo - 缓存计算结果

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useRef - 访问DOM或保存可变值

const inputRef = useRef(null);
// 访问DOM
<input ref={inputRef} />

useImperativeHandle - 自定义暴露给父组件的实例值

useImperativeHandle(ref, () => ({
  focus: () => {
    inputRef.current.focus();
  }
}));

image.png

uniapp

前端工程化

部署方式

mobaxterm,用Linux脚本部署。

webpack

介绍webpack

一. 是什么 ,一句话说明

是一个打包、模块化js的工具,通过构建依赖图将前端项目中的各种资源(JS、CSS、图片等)转换为优化的静态资源。
在webpack里一切文件都是模块,像是模块打包机,通过loader转换文件,plugin注入钩子,最后输出由多个模块合成的文件。
它做的事:分析我的项目结构,找到js模块,以及其他浏览器无法直接解析运行的拓展语言(ts,Scss),把这些文件转换成合适的格式,打包起来供浏览器使用。

二. 核心概念(关键术语)
  1. 入口(Entry) :依赖分析的起点
  2. 输出(Output) :打包后的文件输出位置
  3. Loader:处理非JS文件的转换器(如CSS/图片)
  4. 插件(Plugins) :执行更广泛任务的扩展功能
  5. 模式(Mode) :开发/生产环境预设优化
  6. 模块热替换(HMR) :开发时局部更新不刷新页面
三、核心工作原理
1. "Webpack 的工作原理可以概括为:
  1. 从配置的入口文件开始构建依赖图
  2. 递归分析所有依赖的模块
  3. 根据文件类型使用对应的Loader处理
  4. 通过插件系统执行额外优化任务
  5. 最终输出优化后的静态资源包"
2.构建流程,从读取配置到输出文件?
  • 串行,依次执行以下过程。
    1. 初始化参数:从配置文件到shell语句,读取合并参数,得出最终参数。
    1. 开始编译:用最终参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法,开始执行编译。
    1. 确定入口:根据配置中的entry找到所有的入口文件。
    1. 编译模块:从入口文件触发,调用所有配置的loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤,直到所有入口依赖的文件都经过了这步的处理。
    1. 完成模块编译:在上一步loader翻译完所有模块后,得到了每个模块被翻译后的最终内容,以及她们之间的依赖关系。
    1. 输出资源:根据入口和模块之间的依赖关系,组成一个个包含多个模块的Chunk,再把每个Chunk转换成一个单独文件加入到输出列表中,这步是可以修改输出内容的最后机会。
    1. 输出完成:确定输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统,在以上过程中,webpack会在特定时间点广播特定事件,插件在监听到感兴趣的事件后会执行特定逻辑,并且插件可以调用webpack提供的API改变webpack的运行结果。
3. webpack中有很多常用loader加载器,用来处理不同类型的文件,将其处理成webpack可理解和打包的模块。
1.样式loader
 - css-loader: 解析css文件,使其插入到js模块中
 - Sass-loader:转换成css文件
 - less-loader:同上
 - style-loader:使样式插入到DOM中,适用于内联样式

2.图片loader
file和url loader同样可以处理字体文件
 - svg-loader:把svg文件转换成js模块
 - file-loader:把图片打包到指定目录,并返回图片url路径
 - url-loader:类似file-loader,且可以对图片压缩和转换。

3.其他loader
 - babel-loader:把ES6+的js语法转换成向后兼容的ES5的语法,以便在旧版本浏览器上执行。
 - vue-loader:把vue组价转换成js
 - ts-loader:把ts转成js
四、进阶特性(展示深度)
  1. Tree Shaking:删除未使用代码
  2. Code Splitting:分包加载优化
  3. 缓存策略:contenthash持久化缓存
  4. 性能优化:dllPlugin/thread-loader等
  5. 自定义Loader/Plugin开发

webpack的工具有哪些

1. Webpack Bundle Analyzer
一、基本介绍

npm install --save-dev webpack-bundle-analyzer
Webpack Bundle Analyzer 是一个可视化分析工具,用于帮助开发者:

  • 直观查看 Webpack 打包后的文件组成
  • 分析各个模块的体积大小
  • 识别潜在的优化机会
  • 检查 Tree Shaking 效果
二、替代方案比较
工具特点适用场景
Webpack Bundle Analyzer交互式可视化本地开发深度分析
source-map-explorer基于source map精确到源代码行级分析
webpack-stats-plugin生成JSON数据CI/CD集成分析
webpack-visualizer饼图展示快速查看组成比例

针对业务场景提出

实现图片懒加载的方法和原理

  1. 原理:src属性是获取图片的,给一个统一的替代图片地址或者设置src_代替src属性。在图片进入视图时,赋值正确的src去请求图片。
  2. 实现关键在于判断元素位置,确定其是否出现在视口,有3种方式: - 滚动监听+scrollTop+offsetTop+innerHeight - 滚动监听+getBoundingClientRect() - intersetionObserve()
//方法1:
//object.getBoundingClientRect方法返回元素相对于视口的位置
getBoundingClientRect(ele).top >= 0 &&
getBoundingClientRect(ele).top <= offsetHeight

//方法2:
//intersetionObserve()非常好用 var io = new IntersectionObserver(callback, option); // 开始观察 ,可观察多个 io.observe(document.getElementById('example')); io.observe(el2) // 停止观察 io.unobserve(element); // 关闭观察器 io.disconnect();

前端开发中,搭建前端框架的步骤

  1. 明确需求与框架选型
  2. 环境搭建
  3. 创建项目:使用脚手架创建项目
  4. 路由配置
  5. 状态管理配置:Vuex || vite
  6. 样式配置:Sass、Less、Tailwind CSS
  7. 组件开发
  8. 单元测试
  9. 部署上线:构建项目,将构建好的静态文件部署到服务器上。

账号登录后进入主界面后前端都经历了哪些过程

  1. 登录表单提交

  2. 登录后,存储登录信息,设置HTTP拦截器 3. 路由导航与权限控制

  3. 主界面初始化