前端面试题

浏览器


输入一个URL到页面过程中发生了什么(高频)

  1. 首先在浏览器中输入URL

  1. 查找缓存:浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,如果有则显示页面内容。如果没有则进行下一步。

  1. DNS域名解析:浏览器向DNS服务器发起请求,解析该URL中的域名对应的IP地址。DNS服务器是基于UDP的,因此会用到UDP协议。

  1. 建立TCP连接:解析出IP地址后,根据IP地址和默认80端口,和服务器建立TCP连接

  1. 发起HTTP请求:浏览器发起读取文件的HTTP请求,该请求报文作为TCP三次握手的第三次数据发送给服务器

  1. 服务器响应请求并返回结果:服务器对浏览器请求做出响应,并把对应的html文件发送给浏览器

  1. 关闭TCP连接:通过四次挥手释放TCP连接

  1. 浏览器渲染:客户端(浏览器)解析HTML内容并渲染出来,浏览器接收到数据包后的解析流程为:

  • 构建DOM树:词法分析然后解析成DOM树(dom tree),是由dom元素及属性节点组成,树的根是document对象

  • 构建CSS规则树:生成CSS规则树(CSS Rule Tree)

  • 构建render树:Web浏览器将DOM和CSSOM结合,并构建出渲染树(render tree)

  • 布局(Layout):计算出每个节点在屏幕中的位置

  • 绘制(Painting):即遍历render树,并使用UI后端层绘制每个节点。

浏览器渲染机制、重绘、重排

网页生成的过程:

  • 构建DOM树:词法分析然后解析成DOM树(dom tree),是由dom元素及属性节点组成,树的根是document对象

  • 构建CSS规则树:生成CSS规则树(CSS Rule Tree)

  • 构建render树:Web浏览器将DOM和CSSOM结合,并构建出渲染树(render tree)

  • 布局(Layout):计算出每个节点在屏幕中的位置

  • 绘制(Painting):即遍历render树,并使用UI后端层绘制每个节点。

重排:当DOM的变化影响了元素的几何信息(DOM对象的位置和尺寸大小),浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。

触发条件:

  1. 添加或者删除可见的DOM元素

  1. 元素尺寸改变——边距、填充、边框、宽度和高度

重绘:当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。

触发条件:改变元素的color、background、box-shadow等属性

HTML相关


常见的语义化标签

header nav main article section aside footer

语义化的优点如下:

  • 对机器友好,带有语义的文字表现力丰富,更适合搜索引擎的爬虫爬取有效信息,有利于SEO。除此之外,语义类还支持读屏软件,根据文章可以自动生成目录;

  • 对开发者友好,使用语义类标签增强了可读性,结构更加清晰,开发者能清晰的看出网页的结构,便于团队的开发与维护

H5新特新

  1. 语义化标签

  1. 媒体标签

  1. 表单

  1. DOM查询操作

  1. Web存储

  1. 拖拽

  1. ...

script标签中defer和async的区别

多个带async属性的标签,不能保证加载的顺序;多个带defer属性的标签,按照加载顺序执行;

async属性,表示后续文档的加载和执行与js脚本的加载和执行是并行进行的,即异步执行;defer属性,加载后续文档的过程和js脚本的加载(此时仅加载不执行)是并行进行的(异步),js脚本需要等到文档所有元素解析完成之后才执行,DOMContentLoaded事件触发执行之前。

CSS相关


盒模型(高频)

盒模型都是由四个部分组成的,分别是margin、border、padding和content。

  • 标准盒模型的width和height属性的范围只包含了content,

  • IE盒模型的width和height属性的范围包含了border、padding和content。

HTML 各块级,行内元素

https://blog.csdn.net/qq_42952262/article/details/103834029

块级元素

容器级标签:div , h系列 , li , dt ,dd

  1. 总是从新的一行开始

  1. 高度、宽度都是可控的

  1. 宽度没有设置时,默认为100%

  1. 块级元素中可以包含块级元素和行内元素

行内元素

文本级标签:p , span , a , b , i , u , em

  1. 和其他元素都在一行

  1. 高度、宽度以及内边距都是不可控的

  1. 宽高就是内容的高度,不可以改变

  1. 行内元素只能行内元素,不能包含块级元素

块级元素和行内元素的相互转换

  我们可以通过display属性将块级元素(比如div)和行内元素进行相互转换。

  • display:inline;

  那么这个标签将变为行内元素,即:

    1,此时这个div将不能设置宽度和高度了。

    2,此时这个div可以和其他行内元素并排了。

  同样的到了我们也可以用display将行内元素(比如span)转行成块级元素。

  • display:block;

  那么这个span标签将变为块级标签,即:

    1,此时这个span能够设置宽度,高度。

    2,此时这个span必须独占一行,其他元素无法与之并排。

    3,如果不设置宽度,将占满父级。

其他
说一下浮动?

浮动的作用,设置浮动的图片,可以实现文字环绕图片,设置了浮动的块级元素可以排列在同一行,设置了浮动的行内元素可以设置宽高,同时可以按照浮动设置的方向对齐排列盒子。

设置浮动元素的特点:

· 设置了浮动,该元素脱标。元素不占位置

· 浮动可以进行模式转换(行内块元素)

浮动造成的影响,使盒子脱离文档流,如果父级盒子没有设置高度,需要被子盒子撑开,那么这时候父级盒子的高度就塌陷了,同时也会造成父级盒子后面的兄弟盒子布局受到影响。如果浮动元素后面还有其他兄弟元素,其他兄弟元素的布局也会受到影响。

清除浮动:

  • 伪元素清除浮动:不会新增标签,不会有其他影响,是当下清除浮动最流行的方法

  • overflow:hidden:不会新增标签,超出部分会隐藏,在不涉及父级元素有超出内容的情况,overflow:hidden比较常用,毕竟写法方便简洁

  • 额外标签 {clear: both} ==> 添加多余标签,造成页面混乱

简单说说HTML的语义化

主要是以下几点:

  • 正确的标签做正确的事情(精辟)

  • 页面内容结构化

  • 无CSS样子时也容易阅读,便于阅读维护和理解

  • 便于浏览器、搜索引擎解析。 利于爬虫标记、利于SEO

flex常用属性(高频)

  1. flex-direction属性决定主轴的方向(即项目的排列方向)。

  1. flex-wrap属性定义,如果一条轴线排不下,如何换行。

  1. flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

  1. justify-content属性定义了项目在主轴上的对齐方式。

  1. align-items属性定义项目在交叉轴上如何对齐。

  1. align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

position属性

  1. 固定定位 fixed: 元素的位置相对于浏览器窗口是固定位置,即使窗口是滚动的它也不会移动。Fixed 定 位使元素的位置与文档流无关,因此不占据空间。 Fixed 定位的元素和其他元素重叠。

  1. 相对定位 relative: 如果对一个元素进行相对定位,它将出现在它所在的位置上。然后,可以通过设置垂直 或水平位置,让这个元素“相对于”它的起点进行移动。 在使用相对定位时,无论是 否进行移动,元素仍然占据原来的空间。因此,移动元素会导致它覆盖其它框。

  1. 绝对定位 absolute: 绝对定位的元素的位置相对于最近的已定位父元素,如果元素没有已定位的父元素,那 么它的位置相对于。absolute 定位使元素的位置与文档流无关,因此不占据空间。 absolute 定位的元素和其他元素重叠。

  1. 粘性定位 sticky: 元素先按照普通文档流定位,然后相对于该元素在流中的 flow root(BFC)和 containing block(最近的块级祖先元素)定位。而后,元素定位表现为在跨越特定阈值前为相对定 位,之后为固定定位。

  1. 默认定位 Static: 默认值。没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声 明)。 inherit: 规定应该从父元素继承 position 属性的值。

transition和animation的区别

transition是过度属性,强调过度,它的实现需要触发一个事件(比如鼠标移动上去,焦点,点击等)才执行动画。它类似于flash的补间动画,设置一个开始关键帧,一个结束关键帧。animation是动画属性,它的实现不需要触发事件,设定好时间之后可以自己执行,且可以循环一个动画。它也类似于flash的补间动画,但是它可以设置多个关键帧(用@keyframe定义)完成动画。

CSS3中有哪些新特性

新增各种CSS选择器 (: not(.input):所有 class 不是“input”的节点)

1.圆角 (border-radius:8px)

2.多列布局 (multi-column layout)

3.阴影和反射 (Shadoweflect)

4.文字特效 (text-shadow)

5.文字渲染 (Text-decoration)

6.线性渐变 (gradient)

7.旋转 (transform) 增加了旋转,缩放,定位,倾斜,动画,多背景

隐藏元素的方法有哪些

1.使用display: none; 隐藏dom;

2.使用visibility: hidden; 隐藏dom;

3.使用z-index: -888; 把元素的层级调为负数,然后其他元素覆盖即可;

4.使用opacity: 0; 把元素的透明度调为0,也可以达到隐藏;

5.使用固定定位position: absolute; 把元素定位到看不见的区域;

6.使用transform: scale(0, 0); 把元素缩放为0,也可以实现元素隐藏。

盒子水平垂直居中方法(高频)

1.利用绝对定位,先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过translate来调整元素的中心点到页面的中心

2.利用绝对定位,设置四个方向的值都为0,并将margin设置为auto,由于宽高固定,因此对应方向实现平分,可以实现水平和垂直方向上的居中。该方法适用于盒子有宽高的情况

3.利用绝对定位,先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过margin负值来调整元素的中心点到页面的中心。该方法适用于盒子宽高已知的情况

4.使用flex布局,通过align-items:center和justify-content:center设置容器的垂直和水平方向上为居中对齐,然后它的子元素也可以实现垂直和水平的居中

CSS样式优先级

  • !important

  • 内联样式(1000)

  • ID选择器(0100)

  • 类选择器(0010)

  • 元素选择器(0001)

  • 通配符选择器(0000)

JS相关


JS的数据类型

基本类型:

Number、String、Boolean、Null、undefined、Symbol

复杂类型:

Object、Array、Function

JS的类型检测(高频)

typeof

instanceof

Object.prototype.toString.call()

作用域和作用域链

作用域负责收集和维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。(全局作用域、函数作用域、块级作用域)。 作用域链就是从当前作用域开始一层一层向上寻找某个变量,直到找到全局作用域还是没找到,就宣布放弃。这种一层一层的关系,就是作用域链。

原型和原型链(高频)

原型:

  • 每个 class都有显示原型 prototype

  • 每个实例都有隐式原型 _ proto_

  • 实例的_ proto_指向对应 class 的 prototype

原型链: 当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有自己的原型,于是就这样一直找下去,也就是原型链的概念

特点: JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。

闭包(高频)

闭包是指有权访问另一个函数作用域中的变量的函数 ——《JavaScript高级程序设计》当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行 ——《你不知道的JavaScript》

闭包形成的条件

  1. 函数的嵌套

  1. 内部函数引用外部函数的局部变量,延长外部函数的变量生命周期

闭包用途:

  1. 能够访问函数定义时所在的词法作用域(阻止其被回收)

  1. 私有化变量

  1. 模拟块级作用域

  1. 创建模块

闭包缺点:会导致函数的变量一直保存在内存中,过多的闭包可能会导致内存泄漏

EventLoop(高频)

JS是单线程的,为了防止一个函数执行时间过长阻塞后面的代码,所以会先将同步代码压入执行栈中,依次执行,将异步代码推入异步队列,异步队列又分为宏任务队列和微任务队列,因为宏任务队列的执行时间较长,所以微任务队列要优先于宏任务队列。微任务队列的代表就是,Promise.then,MutationObserver,宏任务的话就是setImmediate setTimeout setInterval

new运算符的实现机制

  1. 首先创建了一个新的空对象

  1. 设置原型,将对象的原型设置为函数的prototype对象。

  1. 让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)

  1. 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。

深拷贝和浅拷贝

JavaScript有两种数据类型,基础数据类型和引用数据类型。基础数据类型都是按值访问的,我们可以直接操作保存在变量中的实际的值。而引用类型如Array,我们不能直接操作对象的堆内存空间。引用类型的值都是按引用访问的,即保存在变量对象中的是一个地址,该地址与堆内存的实际值相关联。参考:JavaScript 基础数据类型和引用数据类型

1、深拷贝和浅拷贝的区别
  • 浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存。

  • 深拷贝(deep copy):复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变。

2、浅拷贝的实现

浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用(地址),所以改变新对象,旧对象也会改变,因为新旧对象共享一块内存。

通过 Object.assign() 拷贝 Object.assign(目标文件, 原文件) 浅拷贝的方法

注意:

  • 当对象只有一级属性为深拷贝;

  • 当对象中有多级属性时,二级属性后就是浅拷贝;

<script>

// 浅拷贝只是拷贝一层, 更深层次对象级别的只拷贝引用(地址)

varobj= {

id: 1,

name: 'andy',

msg: {

age: 18

}

};

varo= {};

// for (var k in obj) {

// // k 是属性名 obj[k] 属性值

// o[k] = obj[k];

// }

// console.log(o);

// o.msg.age = 20;

// console.log(obj);

console.log('--------------');

// Object.assign(目标文件, 原文件) 浅拷贝的方法

Object.assign(o, obj);

console.log(o);

o.msg.age=20;

console.log(obj);

// 结果就是 o 和 obj 的 msg.age 都变成了20, 因为 o 和 obj 是共享一块内存的

</script>

1234567891011121314151617181920212223242526

3、深拷贝的实现

深拷贝的原理:复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变。

3.1 递归遍历所有层级,实现深拷贝

我们采用递归的方式来封装实现深拷贝的函数。

// 1. 使用递归的方式实现深拷贝

// 深拷贝拷贝多层, 每一级别的数据都会拷贝.

varobj= {

id: 1,

name: 'andy',

msg: {

age: 18

},

color: ['pink', 'red']

};

varo= {};

// 封装函数

functiondeepCopy(newobj, oldobj) {

for (varkinoldobj) {

// 判断我们的属性值属于那种数据类型

// 1. 获取属性值 oldobj[k]

varitem=oldobj[k];

// 2. 判断这个值是否是数组(首先判断数组是因为数组也属于对象)

if (iteminstanceofArray) {

newobj[k] = [];

deepCopy(newobj[k], item);

} elseif (iteminstanceofObject) {

// 3. 判断这个值是否是对象

newobj[k] = {};

deepCopy(newobj[k], item);

} else {

// 4. 属于简单数据类型

newobj[k] =item;

}

}

};

deepCopy(o, obj);

o.msg.age=20;

console.log(o);

console.log(obj); // obj.msg.age 还是等于 18 没有改变

vararr= [];

console.log(arrinstanceofObject); // true 数组也是对象

1234567891011121314151617181920212223242526272829303132333435363738

3.2 利用 JSON 对象实现深拷贝

// 深拷贝拷贝多层, 每一级别的数据都会拷贝.

varobj= {

id: 1,

name: 'andy',

msg: {

age: 18

},

color: ['pink', 'red']

};

// 2. 利用 JSON 对象

functiondeepCopy(obj) {

let_obj=JSON.stringify(obj);

letnewObj=JSON.parse(_obj);

returnnewObj;

}

letnewObj=deepCopy(obj);

newObj.msg.age=22;

console.log(newObj); // newObj.msg.age = 22

console.log(obj); // obj.msg.age 还是等于 18 没有改变

123456789101112131415161718192021

注意: 无法实现对象中方法的深拷贝。

3.3 通过 jQuery 的 extend 方法实现深拷贝

jQuery 的 extend 方法也可以拷贝对象。

$.extend([deep ], target, object1 [, objectN ])

1

  • deep:表示是否深拷贝,true,为深拷贝;false,为浅拷贝。

  • target:Object类型 目标对象,其他对象的成员属性将被附加到该对象上。

  • object1: objectN 可选。 Object 类型 第一个以及第 N 个被合并的对象。

leta= [0,1,[2,3],4]

letb=$.extend(true, [], a)

a[0] =1

a[2][0] =1 // [1,1,[1,3],4]

b // [0,1,[2,3],4]

12345

3.4 Object.assign(),slice,concat 拷贝

当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。

(1)通过 Object.assign() 拷贝 Object.assign(目标文件, 原文件) 浅拷贝的方法

varobj= {a:1,b:2}

varnewObj=Object.assign({},obj)

12

(2)使用 concat 实现对数组的深拷贝

concat(arr1, arr2,....)

vararr= [1,2,3]

varnewArr= [].concat(arr)

1234

(3)使用 slice 实现对数组的深拷贝

slice(idx1, idx2)

1

vararr= [1,2,3]

varnewArr=arr.slice(0)

// slice 接受两个参数,第一个表示起始索引,第二个表示结束位置索引,省略时表示一直到末尾

123

参数可以省略

1)没有参数是拷贝数组

2)只有一个参数是从该位置起到结束拷贝数组元素

3)两个参数,拷贝从起始位置到结束位置的元素(不包含结束位置的元素:含头不含尾)

注意:

  • 当数组中的元素均为一维是深拷贝。

  • 数组中元素一维以上是值的引用。

3.5 lodash函数库实现深拷贝

lodash 很热门的函数库,提供了 lodash.cloneDeep()实现深拷贝。

_.cloneDeep(value)

value : 要深拷贝的值。

返回拷贝后的值

vue中使用:

a. npmi--savelodash 下载依赖

b. import_from'lodash' 在组件中引入

c. 用法和下面的一样

varobjects= [{ 'a': 1 }, { 'b': 2 }];

vardeep=_.cloneDeep(objects);

console.log(deep[0] ===objects[0]);

// => false

1234567891011121314

参考:https://www.lodashjs.com/docs/lodash.cloneDeep

3.6 使用扩展运算符实现数组深拷贝

vara=[1,2,3]

varb=[...a];

b.push(4);

console.log(b);//1,2,3,4

console.log(a)//1,2,3

ES相关


var/let/const

  1. var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。 let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。 const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,且不能修改。

  1. var可以先使用,后声明,因为存在变量提升;let必须先声明后使用。

  1. var是允许在相同作用域内重复声明同一个变量的,而let与const不允许这一现象。

  1. 会产生暂时性死区:

https://es6.ruanyifeng.com/#docs/letes6.ruanyifeng.com/#docs/let

var tmp = 123;

if (true) {

// 存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,

所以在let声明变量前,对tmp赋值会报错

tmp = 'abc'; // ReferenceError

let tmp;

}

map 跟 forEach 的区别

map有返回值 forEach 没有返回值

常用的数组方法

不改变原数组
join()

参数:可选。指定要使用的分隔符。如果省略该参数,则使用逗号作为分隔符。

语法:*array.join(separator*)

返回值:转换后的字符串

使用场景:把数组转为字符串的方法

let arr = [1, 2, 3, 4]

let res = arr.join('')

console.log('res', res) // 1234

let res2 = arr.join(',')

console.log('res2', res2) // 1,2,3,4

concat()

语法:*array1.concat(array2,array3,...,arrayX*)

参数:必需。该参数可以是具体的值,也可以是数组对象。可以是任意多个。

返回值:返回新的数组

使用场景: 是连接多个数组的方法

let arr = [1, 2, 3]

let arr2 = [4, 5, 6]

let res = arr.concat(arr2)

console.log('res', res) // [1, 2, 3, 4, 5, 6]

// 可以使用ES6的"..."扩展运算符更简单

let res = [...arr, ...arr2] // [1, 2, 3, 4, 5, 6]

forEach()

语法:array.forEach(function(currentValue, index, arr))

参数:currentValue => 必需,当前元素;index => 可选,当前元素索引;arr => 可选,当前元素所属的数组对象

返回值:undefiend

使用场景:用于调用数组的每个元素

let arr = [1, 2, 3]

let res = []

arr.forEach((item, index) => {

res.push(item * 2)

})

console.log('res', res) // [2, 4, 6]

map()

语法:array.map(function(currentValue, index, arr))

参数:currentValue => 必需,当前元素;index => 可选,当前元素索引;arr => 可选,当前元素所属的数组对象

返回值:返回新的数组

使用场景:比如我们只想拿到arr数组中name

let arr = [

{

name: '轨迹',

year: '10'

},

{

name: '外婆',

year: '11'

},

{

name: '暗号',

year: '12'

}

]

const res = arr.map(item => item.name)

console.log('res', res) // ['轨迹', '外婆', '暗号']

filter()

语法:array.filter(function(currentValue, index, arr))

参数:currentValue => 必需,当前元素;index => 可选,当前元素索引;arr => 可选,当前元素所属的数组对象

返回值:返回(条件满足)新的数组

使用场景:过滤出想要的新数组

// 我们只想要告白气球,过滤掉其他的

let arr = [

{

name: '半岛铁盒',

year: '10'

},

{

name: '回到过去',

year: '11'

},

{

name: '告白气球',

year: '12'

}

]

const res = arr.filter(item => item.name === '告白气球')

console.log('res', res) // { name: '告白气球', year: 12 }

every()

语法:array.every(function(currentValue, index, arr))

参数:currentValue => 必需,当前元素;index => 可选,当前元素索引;arr => 可选,当前元素所属的数组对象

返回值:布尔值。如果所有元素都通过检测返回 true,否则返回 false。

使用场景:一个班级所有人的分数60分(含60分)以上就是优秀班级

注意点:如果有一个不通过就返回false,不会再进行检测。

let arr = [

{

name: '小明',

score: 90

},

{

name: '小红',

score: 88

},

{

name: '小花',

score: 70

}

]

const res = arr.every(item => item.score >= 60)

console.log('res', res) // true => 咱们班是优秀班级

some()

语法:array.some(function(currentValue, index, arr))

参数:currentValue => 必需,当前元素;index => 可选,当前元素索引;arr => 可选,当前元素所属的数组对象

返回值:布尔值。如果有一个元素满足就返回 true,否则返回 false。

使用场景:一个班级只要有一个人超过90分就是优秀班级啦

注意点:如果有一个通过就返回true,不会再进行检测。

let arr = [

{

name: '小明',

score: 90

},

{

name: '小红',

score: 100

},

{

name: '小花',

score: 70

}

]

const res = arr.some(item => item.score > 90)

console.log('res', res) // true => 小花同学好棒,超过了90咱们又是优秀班级啦

reduce()

语法:array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

参数:total => 必需,初始值、或者计算结束后的值;currentValue => 必需,当前元素;index => 可选,当前元素索引;arr => 可选,当前元素所属的数组对象; initialValue => 可选,传递给函数的初始值

返回值:返回计算后的结果

使用场景:这个方法能实现的东西太多了,有兴趣的同学可以去百度看看,咱们这里就是实现一个常规的累加和吧

let arr = [1, 2, 3, 4]

const res = arr.reduce((pre, cur) => pre + cur, 0)

console.log('res', res) // 10

find()

语法:array.find(function(currentValue, index, arr))

参数:currentValue => 必需,当前元素;index => 可选,当前元素索引;arr => 可选,当前元素所属的数组对象

返回值:返回(条件满足)的元素

使用场景:返回第一个满足条件的元素,比如我们翻试卷,看看谁先超过90,有同学超过了,我们就看这同学的答案,我们就不会再往下看了。

注意点:只要有一个元素满足条件就返回当前元素,不会再进行检测;如果没有都没有满足条件就返回 undefined

let arr = [

{

name: '小明',

score: 99

},

{

name: '小红',

score: 100

},

{

name: '小花',

score: 70

}

]

const res = arr.find(item => item.score > 90)

console.log('res', res) // {name: '小明', score: 99}

findIndex()

语法:array.findIndex(function(currentValue, index, arr))

参数:currentValue => 必需,当前元素;index => 可选,当前元素索引;arr => 可选,当前元素所属的数组对象

返回值:返回(条件满足)的元素

使用场景:返回第一个满足条件的元素,比如我们翻试卷,看看谁先超过90,有同学超过了,我们就得到我们翻了第几张卷子,我们就不会再往下看了。

注意点:只要有一个元素满足条件就返回当前元素的索引,不会再进行检测;如果没有都没有满足条件就返回 -1

let arr = [

{

name: '小明',

score: 60

},

{

name: '小红',

score: 50

},

{

name: '小花',

score: 70

}

]

const res = arr.findIndex(item => item.score > 90)

console.log('res', res) // -1 => 没有同学达到条件所以返回了 -1

indexOf()

语法:*string.indexOf(searchvalue,start*)

参数:*searchvalue => 必需,规定检索的字符串; start => 可选,规定字符串开始检索的位置*

返回值:如果找到就返回第一次出现的索引,找不到就返回 -1

使用场景:咱们找出c的索引,然后去xxoo

let str = 'abcdefg'

const index = str.indexOf('c')

console.log('index', index) // 2

includes()

语法:string.includes(searchvalue, start)

参数:*searchvalue => 必需,规定检索的字符串; start => 可选,从那个位置开始查找,默认为0*

返回值:布尔值,找到就返回true,否则返回false

使用场景:如果检测的数组中有 彭于晏 那么就返回true

注意点:不区分大小写哟

let arr = ['胡歌', '彭于晏', '吴彦祖']

const res = arr.includes('彭于晏')

console.log('res', res) // true

slice()

语法:*array.slice(start,end*)

参数:start => 可选从那个位置开始选取,如果是负数就从数组的倒数第几个开始;end => 可选,从那个位置结束,如果不给参数则默认到数组的最后一个元素。

返回值:返回一个新的数组

使用场景:从开始到结束([)左闭右开,即不包括结束)选择数组的一部分浅拷贝到一个新数组。

const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

console.log(arr.slice(1, 5)); // [ 1, 2, 3, 4 ]

at()

语法:array.at(index)

参数:要返回的数组元素的索引(位置)。当传递负数时,支持从数组末端开始的相对索引;也就是说,如果使用负数,返回的元素将从数组的末端开始倒数。

返回值:匹配给定索引的数组中的元素。如果找不到指定的索引,则返回undefined

使用场景:取数组最后一个元素

let arr = ['珊瑚海', '发如雪', '黑色毛衣']

const res = arr.at(-1)

console.log('res', res) // 黑色毛衣

// ES5取最后一个元素

arr[arr.length - 1]

flat()

语法:array.flat(index)

参数:数值 默认是0

返回值:返回扁平后的数组

使用场景:把一个多维数组扁平

const arr = [1, [2]]

const res = arr.flat() // [1, 2]

// Infinity关键字参数

const arr1 = [1, [2, [3, [4, 5]]], 6]

const res2 = arr1.flat(Infinity)

console.log(names) // [1, 2, 3, 4, 5, 6]

fill()

语法:array.fill(value, start, end)

参数:value => 必需,填充的值;start => 可选,开始填充的位置;end, 可选,停止填充位置

返回值:返回新的数组

使用场景:把一个数组进行填充

let arr = ['珊瑚海', '发如雪', '黑色毛衣']

const res = arr.fill('爱在西元前')

console.log('res', res) // ['爱在西元前', '爱在西元前', '爱在西元前']

改变数组
push()

语法:*array.push(item1,item2, ...,itemX*)

参数:必需,要添加到数据的元素

返回值:新数组长度

使用场景:将元素添加到数组的末尾

let arr = []

arr.push('退后', '搁浅', '彩虹')

console.log('arr', arr) // ['退后', '搁浅', '彩虹']

pop()

语法:*array*.pop()

返回值:返回删除的元素

使用场景:删除数组最后的元素

let arr = ['青花瓷', '我不配', '甜甜的']

const res = arr.pop()

console.log('res', res) // 甜甜的

console.log('arr', arr) // ['青花瓷', '我不配']

shift()

语法:*array*.shift()

返回值:返回删除的元素

使用场景:删除数组第一个元素

let arr = ['说好不哭', '烟花易冷', '本草纲目']

const res = arr.shift()

console.log('res', res) // 说好不哭

console.log('arr', arr) // ['烟花易冷', '本草纲目']

unshift()

语法:*array.unshift(item1,item2, ...,itemX*)

参数:可选。向数组起始位置添加一个或者多个元素。

返回值:返回新数组的长度

使用场景:方法可向数组的开头添加一个或更多元素,并返回新的长度。

let arr = ['晴天', '暗号']

const res = arr.unshift('外婆')

console.log('res', res) // 3

console.log('arr', arr) // ['外婆', '晴天', '暗号']

splice()

语法:*array.splice(index,howmany,item1,.....,itemX*)

参数:index =>必需,从何处添加/删除元素;howmany => 可选,要删除几个元素,必需是数字,可以为0,如果不给参数则从index删除到数组的结尾 item1,.....,itemX => 可选,要添加到数组的新元素

返回值:如果是删除元素就返回含有被删除元素的数组

使用场景:数组可以在规定的位置删除/添加元素

let arr = ['听妈妈的话', '算什么男人', '黑色幽默', '可爱女人']

const res = arr.splice(1, 2, '千里之外', '反向的钟')

console.log('res', res) // ['算什么男人', '黑色幽默']

console.log('arr', arr) // ['听妈妈的话', '千里之外', '反向的钟', '可爱女人']

sort()

语法:*array.sort(sortfunction*)

参数:*sortfunction => 可选,规定排序顺序,必需可选*

使用场景:方法用于对数组的元素进行排序。

// 个位数直接 arr.sort() 就可以

let arr = [1, 6, 2, 9, 4]

arr.sort()

console.log('arr', arr) // [1, 2, 4, 6, 9]

// 非个位数

let arr = [40, 100, 1, 5, 25, 10]

arr.sort(function (a, b) {

return a - b // 如果是b - a 就是倒过来排序了

})

console.log('arr', arr) // [1, 5, 10, 25, 40, 100]

// 字母

let arr = ['Banana', 'Orange', 'Apple', 'Mango']

arr.sort()

console.log('arr', arr) // ['Apple', 'Banana', 'Mango', 'Orange']

reverse()

语法:*array*.reverse()

参数:

返回值:

颠倒顺序后的数组

使用场景:把一个数组的元素翻转过来

let arr = [1, 2, 3, 4, 5, 6]

const res = arr.reverse()

console.log('res', res) // [6, 5, 4, 3, 2, 1]

ES6常用的新特新

1.新增symbol类型 表示独一无二的值,用来定义独一无二的对象属性名;

2.const/let 都是用来声明变量,不可重复声明,具有块级作用域。存在暂时性死区,也就是不存在变量提升。(const一般用于声明常量);

3.变量的解构赋值(包含数组、对象、字符串、数字及布尔值,函数参数),剩余运算符(...rest);

4.模板字符串(${data});

5.扩展运算符(数组、对象);

6.箭头函数;

7.Set和Map数据结构;

8.Proxy/Reflect;

9.Promise;

10.async函数;

Vue相关


vue 的生命周期

create阶段:vue实例被创建 beforeCreate: 创建前,此时data和methods中的数据都还没有初始化 created: 创建完毕,data中有值,未挂载

mount阶段: vue实例被挂载到真实DOM节点 beforeMount:可以发起服务端请求,去数据 mounted: 此时可以操作Dom

update阶段:当vue实例里面的data数据变化时,触发组件的重新渲染 beforeUpdateupdated

destroy阶段:vue实例被销毁 beforeDestroy:实例被销毁前,此时可以手动销毁一些方法 destroyed

vue 的通讯方式

父子组件通讯 props $emit parent、children Ref

兄弟之间通讯 event bus

跨组件通讯 $attrs、$listeners Provide、inject

vuex

computed与watch

watch 属性监听 是一个对象,键是需要观察的属性,值是对应回调函数,主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作,监听属性的变化,需要在数据变化时执行异步或开销较大的操作时使用

computed 计算属性 属性的结果会被缓存,当computed中的函数所依赖的属性没有发生改变的时候,那么调用当前函数的时候结果会从缓存中读取。除非依赖的响应式属性变化时才会重新计算,主要当做属性来使用 computed中的函数必须用return返回最终的结果 computed更高效,优先使用。data 不改变,computed 不更新。

使用场景computed:当一个属性受多个属性影响的时候使用,例:购物车商品结算功能 watch:当一条数据影响多条数据的时候使用,例:搜索数据

v-if 和 v-for 的优先级

v-for的优先级是高于v-if的,如果两者同时出现的话,那每次循环都会执行v-if,会很浪费性能,我们正确的做法应该是再v-for的外面新增一个模板标签template,在template上使用v-if

$nextTick

  1. nextTick是Vue提供的一个全局API,是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后使用$nextTick,则可以在回调中获取更新后的DOM;

  1. Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启1个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中-次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。nextTick方法会在队列中加入一个回调函数,确保该函数在前面的dom操作完成后才调用;

  1. 比如,我在干什么的时候就会使用nextTick,传一个回调函数进去,在里面执行dom操作即可;

  1. 我也有简单了解nextTick实现,它会在callbacks里面加入我们传入的函数,然后用timerFunc异步方式调用它们,首选的异步方式会是Promise。这让我明白了为什么可以在nextTick中看到dom操作结果。

v-for中key的作用

  1. key的作用是为了在diff算法执行时更快的找到对应的节点,提高diff速度,更高效的更新虚拟DOM;

  1. Vue在patch过程中判断两个节点是否是相同节点,key是一个必要条件,渲染一组列表时,key往往是唯一标识,所以如果不定义key的话,Vue只能认为比较的两个节点是同一个,哪怕它们实际上不是,这导致了频繁更新元素,使得整个patch过程比较低效,影响性能;

  1. 从源码中可以知道,Vue判断两个节点是否相同时主要判断两者的key和元素类型等,因此如果不设置key,它的值就是undefined,则可能永 远认为这是两个相同的节点,只能去做更新操作,这造成了大量的dom更新操作,明显是不可取的。

keep-alive的实现

作用:实现组件缓存

钩子函数: ”activated “组件渲染后调用 ”deactivated“组件销毁后调用

原理:Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。

配置属性: include 字符串或正则表达式。只有名称匹配的组件会被缓存 exclude 字符串或正则表达式。任何名称匹配的组件都不会被缓存 max 数字、最多可以缓存多少组件实例

Vuex

Vuex 是一个专为 Vue 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。

  1. Vuex 的状态存储是响应式的;当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新

  1. 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation, 这样使得我们可以方便地跟踪每一个状态的变化 Vuex主要包括以下几个核心模块:

  1. State:定义了应用的状态数据

  1. Getter:在 store 中定义“getter”(可以认为是 store 的计算属性),就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来, 且只有当它的依赖值发生了改变才会被重新计算

  1. Mutation:是唯一更改 store 中状态的方法,且必须是同步函数

  1. Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作

  1. Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中

vue-router 怎么传参

1.id传参

//路由配置

{

path: '/orderDetail/:id',

name: 'orderdetail',

component: () => import('../components/orderDetail.vue'),

meta: { showTabbar: false}

}

this.$router.push({path:`/orderDetail/${this.id}`})

2.query传参

this.$router.push({path:`/orderDetail`,query:{id:this.id}})

3.pramas传参

this.$router.push({name:`orderdetail`,params:{id:this.id}})

插槽

1.普通插槽

2.具名插槽

3.作用域插槽

vue 的双向绑定原理

当一个Vue实例创建时,Vue会遍历data选项的属性,用 Object.defineProperty 将它们转为 getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher重新计算,从而致使它关联的组件得以更新。

Vue2和Vue3区别

属性监听:

defineProperty: 遍历对象,劫持对象的每一个属性; Proxy: 劫持整个对象,并返回一个代理对象;

对数组方法的支持:

defineProperty: push...等不能监听;Proxy: 可以监听;

兼容:

defineProperty(ES5)兼容性比Proxy(ES6)好。

Vue3

  1. setup函数

  1. 声明普通类型 ref

  1. 声明复杂类型 reactive

  1. (https://link.zhihu.com/?target=http%3A//4.watch)watch 的用法

  1. v-model 的用法

  1. Vue3双向绑定原理

  1. Vue3的生命周期

  1. Vue3通讯

  1. Vue3.2 setup 语法糖等

webpack相关


常用Loader(高频)

raw-loader:加载文件原始内容(utf-8)

file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)

source-map-loader:加载额外的 Source Map 文件,以方便断点调试

svg-inline-loader:将压缩后的 SVG 内容注入代码中

image-loader:加载并且压缩图片文件

json-loader 加载 JSON 文件(默认包含)

babel-loader:把 ES6 转换成 ES5

ts-loader: 将 TypeScript 转换成 JavaScript

awesome-typescript-loader:将 TypeScript 转换成 JavaScript,性能优于 ts-loader

sass-loader:将SCSS/SASS代码转换成CSS

css-loader:加载 CSS,支持模块化、压缩、文件导入等特性

style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS

postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀

vue-loader:加载 Vue.js 单文件组件

常用的Plugin

define-plugin:定义环境变量 (Webpack4 之后指定 mode 会自动配置)

ignore-plugin:忽略部分文件

html-webpack-plugin:简化 HTML 文件创建 (依赖于 html-loader)

web-webpack-plugin:可方便地为单页应用输出 HTML,比 html-webpack-plugin 好用

uglifyjs-webpack-plugin:不支持 ES6 压缩 (Webpack4 以前)

terser-webpack-plugin: 支持压缩 ES6 (Webpack4)

webpack-parallel-uglify-plugin: 多进程执行代码压缩,提升构建速度

mini-css-extract-plugin: 分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin)

serviceworker-webpack-plugin:为网页应用增加离线缓存功能

clean-webpack-plugin: 目录清理

oader 和 plugin 的区别

Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。 因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。

Plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。

Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。

Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入

Git相关

git 常用命令

  1. git clone

  1. git add .

  1. git commit -m

  1. git push

  1. git pull

  1. git branch 查看本地分支

  1. git branch -a 查看远程分支

  1. git branch -d xxx 删除分支

  1. git checkout xxx 切换分支

  1. git checkout -b xxx 新建分支并且切换到改分支

  1. git branch -m 旧分支名 新分支名 分支重命名

  1. git status 查看修改的文件

  1. git merge 合并分支

  1. git checkout -b xxx origin/xxx 拉去远程分支

  1. git log 查看提交记录

网络安全


GET和POST区别(高频)

1.GET在浏览器回退不会再次请求,POST会再次提交请求

2.GET请求会被浏览器主动缓存,POST不会,要手动设置

3.GET请求参数会被完整保留在浏览器历史记录里,POST中的参数不会

4.GET请求在URL中传送的参数是有长度限制的,而POST没有限制

5.GET参数通过URL传递,POST放在Request body中

6.GET参数暴露在地址栏不安全,POST放在报文内部更安全

7.GET一般用于查询信息,POST一般用于提交某种信息进行某些修改操作

8.GET产生一个TCP数据包;POST产生两个TCP数据包

HTTP和HTTPS


http 和 https 的基本概念

http: 是一个客户端和服务器端请求和应答的标准(TCP),用于从 WWW 服务器传输超文本到本地浏览器的超文本传输协议。 https:是以安全为目标的 HTTP 通道,即 HTTP 下 加入 SSL 层进行加密。其作用是:建立一个信息安全通道,来确保数据的传输,确保网站的真实性。

http 和 https 的区别(高频)

1.HTTP 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头

2.HTTP 是不安全的,而 HTTPS 是安全的

3.HTTP 标准端口是80 ,而 HTTPS 的标准端口是443

4.在OSI 网络模型中,HTTP工作于应用层,而HTTPS 的安全传输机制工作在传输层

5.HTTP 无法加密,而HTTPS 对传输的数据进行加密

6.HTTP无需证书,而HTTPS 需要CA机构wosign的颁发的SSL证书

http状态码(高频)

  1. 200响应成功

  1. 301永久重定向

  1. 302临时重定向

  1. 304资源缓存

  1. 403服务器禁止访问

  1. 404服务器资源未找到

  1. 500 502服务器内部错误 504 服务器繁忙

性能相关


请求跨域

  1. jsonp(利用script标签没有跨域限制的漏洞实现。缺点:只支持GET请求)

  1. CORS(设置Access-Control-Allow-Origin:指定可访问资源的域名)

  1. proxy代理 目前常用方式,通过服务器设置代理

  1. 利用h5新特性window.postMessage()

浏览器本地存储

浏览器的本地存储主要分为Cookie、localStorage和sessionStorage。

共同点: 都是保存在浏览器端、且同源的不同点:

  1. cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。

  1. 存储大小限制也不同,

  • cookie数据不能超过4K,sessionStorage和localStorage可以达到5M

  • sessionStorage:仅在当前浏览器窗口关闭之前有效;

  • localStorage:始终有效,窗口或浏览器关闭也一直保存,本地存储,因此用作持久数据;

  • cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭

3.作用域不同

  • sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面;

  • localstorage:在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在

  • cookie: 也是在所有同源窗口中都是共享的.也就是说只要浏览器不关闭,数据仍然存在

防抖

多次触发事件,事件处理函数只能执行一次,并且是在触发操作结束时执行。也就是说,当一个事件被触发准备执行事件函数前,会等待一定的时间(这时间是码农自己去定义的,比如 1 秒),如果没有再次被触发,那么就执行,如果被触发了,那就本次作废,重新从新触发的时间开始计算,并再次等待 1 秒,直到能最终执行!

const debounce = (fn, time) => {

let timer = null

return function () {

clearTimeout(timer)

timer = setTimeout(() => {

fn.apply(this)

}, time)

}

}

节流

事件触发后,规定时间内,事件处理函数不能再次被调用。也就是说在规定的时间内,函数只能被调用一次,且是最先被触发调用的那次。

const throttle = (fn, time) => {

let flag = true

return function () {

if (!flag) return

flag = false

setTimeout(() => {

fn.apply(this)

flag = true

}, time)

}

}

优化项目


  1. 移除生产环境的控制台打印

  1. 第三方库的按需加载

  1. 避免css表达式、滤镜,较少DOM操作,优化图片、精灵图,避免图片空链接等

  1. 降低请求成本

  1. 减少请求数

  1. 减少传输体积

  1. ...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值