本文主要介绍2022年前端面试基础试题
CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。

注:浏览器的兼容性问题
IE5.X 和 6 在怪异模式中使用自己的非标准模型。这些浏览器的 width 属性不是内容的宽度,而是内容、内边距和边框的宽度的总和。
IE8 及更早IE版本不支持设置填充的宽度和边框的宽度属性。
解决IE8及更早版本不兼容问题可以在HTML页面声明 即可。
静态布局
以像素为基本单位布局,像素是绝对单位
特点:宽度都是固定的
缺点:浏览器无法适配,页面小,内容显示不全,页面大,两侧空白过大
HTML默认布局(DIV+CSS布局)
特点:相对单位,根据页面进行等比改变
缺点:文字无法进行流动
CSS 的 Float(浮动),会使元素向左或向右移动,其周围的元素也会重新排列。

float:leftfloat:right当我们想让两个
块级元素再同一行排列是,可以使用浮动布局实现
注:清除浮动,使用 clear:both;
元素浮动之后,周围的元素会重新排列,为了避免这种情况,使用 clear 属性。
特点:图文混排友好,块级元素布局友好
缺点:浮动元素脱离文档流,无法撑起父元素,造成父元素高度坍塌,需配合clear:both
display:tabledisplay:table-cell(空间平均划分:子级容器默认是自动平分宽度沾满父级容器;)对容器内内垂直居中,等高对齐布局友好
特点:兼容性好,易上手,
缺点:代码冗余,灵活性差,加载慢,SEO不友好
rem布局(css3新增单位,移动端友好度极高)@media screen(设置不同类型的媒体条件,适配不同设备pc大中小屏,移动端横竖屏,制作复杂,加载速度相对较慢)flex布局(弹性布局,易上手,兼容IE9及以上)vw和vh布局(视窗单位,分为100份,相对视窗实际宽高自动计算适配,适合大数据大屏展示,1px、较小像素不好适配)特点:适配不同视窗网页结构(pc+手机+平板等),无滚动条,
缺点:上手复杂度高,渲染速度相对较慢
article、footer、header、nav、sectioncalendar、date、time、email、url、searchHTML5新增语义化元素→传送门
Block Formatting Context 块级格式化上下文
一个BFC区域包含创建该上下文元素的所有子元素,但是不包括创建了新的BFC的子元素的内部元素,BFC是一块块独立的渲染区域,可以将BFC看成是元素的一种属性,拥有了这种属性的元素就会使他的子元素与世隔绝,不会影响到外部其他元素
如何形成BFC区域:
设置浮动,不包括none
设置定位,absoulte或者fixed
行内块显示模式,inline-block
设置overflow,即hidden,auto,scroll
表格单元格,table-cell
弹性布局,flex
.class{
height: 0.2rem;
line-height:0.2rem; // 垂直居中
text-align:center; // 水平居中
// vertical-align: middle; // 针对行内元素
}
.class{
display:flex;
align-items:center; // 垂直居中
justify-content:center; // 水平居中
}
.father{
position: relative;
}
.child{
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
}
.father{
display:table;
text align:center;
}
.child{
display:table cell;
vertical-align:middle;
}
.father{
position:relative;
}
.child{
width: 50%;
height: 50%;
overflow: auto;
margin: auto;
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
}
.class{
position:fixed;
z-index: 99;//层叠属性值
}
.class{
margin:0 auto;//根据实际上下边距调整
display:block;//根据实际实际要求是否转化成行列元素和块级元素
}
特点缺点→传送门
CSS选择器的作用是从HTML页面中找出特定的某类元素。
*{属性:属性值;}常用于设置所有HTML标记#id名{属性:属性值;}元素的id值是唯一的,只能对应于文档中某一个具体的元素。.class名{属性:属性值;}大多数HTML元素都可以定义class属性标签名{属性:属性值;}所有的HTML标记名都可以作为标签选择器,例如a、body、p、h1等等。用标签选择器定义的样式对页面中该类型的所有标签都有效。伪类选择器和伪对象选择器。前者分别对应HTML标记的五种状态:hover、:link、:focus、:visited、:active,后者根据对象内部的局部元素定义其样式:first-letter、:first-line、:before、:after。[标签名]{属性:属性值;}注:只有在规定了 !DOCTYPE 时,IE7 和 IE8 才支持属性选择器。在 IE6 及更低的版本中,不支持属性选择。标签名1,标签名2{属性:属性值;}同时匹配多个选择器,取多个选择器的并集,选择器之间用逗号隔开,如div,p{ }。标签名1 标签名2{属性:属性值;}用来选择特定元素的后代.标签名1 > 标签名2{属性:属性值;}表示匹配第二个选择器,且为第一个选择器的元素的后代。+和~)标签名1+标签名2{属性:属性值;}相邻兄弟选择器使用+号表示,如p+a{ },表示匹配紧跟第一个选择器并匹配第二个选择器的元素,如紧跟p元素后的a的元素。标签名1~标签名2{属性:属性值;}作用是查找某一个指定元素的后面的所有兄弟结点。css选择器→传送门
注:样式优先级内联>id选择器>类(class)选择器>标签选择器
TITLE,KEYWORDS和DESCRIPTION等都可以直接在后台进行动态修改和添加;每篇文章,管理者也可以设定不定的标题、关键词和描述,并可以生成地图文件sitemap.xml,以方便搜索引擎收录。HTML代码放前面。语义化HTML标签。alt属性需要填写相关信息。iframe不会被搜索引擎收录。let和const块级作用域,同一作用域不允许重复声明变量。
var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined。let和const不存在变量提升,即它们所声明的变量一定要在声明后使用,否则报错。var不存在暂时性死区let和const存在暂时性死区,只有等到声明变量的那一行代码出现,才可以获取和使用该变量var不存在块级作用域let和const存在块级作用域var允许重复声明变量let和const在同一作用域不允许重复声明变量var和let可以const声明一个只读的常量。一旦声明,常量的值就不能改变注:能用
const的情况尽量使用const,其他情况下大多数使用let,避免使用var
解构赋值就是从目标对象或数组中提取自己想要的变量
默认值
let arr=[1,2]
let [a,b,c=10]=arr //其中的10就是默认值
console.log(a,b,c);
//在浏览器中打印出来的是[1,2,10]
交换变量
let arr=[1,2,3]
let [a,b,c=10]=arr //其中的10就是默认值
console.log(a,b,c); //那么c就被重新赋值
//在浏览器中打印出来的是[1,2,3]
将剩余数组赋给一个变量
let [a,b,[...c]] = [1,2,3,4,5,6,7] // a=1,b=2,c=[3,4,5,6,7]
let [a,b,[...c]] = 'hello'
console.log(a,b,c) // h e [llo]
给新的变量名赋值
let {a,b} = {a:1,b:2} // a=1,b=2
通过新建一个 Promise,更加优雅地书写复杂的异步任务。我们之前遇到的异步任务都是一次异步,如果需要多次调用异步函数呢?例如,如果我想分三次输出字符串,第一次间隔 1 秒,第二次间隔 4 秒,第三次间隔 3 秒:
// 地狱式回调
setTimeout(function () {
console.log("First");
setTimeout(function () {
console.log("Second");
setTimeout(function () {
console.log("Third");
}, 3000);
}, 4000);
}, 1000);
// 构造 Promise 优化地狱回调
new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("First");
resolve();
}, 1000);
}).then(function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("Second");
resolve();
}, 4000);
});
}).then(function () {
setTimeout(function () {
console.log("Third");
}, 3000);
});
注:resolve 代表一切正常 reject 是出现异常时所调用的
// 拓展Promise "计时器" 代码
new Promise(function (resolve, reject) {
var a = 0;
var b = 1;
if (b == 0) reject("Divide zero");
else resolve(a / b);
}).then(function (value) {
console.log("a / b = " + value);
}).catch(function (err) {
console.log(err);
}).finally(function () {
console.log("End");
});
Promise类有.then().catch()和.finally()三个方法,这三个方法的参数都是一个函数,.then()可以将参数中的函数添加到当前Promise的正常执行序列,.catch()则是设定Promise的异常处理序列,.finally()是在Promise执行的最后一定会执行的序列。.then()传入的函数会按顺序依次执行,有任何异常都会直接跳到catch序列
symbol
symbol 是ES6 引入了一种新的基本数据类型(原始数据类型) Symbol ,表示独一无二的值。它是JavaScript 语言的第七种数据类型,前六种是: undefined 、 null 、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
// Symbol语法
let sym1 = Symbol('vitian')
let sym2 = Symbol('vitian')
Symbol('vitian') === Symbol('vitian') // false
sym1 === sym2 // false
// Symbol.for() 重新使用同一个 Symbol 值
let sym1 = Symbol.for('vitian')
let sym2 = Symbol.for('vitian')
sym1 === sym2 // true
Symbol的值是唯一的,用来解决命名冲突的问题
Symbol值不能与其他数据类型进行运算
Symbol定义得的对象的属性不能使用for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
Map
Map是一个类似于对象的数据类型,它是object的升级版,它可以将任何类型的数据作为key值;
// map的写法
let a = new Map(); //创建一个map对象
a.set(name , 'vitian'); //赋值
// 通过get方法获取 map数据类型中的数据
const b = new Map([ ['name','vitian'] , ['age',18] ]);
console.log(b.get('name')); //获取Map数据类型并在打印输出 'vitian'
// 去重数组的重复对象
let arr = [1, 2, 3, 2, 1, 1]
[... new Set(arr)] // [1, 2, 3]
共同点:set、Map可以储存不重复的值
不同点:set是以 [value, value]的形式储存元素,Map是以 [key, value] 的形式储存
函数参数的默认值
this.$nextTick(function(){
// Do SomeThing
})
this.$nextTick(()=>{
// Do SomeThing
})
异步函数 async function 中可以使用 await 指令,await 指令后必须跟着一个 Promise,异步函数会在这个 Promise 运行中暂停,直到其运行结束再继续运行。
// 将异步操作变得像同步操作一样容易,代码变得更好看,增加易读性
async function asyncFunc() {
await print(1000, "First");
await print(4000, "Second");
await print(3000, "Third");
}
asyncFunc();
// 处理异常的机制将用 try-catch 块实现
async function asyncFunc() {
try {
await new Promise(function (resolve, reject) {
throw "Some error"; // 或者 reject("Some error")
});
} catch (err) {
console.log(err);
// 会输出 Some error
}
}
asyncFunc();
新的class写法让对象原型的写法更加清晰,更加面向对象编程的语法。
class Foo{
static classMethod(){
return 'vitian';
}
}
console.log(Foo.classMethod()); // =>vitian
let foo=new Foo();
foo.classMethod();
// 父类的静态方法可以被子类继承
class Foo{
static classMethod(){
return 'vitian';
}
}
class Bar extends Foo{
}
console.log(Bar.classMethod()); // =>vitian
模块化是指将一个很大的程序文件,拆分为许多个小的文件,然后将多个小文件组合起来。
//export 暴露方式1 分别暴露 js/a1.js
export let a= 'vitian';
export function b() {
return "vitian.vip";
};
//export 暴露方式2 统一暴露 js/a2.js
let a= 'vitian';
function b() {
return "vitian.vip";
};
export {a, b};
//export 暴露方式3 默认暴露 js/a3.js
export default {
a: 'vitian',
change: function(){
return "vitian.vip";
}
}
// import 导入方式1 通用的导入方式
import * as _a1 from "js/a1.js"; // 引入 a1.js 模块内容
_a1.a; // vitian
import * as _a2 from "js/a2.js"; // 引入 a2.js 模块内容
_a2.b(); // vitian.vip
import * as _a3 from "js/a3.js"; // 引入 a3.js 模块内容
_a3.default.change(); // vitian.vip
// import 导入方式2 解构赋值形式
import {a,b} from "js/a1.js"; // 引入 a1.js 模块内容
console.log(a); // vitian
import {a as _a2,b as _b2} from "js/a2.js"; // 引入 a2.js 模块内容
console.log(_b2); // vitian.vip
import {default as _a3} from "js/a3.js"; // 引入 3.js 模块内容
console.log(_a3.change()); // vitian.vip
// import 导入方式3 只针对于默认暴露
import _a3 from "js/a3.js"; // 引入 3.js 模块内容
console.log(_a3.change()); // vitian.vip
用于判断数组是否包含给定的值,返回一个布尔值
// 判断字符串
var str = 'vitian'
console.log(str.includes('vi')) // 返回的结果是true
// 判断数组
var arr = ['a','b','c']
console.log(arr.includes('a')) // 返回结果是true
//判断NaN
var arr = ['a','b',NaN]
console.log(arr.includes(NaN)) //返回的记过是true
find()和findIndex()查找函数
// 用来查找目标元素,找出第一个符合条件的数组,找到就返回该元素,找不到返回undefined.
array.find((value, index, arr) => {value === '匹配对象'}
// 用来查找目标元素,找到就返回元素的位置,找不到就返回-1。
array.findIndex((value, index, arr) => {value === '匹配对象'})
// 方法1 数组.push():向数组的末尾添加元素 params多个任意类型值
let arr = [1,2],
newArr = arr.push(3);
console.log(arr) // push方法会改变原数组,向数组的末尾添加元素 => [1,2,3]
console.log(newArr) // 增后的数组长度 => 3
// 方法2 数组.unshift():向数组开头添加元素 params多个任意类型值
let arr = [1,2],
newArr = arr.unshift(3);
console.log(arr) // unshift方法会改变原数组,向数组开头添加元素 => [3,1,2]
console.log(newArr) // 增后的数组长度 => 3
// 方法3 数组.concat():向数组的末尾添加元素 params多个任意类型值
let arr = [1,2],
newArr = arr.concat(3);
console.log(arr) // concat方法不会改变原数组 => [1,2]
console.log(newArr) // 向数组的末尾添加元素 => [1,2,3]
// 方法1 数组.pop():删除数组中最后的那项 params多个任意类型值
let arr = [1,2,3],
newArr = arr.pop();
console.log(arr) // pop方法会改变原数组,删除数组中最后的那项 => [1,2]
console.log(newArr) // 被删除的那项 => 3
// 方法2 数组.shift():删除数组中开头的那项元素 params多个任意类型值
let arr = [1,2,3],
newArr = arr.shift();
console.log(arr) // shift方法会改变原数组,删除数组中开头的那项元素 => [2,3]
console.log(newArr) // 被删除的那项 => 1
// 数组.splice():数组从索引n开始向后截取n个元素,并在此插入元素n params多个任意类型值
let arr = [1,2,3],
newArr = arr.splice(0,1,3);
console.log(arr) // splice方法会改变原数组,从索引0开始向后截取1个元素,并在此插入元素3 => [3,2,3]
console.log(newArr) // 被截取的那项 => [0]
// 拓展1 增
let arr = [1,2,3],
newArr = arr.splice(arr.length-1,0,4);
console.log(arr) //从数组末尾开始向后截取0个元素,并在此插入新元素7 => [1,2,3,4]
console.log(newArr) // 被截取的项 => []
// 拓展2 删
let arr = [1,2,3],
newArr = arr.splice(0,1);
console.log(arr) //从索引0开始向后截取1个元素 => [2,3]
console.log(newArr) // 被截取的项 => [0]
// slice方法不会改变原数组
let arr = [1,2,3],
newArr = arr.slice(0,arr.length);
console.log(arr) //从索引0开始查询到数组末尾的后一项(复制数组) => [1,2,3]
console.log(newArr) // 复制数组 => [1,2,3]
let arr = [1,2,3],
newArr = arr.slice(0.9,1.1); // 当x和y为小数时,会先通过parseInt转换为整数在进行查询操作 => 从索引0开始查询到索引为1的元素
console.log(arr) // => [1,2,3]
console.log(newArr) // => [1]
冒泡排序法,也叫升序排序法,但是相比起二分法查找只能应用于有序数列
let arr = [1,3,7,5,9];
bubblingFunc(arr){
if(Array.isArray(arr)){
if(arr.length==1){
return arr;
}
let data = null;
for(let i = 0; i< arr.length; i++) {
for (let j = 0; j< arr.length; j++) {
if(arr[j] > arr[i]) {
data = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = data;
}
}
}
return arr;
}
}
console.log(bubblingFunc(arr)); // => [1,3,5,7,9]
在未排序序列中找到最小元素,把它放到排序序列起始位置。从剩余未排序序列中继续寻找最小元素,然后放在排序序列末尾。以此类推,直至所有元素排序完成。
let arr = [1,3,7,5,9];
selectionFunc(arr){
if(Array.isArray(arr)){
if(arr.length==1){
return arr;
}
let data = null;
for(let i = 0; i< arr.length-1; i++) {
let min = i;
for (let j = i+1; j< arr.length; j++) {
min = arr[min] < arr[j] ? j : min
}
[arr[i],arr[min]] = [arr[min],arr[i]];
}
return arr;
}
}
console.log(selectionFunc(arr)); // => [9,7,5,3,1]
从数列中取出一个数作为参考,分区过程。将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。对左右区间重复第二步,直到各区间只有一个数。
let arr = [1,3,7,5,9];
recursiveFunc(arr){
if(Array.isArray(arr)){
if(arr.length==1){
return arr;
}
let mid= Math.ceil(arr.length / 2);
let val = arr.splice(mid, 1);
let left = [];
let right = [];
array.forEach((value)=> {
if (value > val) {
left.push(value);
}
else {
right.push(value);
}
});
return recursiveFunc(left).concat(val , digui(right));
}
}
console.log(recursiveFunc(arr)); // => [9,7,5,3,1]
将要排序的数组分成两部分,每次从后面的部分取出索引最小的值插入前面适当的位置。
let arr = [1,3,7,5,9];
insertFunc(arr){
if(Array.isArray(arr)){
if(arr.length==1){
return arr;
}
for (let i = 1; i < arr.length; i++) {
let current = arr[i];
let preIndex = i - 1;
while (preIndex >= 0 && arr[preIndex] < current) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
arr[preIndex + 1] = current;
}
return arr;
}
}
console.log(insertFunc(arr)); // => [9,7,5,3,1]
let arr = [1,3,7,5,9];
arr .sort((a,b)=>{
return b - a;
})
console.log(arr );// => [9,7,5,3,1]
let arr = [1,3,7,5,9];
arr.reverse();
console.log(arr);// => [9,5,7,3,1]
const array = [1,1,2,'a','a','b',true,true,false,undefined,undefined,NaN,NaN];
const result = Array.from(new Set(array)) // =>[1,2,a,b,true,false,undefined,NaN]
removeFunc(array) {
for (let i = 0; i < array.length; i++) {
for (let j = i + 1; j < array.length; j++) {
if (array[i] === array[j]) {
array.splice(j, 1)
len--
j--
}
}
}
return array
}
const result = removeFunc(array) // =>[1,2,a,b,true,false,undefined,NaN]
removeFunc(array) {
let data = []
array.forEach(e=>{
if(data.indexOf(e) === -1) {
data.push(e)
}
})
return data
}
const result = removeFunc(array) // =>[1,2,a,b,true,false,undefined,NaN]
removeFunc(array) {
let data = []
array.forEach(e=>{
if(!data.includes(e)) {
data.push(e)
}
})
return data
}
const result = removeFunc(array) // =>[1,2,a,b,true,false,undefined,NaN]
removeFunc(array) {
return array.filter((e, i) => {
return array.indexOf(e) === i
})
}
const result = removeFunc(array) // =>[1,2,a,b,true,false,undefined,NaN]
removeFunc(array) {
let map = new Map()
let data = []
array.forEach(e=>{
if (!map.has(e)) { // has()用于判断map是否包为item的属性值
map.set(e, true) // 使用set()将item设置到map中,并设置其属性值为true
data.push(e)
}
})
return data
}
const result = removeFunc(array) // =>[1,2,a,b,true,false,undefined,NaN]
存储大小:
cookie数据大小不能超过4k,sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。
有效时间:localStorage存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;sessionStorage数据在当前浏览器窗口关闭后自动删除;cookie设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。
数据与服务器之间的交互方式:cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端;sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。
闭包是指有权访问另外一个函数作用域中的变量的函数,可以理解为(能够读取其他函数内部变量的函数)
特点: 正常函数执行完毕后,里面声明的变量被垃圾回收处理掉,但是闭包可以让作用域里的 变量,在函数执行完之后依旧保持没有被垃圾回收处理掉。
缺点:闭包会导致内存占用过高,因为变量都没有释放内存。
for (let i = 0; i < 4; i++) {
setTimeout(()=> {
console.log(i);
}, 300);
}
// => 4
基于JQuery的AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。
特点:
缺点: