我的前端面试复习

114 阅读16分钟

HTML

标签的常用属性及其作用

src属性

作用:指定图像的URL地址,是最重要的属性,用于告诉浏览器从那获取显示的图像

alt属性

作用:为图像提供文本描述.当图像由于某种原因(例如网络问题无法加载,浏览器不支持时)不能显示时,浏览器会使用这个代替文本

width和height属性

作用:用于设置图像的宽度和高度,单位可以是像素或者百分比

title属性

作用:用于提供图像的额外信息,当用户将鼠标悬停再图像上时,会显示这个标题文本

请简述HTML语义化标签的作用

增加代码可读性和可维护性

直接展示了页面结构,一眼就能看出该部分的用途,代码阅读更加清晰

有利于搜索引擎优化(seo)

搜索引擎能更好理解页面结构和内容,从而提升网站在搜索结果中的排名

方便屏幕阅读器等辅助设备理解页面结构

input标签有哪些常见的type属性值

  • text 创建一个单行文本输入框,用户可以输入文本信息,比如输入用户名邮箱
  • password 创建一个密码输入框,输入的内容会以掩码形式显示
  • radio 创建单选按钮,通常将多个具有相同name 属性的单选按钮形成一组,用户只能选一个
  • checkbox 创建复选框,用户可以选择多个选项
  • submit 创建一个提交按钮,用于将表单数据提交到服务器中
  • reset 创建一个重置按钮,用于将表单元素的值重置为初始值
  • file 创建一个文件选择框,允许用户选择本地文件进行上传

浏览器存储

浏览器存储主要有五种方式,分别是 cookie,LocalStorage ,seesionStorage,webSQL,indexedDB

cookie

Cookie,类型未[小型文本文件],指某些网站为了辨别用户身份而存储在用户本地终端上的数据,不受前端掌控,由后端控制.是为了解决HTTP无状态导致的问题. 作为一段一般不超过4KB的小型文本数据,它由一个名称(name),一个值(value)和其他几个用于控制cookie有效期,安全性,使用范围的可选属性组成

但是Cookie在每次请求中都会被发送,如果不使用HTTPS对其进行加密,其保存的信息很容易被窃取,导致安全风险

常用属性

  • expires:用于设置cookie的过期时间

    Expires=Wed,21 Oct 2015 07:28:00 GMT

  • Max-Age:用于设置在cookie失效前需要经过的秒数(优先级高于expires)

    Max-Age=604800

  • Domain:指定了Cookie可以送达的主机名

  • Path:指定了一个URL路径,这个路径必须出现在要请求的资源的路径中才可以发送Cookie首部

    Path=/docs #/docs/Web/下的资源会带Cookie 首部

Cookie的使用: document.cookie='名字=值';

修改: Set-Cookie:name=aa;domain=aa.net;path=/#服务端设置 document.cookie =name=bb;domain=aa.net;path=/ #客户端设置

LocalStorage

CSS

在CSS中如何清除浮动

什么是浮动

在css中,浮动(Float)是一种布局技术,最初设计用于实现文字环绕图片的效果,后来被广泛用于实现多列布局.浮动元素会脱离文档流,向左或向右移动,直到碰到父容器或者其他浮动元素的边缘

浮动语法 float:left ; //向左浮动 float:right; //向右浮动 效果:

  • 浮动元素会脱离文档流
  • 其他非浮动元素会围绕浮动元素排列
  • 多个浮动元素会依次排列,直到父容器宽度不够再换行

浮动产生的问题:

  • 浮动元素脱离文档流后会导致父容器高度塌陷,进而影响布局

如何清除浮动

为了解决浮动带来的问题,需要使用清除浮动的技术

空元素清除法

使用clear:both

浮动元素
//清除浮动
父元素触发BFC

.parent{ overflow:hidden; }

伪元素清除法(推荐)

.clearfix::after{ content:""; display:block; clear:both; }

CSS中盒模型有哪些组成部分,并解释一下他们的作用

盒模型在CSS中是一个核心的概念,用于描述元素在页面中所占空间的大小 盒模型由内容(content),内边距(padding),边框(border),外边距(margin)所组成

内容部分(content)

这里是元素实际显示内容的部分,比如图像或者文本等

内边距(padding)

内边距是内容与边框之间的距离.内边距在内容周围创建了一个透明区域,可以使得内容和边框之间有一定的间隔,起到美化页面布局的作用 通常为上右下左设置其大小宽度

边框(border)

边框包裹着内容和内边距,可以通过设置边框的宽度样式和颜色来定制边框的外观. 边框的宽度由 border-width设置 样式由border-style设置 颜色由border-color设置

外边距(margin)

外边距是边框与其他元素边框之间的距离.主要用于控制元素与其他元素之间的间距,使得页面布局更加合理 同样为上右下左

CSS选择器有哪些

元素选择器

通过元素名称来选中元素,如p选择所有

标签

类选择器

以.开头,通过元素的class属性来选择元素

ID选择器

以#开头,通过元素的id属性来选择元素,如#main可以选择id为main的元素,由于id在页面中具有唯一性,所有ID选择器只会匹配一个元素

属性选择器

根据元素的属性来选择元素

伪类选择器

用于选择处于特定状态的元素

伪元素选择器

用于选择元素中的特定部分

组合选择器

请解释CSS中的BFC(块级格式化上下文)

块级格式化上下文是一个独立的渲染区,规定了内部的块级元素如何布局,并且与外部元素相互隔离

触发BFC的情况

  • 浮动元素(float不为none)
  • 绝对定位元素(position值为absolute或者fixed)\
  • 行内块元素(display值为inline-block)
  • 表格单元格(display值为table-cell)

BFC的作用

  • 清除浮动.防止margin重叠
  • 自适应多栏布局场景,利用BFC相互隔离的特性,可以实现布局效果互不影响

Javascript

什么是闭包,其使用场景有哪些

什么是闭包

闭包:是指一个函数能够访问并记住其词法作用域,即使这个函数在其词法作用域外执行 形成:当一个函数内部定义了另一个函数,并且内部函数引用了外部函数的变量,就会形成闭包

实例: function outer(){ let count = 0; //外部函数的变量

function inner(){
    count++; //内部函数访问外部函数的变量
    console.log(count);
}

return inner; //返回内部函数

}

const closuerFn = outer(); //调用outer函数,返回inner函数 closuerFn(); //输出1 closuerFn(); //输出2 closuerFn(); //输出3

闭包的使用场景

数据封装与私有变量

闭包可以创建私有变量,避免全局污染

函数柯里化

闭包可以用于实现函数柯里化,即将一个多参数函数转化为一系列单参数函数

回调函数与异步编程

闭包常用于回调函数中,保留外部作用域的变量

防抖和节流

闭包可以实现防抖和节流函数,优化性能

闭包的注意事项

内存泄漏
  • 闭包会保留对外部作用域的引用,可能会导致内存无法释放
  • 解决方法: 在不需要时手动解除引用
性能问题

过度使用闭包可能会导致内存占用过高,影响性能

闭包的面试常见问题

什么是闭包

闭包是指一个函数能够访问并记住其词法作用域,即使这个函数在词法作用域执行之外

闭包的使用场景有哪些

数据封装,函数柯里化,防抖与节流,回调函数

闭包会导致什么问题

可能会导致内存泄漏和性能问题

如何避免闭包的内存泄漏

在不需要的时候手动解除对闭包的引用

Js中的各个数组方法

数组的创建与转换

from()

将类数组对象或可迭代对象转化为数组

const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3}; const newArray = Array.from(arrayLike); console.log(newArray); // 输出: ['a', 'b', 'c']

of()

创建一个包含指定元素的数组

const newArray = Array.of(1, 2, 3); console.log(newArray); // 输出: [1, 2, 3]

数组查询

indexOf

返回指定元素在数组中第一次出现的索引,如不存在则返回-1

const numbers = [1, 2, 3, 2]; const index = numbers.indexOf(2); console.log(index); // 输出: 1

lastIndexOf

返回指定元素在数组中的最后一次出现的索引,如不存在则返回-1

const numbers = [1, 2, 3, 2]; const index = numbers.lastIndexOf(2); console.log(index); // 输出: 3

includes

判断数组是否包含指定元素,返回布尔值

const numbers = [1, 2, 3]; const includes = numbers.includes(2); console.log(includes); // 输出: true

find

返回数组中满足提供的测试函数的第一个元素值,如没有找到则返回undefined

const numbers = [1, 2, 3, 4]; const found = numbers.find((element) => element > 2); console.log(found); // 输出: 3

findIndex

返回数组中满足听过测试函数的第一个元素的索引值,如果没有就返回-1

const numbers = [1, 2, 3, 4]; const foundIndex = numbers.findIndex((element) => element > 2); console.log(foundIndex); // 输出: 2

数组迭代

forEach

对数组的每个元素执行一次提供的函数,无返回值

const numbers = [1, 2, 3]; numbers.forEach((element) => { console.log(element); });

map

创建一个新数组,其结果是该数组中的每个元素都经过某种处理

const numbers = [1, 2, 3]; const newNumbers = numbers.map((element) => element * 2); console.log(newNumbers); // 输出: [2, 4, 6]

filter

创建一个新数组,新数组中的元素是原数组中满足条件的所有元素

const numbers = [1, 2, 3, 4]; const filteredNumbers = numbers.filter((element) => element % 2 === 0); console.log(filteredNumbers); // 输出: [2, 4]

reduce

对数组中的所有元素执行一个累加器函数(升序执行),将其结果汇总为单个返回值

const numbers = [1, 2, 3]; const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0); console.log(sum); // 输出: 6

some

判断数组中是否有一个元素满足提供的测试函数,返回布尔值

const numbers = [1, 2, 3]; const result = numbers.some((element) => element > 2); console.log(result); // 输出: true

every

判断数组中的所有元素是否都满足提供的测试函数,返回布尔值

const numbers = [1, 2, 3]; const result = numbers.every((element) => element > 0); console.log(result); // 输出: true

数组修改

push

再数组的末尾添加一个或多个元素,并返回新数组的长度

const numbers = [1, 2]; const newLength = numbers.push(3); console.log(numbers); // 输出: [1, 2, 3] console.log(newLength); // 输出: 3

pop

删除数组的最后一个元素,并返回删除的元素

const numbers = [1, 2, 3]; const poppedElement = numbers.pop(); console.log(numbers); // 输出: [1, 2] console.log(poppedElement); // 输出: 3

shift

删除数组的第一个元素,并返回删除的元素

const numbers = [1, 2, 3]; const shiftedElement = numbers.shift(); console.log(numbers); // 输出: [2, 3] console.log(shiftedElement); // 输出: 1

unshift

再数组的开头添加一个或多个元素,并返回新数组的长度

const numbers = [1, 2]; const newLength = numbers.unshift(0); console.log(numbers); // 输出: [0, 1, 2] console.log(newLength); // 输出: 3

splice

用于在数组中添加或删除元素

const numbers = [1, 2, 3, 4]; numbers.splice(1, 1, 5); // 从索引1处删除1个元素,并在该位置插入5 console.log(numbers); // 输出: [1, 5, 3, 4]

数组排序与反转

reverse

反转数组中元素的顺序

const numbers = [1, 2, 3]; numbers.reverse(); console.log(numbers); // 输出: [3, 2, 1]

sort

对数组的元素进行排序,默认顺序是根据字符串窜unicode码点

const numbers = [3, 1, 2]; numbers.sort((a, b) => a - b); console.log(numbers); // 输出: [1, 2, 3]

数组拼接与切割

concat

用于合并两个或多个数组,返回一个新数组,不改变原数组

const array1 = [1, 2]; const array2 = [3, 4]; const newArray = array1.concat(array2); console.log(newArray); // 输出: [1, 2, 3, 4]

slice

提取数组的一部分,返回一个新数组,不改变原数组

const numbers = [1, 2, 3, 4]; const slicedArray = numbers.slice(1, 3); console.log(slicedArray); // 输出: [2, 3]

浅拷贝和深拷贝

浅拷贝

浅拷贝只复制对象的第一层属性.如果对象的属性值是引用类型(如数组,对象等),浅拷贝会复制引用(即内存地址),而不是创建新的对象. 因此,修改嵌套对象会影响原对象和拷贝对象.

实现浅拷贝的方式
  • 使用Object.assign() const original = { a: 1, b: { c: 2 } }; const shallowCopy = Object.assign({}, original);

shallowCopy.b.c = 3; // 修改嵌套对象 console.log(original.b.c); // 输出 3,原对象也被修改

  • 使用扩展运算符 const original = { a: 1, b: { c: 2 } }; const shallowCopy = { ...original };

shallowCopy.b.c = 3; // 修改嵌套对象 console.log(original.b.c); // 输出 3,原对象也被修改

深拷贝

深拷贝会递归地复制对象的所有层级,包括嵌套对象和数组.深拷贝后的对象与原对象完全独立,修改拷贝对象不会影响原对象

实现深拷贝的方式
  • 使用JSON.parse(JSON.stringify()) const original = { a: 1, b: { c: 2 } }; const deepCopy = JSON.parse(JSON.stringify(original));

deepCopy.b.c = 3; // 修改嵌套对象 console.log(original.b.c); // 输出 2,原对象不受影响

  • 使用递归实现深拷贝 function deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; } const clone = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = deepClone(obj[key]); } } return clone; }

const original = { a: 1, b: { c: 2 } }; const deepCopy = deepClone(original);

deepCopy.b.c = 3; // 修改嵌套对象 console.log(original.b.c); // 输出 2,原对象不受影响

  • 使用第三方库 const _ = require('lodash'); const original = { a: 1, b: { c: 2 } }; const deepCopy = _.cloneDeep(original);

deepCopy.b.c = 3; // 修改嵌套对象 console.log(original.b.c); // 输出 2,原对象不受影响

栈和堆

在Javascript中,栈和堆是内存管理的两个重要概念,用于存储不同类型的数据

栈(stack)

栈是一种线性数据结构,遵循后进先出的原则.它用于存储基本数据类型(如number,string,boolean等)以及函数调用的执行上下文(如局部变量,函数参数等)

特点

  • 内存分配:内存分配和释放是自动的,由系统管理
  • 速度块:栈的访问速度非常快,因为内存是连续的
  • 大小固定:栈的大小是有限的,通常比堆小
  • 生命周期:栈中的数据生命周期与函数调用有关,函数执行结束后,栈中的数据会自动释放

示例

function foo() { let a = 10; // a 存储在栈中 let b = "hello"; // b 存储在栈中 return a + b; } foo(); // 函数执行结束后,栈中的数据自动释放

优点:

  • 访问速度快
  • 内存管理自动完成,无需手动释放
  • 适合存储小型,生命周期短的数据

缺点:

  • 内存大小有限,不适合存储大量数据
  • 不适合存储复杂或动态大小的数据

堆(heap)

堆是一种非线性数据结构,用于存储引用数据类型(如对象,数组,函数等).堆的内存分配是动态的,大小不固定

特点:

  • 内存分配:需要手动管理(在JS种由垃圾回收机制自动管理)
  • 速度较慢:堆的访问速度比栈满,因为内存是不连续的
  • 大小灵活:堆的大小可以动态调整,适合存储大量数据
  • 生命周期:堆种的数据生命周期不确定,直到没有引用指向它时,才会被垃圾回收机制释放

示例:

let obj = { name: "Alice", age: 25 }; // obj 存储在堆中 let arr = [1, 2, 3]; // arr 存储在堆中

优点:

  • 内存大小灵活,适合存储大量数据
  • 适合存储复杂或动态大小的数据

缺点:

  • 访问速度较慢
  • 内存管理复杂(有可能引发内存泄漏)

垃圾回收机制

JS中的垃圾回收机制是自动管理内存的一种机制,用于释放不再使用的内存,防止内存泄漏.JS引擎内置了垃圾回收器,开发者通常不需要手动管理内存

垃圾回收的基本原理

垃圾回收的核心思想是找到不再被引用的对象,并释放他们占用的内存. Js中的垃圾回收机制主要基于两种算法

引用计数(reference counting)

原理:每个对象有一个引用计数,负责记录有多少引用指向它,当引用计数为0时,对象被视为垃圾,可以被回收 优点:实现简单,可以立即回收垃圾 缺点:无法处理循环引用的情况(即两个或多个对象相互引用,但不再被其他对象引用)

示例: let a = { name: "Alice" }; let b = { name: "Bob" };

a.friend = b; // a 引用 b b.friend = a; // b 引用 a

a = null; // a 不再引用对象 b = null; // b 不再引用对象

// 虽然 a 和 b 不再被引用,但由于循环引用,引用计数不为 0,无法回收

标记清除法(mark and sweep)

原理:从根对象开始,递归标记所有可达对象.未标记对象视为垃圾,随后清除 优点:可以处理循环引用 缺点:需要暂停程序执行,可能会导致内存泄漏和性能问题

示例: function createObjects() { let a = { name: "Alice" }; let b = { name: "Bob" };

a.friend = b; b.friend = a;

return "done"; }

createObjects(); // 函数执行结束后,a 和 b 不再被引用,标记-清除算法会回收它们

现代垃圾回收机制的优化

为了提高垃圾回收的效率,现代JS引擎对标记清除算法进行了优化,主要包括以下技术

  1. 分代回收 原理:将内存分为新生代和老生代
  • 新生代:存放生命周期较短的对象,采用Scavenge算法进行频繁回收
  • 老生代:存放生命周期长的对象,采用标记清除进行较少次数的回收
  1. 增量回收 原理:将垃圾回收过程分成多个小步骤,与程序执行交替进行,避免长时间暂停 优点:减少对性能的影响

  2. 空闲时间回收 原理:在程序空闲时执行垃圾回收机制 优点:进一步减少对程序性能的影响

内存泄漏

尽管JS中有垃圾回收机制,但如果代码编写不当也会造成内存泄漏

意外的全局变量

function leak() { leakedVar = "I am a global variable"; // 未使用 var/let/const,导致 leakedVar 成为全局变量 }

未清理的定时器或回调函数

let data = getData(); setInterval(() => { console.log(data); }, 1000);

// 即使不再需要 data,定时器仍持有对 data 的引用,导致无法回收

闭包

function createClosure() { let largeData = new Array(1000000).fill("data"); return function() { console.log("Closure"); }; }

let closure = createClosure(); // largeData 被闭包引用,无法回收

未移除的DOM引用

let button = document.getElementById("myButton"); button.addEventListener("click", onClick);

// 即使移除按钮,事件监听器仍持有对 button 的引用

如何避免闭包的内存泄漏

  • 避免创建意外的全局变量
  • 及时清理定时器,事件监听器和回调函数
  • 避免不必要的闭包
  • 使用弱引用存储临时数据