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引擎对标记清除算法进行了优化,主要包括以下技术
- 分代回收 原理:将内存分为新生代和老生代
- 新生代:存放生命周期较短的对象,采用Scavenge算法进行频繁回收
- 老生代:存放生命周期长的对象,采用标记清除进行较少次数的回收
-
增量回收 原理:将垃圾回收过程分成多个小步骤,与程序执行交替进行,避免长时间暂停 优点:减少对性能的影响
-
空闲时间回收 原理:在程序空闲时执行垃圾回收机制 优点:进一步减少对程序性能的影响
内存泄漏
尽管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 的引用
如何避免闭包的内存泄漏
- 避免创建意外的全局变量
- 及时清理定时器,事件监听器和回调函数
- 避免不必要的闭包
- 使用弱引用存储临时数据