赞
踩
本文主要介绍2022年前端面试基础试题
CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际内容。
注:浏览器的兼容性问题
IE5.X 和 6 在怪异模式中使用自己的非标准模型。这些浏览器的 width 属性不是内容的宽度,而是内容、内边距和边框的宽度的总和。
IE8 及更早IE版本不支持设置填充的宽度和边框的宽度属性。
解决IE8及更早版本不兼容问题可以在HTML页面声明 即可。
静态布局
以像素为基本单位布局,像素是绝对单位
特点:宽度都是固定的
缺点:浏览器无法适配,页面小,内容显示不全,页面大,两侧空白过大
HTML默认布局(DIV+CSS布局)
特点:相对单位,根据页面进行等比改变
缺点:文字无法进行流动
CSS 的 Float(浮动),会使元素向左或向右移动,其周围的元素也会重新排列。
float:left
float:right
当我们想让两个
块级元素
再同一行排列是,可以使用浮动布局
实现
注:清除浮动,使用 clear:both;
元素浮动之后,周围的元素会重新排列,为了避免这种情况,使用 clear 属性。
特点:图文混排友好,块级元素布局友好
缺点:浮动元素脱离文档流,无法撑起父元素,造成父元素高度坍塌,需配合clear:both
display:table
display:table-cell
(空间平均划分:子级容器默认是自动平分宽度沾满父级容器;)对容器内内垂直居中,等高对齐布局友好
特点:兼容性好,易上手,
缺点:代码冗余,灵活性差,加载慢,SEO不友好
rem
布局(css3
新增单位,移动端友好度极高)@media screen
(设置不同类型的媒体条件,适配不同设备pc大中小屏,移动端横竖屏,制作复杂,加载速度相对较慢)flex
布局(弹性布局,易上手,兼容IE9及以上)vw
和vh
布局(视窗单位,分为100份,相对视窗实际宽高自动计算适配,适合大数据大屏展示,1px、较小像素不好适配)特点:适配不同视窗网页结构(pc+手机+平板等),无滚动条,
缺点:上手复杂度高,渲染速度相对较慢
article
、footer
、header
、nav
、section
calendar
、date
、time
、email
、url
、search
HTML5新增语义化元素→传送门
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
不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。
特点:
缺点:
props
和this.$emit
ref
链:父组件要给子组件传值,在子组件上定义一个 ref
属性,这样通过父组件的 $refs
属性就可以获取子组件的值了,也可以进行父子,兄弟之间的传值($parent
/ $children
与 ref类似)EventBus
:使用一个空的 VUE 实例作为事件总线,自定义事件EventBus.$on
、 EventBus.$off
、EventBus.$emit
vuex
m:
model
v:view
vm:viewModel
dom
监听data
,反之data
通过指令操作dom
,即数据驱动视图
。
vuex
是一个专门为 vue
构建的状态管理工具,主要是为了解决多组间之间状态共享问题。强调的是集中式管理,(组件与组件之间的关系变成了组件与仓库之间的关系)
vuex
的核心包括:state
(存放状态)、mutations
(同步的更改状态)、actions
(发送异步请求,拿到数据)、getters
(根据之前的状态派发新的状态)、modules
(模块划分)
state
发布一条新的数据,在 getters
里面根据状态派发新的状态,actions
发送异步请求获取数据,然后在 mutations
里面同步的更改数据。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。