面试题

51 阅读30分钟

一 HTML语义化

HTML语义化:是指使用合适的HTML标签来描述页面中不同的内容模块以及模块之间的关系。

优点

让页面结构更加清晰,可读性更高,代码更加容易编写和维护;

在css没有加载出来的情况下也能很好的展示结构,

利于搜索引擎的seo优化。(爬虫依赖标签来确定关键字的权重,因此可以和搜索引擎建立良好的沟通,帮助爬虫抓取更多的有效信息)

常用的HTML标签:header,footer,aside,article,section

image.png  

二 盒模型

css盒子的组成包括:margin、border、padding、content。

分类:盒模型分为两类,一是标准盒模型,二是ie(怪异)盒模型。一般我们的盒子默认是标准盒模型。

区别:标准盒模型设置 width,height 时不包括 padding,border,而ie(怪异)盒模型包含了padding和border。css可以通过box-sizing设置盒子为哪一种,content-box标准盒模型,border-box (怪异)ie盒模型

    box-sizing: content-box;//标准盒模型
    box-sizing: border-box;//ie(怪异)盒模型

浮动

左浮动:float:left 右浮动:float:right

html中有两种元素:1.行元素,2.块元素;

浮动解决了文字绕排问题。

“浮动元素”脱离了标准文档流,导致块元素感知不到它。 这有两个后果,

1.块元素会被“浮动元素”遮盖。

解决办法:块元素加“clear:both”属性。

2.父元素塌陷。(浮动元素脱离了文档流,而原来父级元素是由内容撑开,此时父级高度为0,就会造成高度塌陷问题。使布局出现混乱。)

解决办法

1.给父级添加高度

  1. 给父级添加overflow:hidden

  2. 创建一个空白div,添加clear:both 属性

  3. 添加一个after伪类 ::after{content:" "; width:" "; height:" "; display:block;overflow:hidden;visibility:hidden;clear:both;zoom:1}

 

四 优先级的规则

!important > 内联样式(style) > ID选择器(id) > 类选择器(class) > 标签选择器

1.!important、 2.行内样式>(嵌入样式、外链样式)、 3.id选择器>(类选择器、伪类选择器、属性选择器)>(后代选择器、伪元素选择器)>(子选择器、相邻选择器)>通配符选择器、 4.继承样式、 5.浏览器默认样式

 

五 css尺寸设计的单位

①共有五个单位,分别是px,em,rem,vw,vh。

②除了px是绝对单位,其余都是相对单位。

③em相对于他本身的字体大小(但他本身字体大小是相对于父级字体大小的)。

④rem相对于根元素的字体大小。

⑤vw相对于可视化窗口的宽(1vw就是1%窗口宽度)。

⑥vh相对于可视化窗口的高(1vh就是1%窗口高度)。

⑦一般采用rem+媒体查询或者rem+vw来实现响应式布局。原理是当窗口大小发生变化时,通过媒体查询或vw改变根元素的字体大小,从而改变以rem为单位的元素大小。

 

BFC

块级格式化上下文,是一块独立的渲染区域,BFC的特性容器里面的元素不会影响外边元素布局,当然外部的元素也不能影响内部元素的布局

什么情况下可以让元素产生BFC

1、有浮动

2、绝对或弹性布局position为absolute或fixed

3、display为inline-block、table-cell、table-caption、flex、inline-flex

4、overflow:hidden

BFC元素具有的特性

1、在BFC中,盒子从顶部开始垂直地一个接一个排列

2、盒子垂直方向的距离由margin决定。同一个BFC的两个相邻盒子margin会重叠

3、BFC中,margin-left会触碰到border-left(对于从左至右的方式,反之)

4、BFC区域不会与浮动的盒子产生交集,而是紧贴边缘浮动

5、计算BFC高度时,自然会检测浮动的盒子高度

主要用途

1、清除内部浮动,父元素设置为BFC可以清除子元素的浮动(最常用overflow:hidden,IE6需加上*zoom:1):计算BFC高度时会检测浮动子盒子高度

2、解决外边距合并问题

3、右侧盒子自适应:BFC区域不会与浮动盒子产生交集,而是紧贴浮动边缘

 

七 未知宽高元素水平垂直居中

对于宽高固定的元素:

(1)我们可以利用margin:0 auto来实现元素的水平居中。

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

(3)利用绝对定位,先将元素的左上角通过top:50%和left:50%定位到页面的中心,然后再通过margin负值来调整元素 的中心点到页面的中心。

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

(5)使用flex布局,通过align-items:center和justify-content:center 设置容器的垂直和水平方向上为居中对 齐,然后它的子元素也可以实现垂直和水平的居中。 对于宽高不定的元素,上面的后面两种方法,可以实现元素的垂直和水平的居中。

 

八 三栏布局实现方案

1、使用浮动+外边距 2、定位+外边距

三栏布局一般指的是页面中一共有三栏,左右两栏宽度固定,中间自适应的布局,一共有五种实现方式。 这里以左边宽度固定为100px,右边宽度固定为200px为例。

(1)利用绝对定位的方式,左右两栏设置为绝对定位,中间设置对应方向大小的margin的值。

(2)利用flex布局的方式,左右两栏的宽度分别设置为100px和200px,中间一栏增长系数设置为1

(3)利用浮动的方式,左右两栏设置固定大小,并设置对应方向的浮动。中间一栏设置左右两个方向的margin值,注意这种方式,中间一栏必须放到最后。

(4)圣杯布局,利用浮动和负边距来实现。父级元素设置左右的padding,三列均设置向左浮动,中间一列放在最前面,宽度设置为父级元素的宽度,因此后面两列都被挤到了下一行,通过设置margin负值将其移动到上一行,再利用相对定位,定位到两边。圣杯布局中间列的宽度不能小于左边列的宽度,否则左边列上不去,而双飞翼布局则不存在这个问题。

(5)双飞翼布局,双飞翼布局相对于圣杯布局来说,左右位置的保留是通过中间列的margin值来实现的,而不是通过父元素的padding来实现的。本质上来说,也是通过浮动和外边距负值来实现的。

 

九 js数据类型,区别

原始数据类型: undefined - - (未定义)、null- - (空的)、number - - (数字)、boolean- - (布尔值)、string- - (字符串)、Symbol - - (符号)

引用数据类型:object(对象)(包括Array(数组),Function(函数)、Object类等)

区别:原生数据类型的存放在栈中。

引用数据类型存放在堆中,它的地址在栈中,一般我们访问就是它的地址

 

十 null 和undefined区别,如何让一个值变null

null 是定义 并赋值null

undefined是定义未赋值

undefined !== null

让一个变量为null,直接给该变量赋值为null即可

 

十一 JavaScript 类型判断

  1. typeof(根据二进制判断)对于null及数组、对象,typeof均检测出为object,不能进一步判断它们的类型。
  2. intanceof(根据原型链判断),原生数据类型不能判断
  3. constructor.name(根据构造器判断),不能判断null数据类型
  4. Object.prototype.toString.call()(用Object的toString方法判断)所有类型数据都能判断,记住判断结果打印为:'[object xxx]'

十二 数组去重方法

1、利用Set()+Array.from()(ES6中最常用)

不考虑兼容性,这种方法代码最少。无法去掉“{}”空对象,后面的高阶方法会添加去掉重复“{}”的方法。

var arr = [1,1,8,8,12,12,15,15,16,16];
function unique (arr) {
  return Array.from(new Set(arr))
}
console.log(unique(arr)) //[1,8,12,15,16]

2、利用两层循环+数组的splice方法(ES5中最常用)

NaN和{}没有去重,两个null直接消失了

var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];

function unlink(arr) {
    for (var i = 0; i < arr.length; i++) {    // 首次遍历数组
        for (var j = i + 1; j < arr.length; j++) {   // 再次遍历数组
            if (arr[i] == arr[j]) {          // 判断连个值是否相等
                arr.splice(j, 1);           // 相等删除后者
                j--;
            }
        }
    }
    return arr;
}
console.log(unlink(arr));

3、利用数组 indexOf 去重

新建一个空的结果数组,for循环原数组,判断结果数组中是否存在当前元素,如果存在则跳过,如果不存在,就push到结果数组;

  • indexOf() 方法:返回调用它的String对象中第一次出现的指定值的索引,从 fromIndex 处进行搜索。如果未找到该值,则返回 -1。

NaN、{}没有去重

var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unlink(arr) {
    if (!Array.isArray(arr)) {
        console.log('错误!');
        return;
    }
    var array = [];
    for (var i = 0; i < arr.length; i++) {    // 首次遍历数组
        if (array.indexOf(arr[i]) === -1) {   // 判断索引有没有等于
            array.push(arr[i]);
        }
    }
    return array;
}
console.log(unlink(arr));

4、利用 includes

 利用include,判断数组中是否包含某个值,不包含则 push,包含则跳过; 对NaN有去重。

  • includes()方法:用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false
var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array =[];
    for(var i = 0; i < arr.length; i++) {
            if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
                    array.push(arr[i]);
              }
    }
    return array
}
console.log(unique(arr))

5、利用数组的filter()+indexOf()

  • filter() 方法:会创建一个新数组,其包含通过所提供函数实现的测试的所有元素。

输出结果中不包含NaN,因为indexOf()无法对NaN进行判断,即arr.indexOf(item) === index返回结果为false。

var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unlink(arr) {
    return arr.filter(function (item, index, arr) {
        //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
        return arr.indexOf(item, 0) === index;
    });
}
console.log(unlink(arr));

6、利用sort

(倒叙方法,然后根据排序后的结果进行遍历及相邻的元素比较);

7、利用map数据结构去重:

创建一个空的map数据结构,遍历需要去重的数组,把数组的每个元素作为key存在map中。由于map中不会出现相同的值,所以最终得到的是去重之后的结果;

8、[...new Set(arr)]

十三 伪数组,数组区别

  1. 伪数组是object,数组是Array
  2. 伪数组不能使用数组方法,比如增删改
  3. 可以用for in遍历可以获取长度,可以用下标获取对应元素
  4. 伪数组转换为数组的方法Array.prototype.slice.call(),Array.from(),扩展运算符等等 5. 函数参数的arguments,获取的dom,Map和Set的keys,values,entries都是伪数组

 

十四 map和foreach区别

1.共同点:都是循环遍历一个数组中的每一项 2.forEach() 和 map() 里面每一次执行匿名函数都支持3个参数:数组中的当前项item,当前项的索引index,原始数组input 3.匿名函数中的this都是指Window 4.只能遍历数组 不同点:foreach没有返回值 能改变原数组 map返回一个新的数组,不修改原数组 map的执行速度更快

直白点说,forEach是针对数组中每一个元素,提供一个可执行的函数操作,因此它(可能)会改变原数组中的值。不会返回有意义的值,或者说会返回undefined;而map是会分配内存空间创建并存储一个新的数组,新数组中的每一个元素由调用的原数组中的每一个元素执行所写的函数得来,返回的就是新数组,因此不会改变原数组的值;

个人感觉map更加贴近于函数式编程的特点,而且执行起来也会比forEach快很多,所以我在二者都可的情况下会更推荐map

 

十五 es6中的箭头函数

箭头函数比普通函数的定义写法更加简洁明了和快捷。

  • 区别:箭头函数没有原型prototype和super,所以无法创建this,其this是通过继承外部函数环境中的变量获取的,所以call、bind、apply都无法改变其this的指向;在找不到最外层的普通函数时,其this一般指向window;
  • 箭头函数不能使用new;
  • 箭头函数没有arguments;
  • 也不能作为generator函数,不能使用yield命令;
  • 箭头函数不能用于对象域和回调函数动态this中,一般用在内部没有this引用。
  • 不能作为构造函数new对象

 

十六 事件扩展符...

对象中的扩展运算符(...)用于取出参数对象中的所有可遍历的属性,浅拷贝到当前的对象中。

浅拷贝和深拷贝:

  • 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
  • 但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
  1. 数组去重[...new Set(arr)]
  2. 数组拷贝[...arr]
  3. 伪数组转真数组 [...伪数组]

十七 闭包

闭包形成的原理:作用域链,当前作用域可以访问上级作用域中的变量 闭包解决的问题:能够让函数作用域中的变量在函数执行结束之后不被销毁,同时也能在函数外部可以访问函数内部的局部变量。 闭包带来的问题:由于垃圾回收器不会将闭包中变量销毁,于是就造成了内存泄露,内存泄露积累多了就容易导致内存溢出。

闭包的应用,能够模仿块级作用域,能够实现柯里化,在构造函数中定义特权方法、Vue中数据响应式Observer中使用闭包等。

 

十八 js变量提升

var声明会存在变量提升,函数声明也会提升,let,const不会存在变量提升问题 变量提升,函数声明提示,都是在js编译阶段提升的,将当前变量、函数提升至当前作用域的最顶端 并且由于编译阶段,并没有开始执行代码,所以都会被赋初始值undefined,由于函数不同,函数会在堆内存中开辟一个空间用于来保存函数的执行体,会将当前内存地址赋值给函数变量,所以可以在函数声明上面,来调用该函数 并且let,const声明变量会形成暂时性死区,就是在该作用域当中,无法在变量声明之前去访问,即使全局存在一个同名变量,也无法访问

 

十九 this指向√

普通函数调用时,在严格模式下指向的是undefined,非严格模式下指向的是window;

对象调用时指向的是当前的对象;

用new关键字构造出的新对象,指向的是新对象;

call,apply,bind指向的是绑定后的this指向;箭头函数没有自己的this,指向外部执行上下文的this

首先,call apply bind三个方法都可以用来改变函数的this指向,具体区别如下:

1、fn.call (newThis,params) call函数的第一个参数是this的新指向,后面依次传入函数fn要用到的参数。会立即执行fn函数。

2、fn.apply (newThis,paramsArr) apply函数的第一个参数是this的新指向,第二个参数是fn要用到的参数数组,会立即执行fn函数。

3、fn.bind (newThis,params) bind函数的第一个参数是this的新指向,后面的参数可以直接传递,也可以按数组的形式传入。  不会立即执行fn函数,且只能改变一次fn函数的指向,后续再用bind更改无效。返回的是已经更改this指向的新fn

 

二十 js继承的方法和优缺点

  1. 原型链继承 --- 优点:写法简单、容易理解。 缺点: ①引用类型的值会被所有实例共享; ②在子类实例对象创建时,不能向父类传参;
  2. 借用构造函数继承 --- 优点: ①避免了引用类型的值会被所有实例共享; ②在子类实例对象创建时,可以向父类传参; --- 缺点:方法在构造函数中,每次创建实例对象时都会重新创建一遍方法;
  3. 组合继承 --- 融合原型链和借用构造函数的优点,是js中最常用的继承方式; --- 缺点:无论什么情况下,父类构造函数都会被调用两次,一是创建子类原型对象时,二是子类构造函数内部。
  4. 原型式继承 --- 优点:不需要单独创建构造函数; --- 缺点:引用类型的值会被所有实例共享。
  5. 寄生式继承 --- 优点:不需要单独创建构造函数; --- 缺点:方法在构造函数中,每次创建实例对象时都会重新创建一遍。
  6. 寄生组合继承 --- 优点:高效率只调用一次父类构造函数,并且避免了子类原型对象上不必要、多余的属性,同时,还能将原型链保持不变,因此能使用instanceof 和 isPrototypeOf。 --- 缺点:代码复杂

www.cnblogs.com/wrhbk/p/144…

 

二十一 new会发生什么

new的实现代码就知道有以下四步:

  1. 创建一个新对象
  2. 将新对象的__proto__(原型)指向构造函数的prototype(原型对象)
  3. 构造函数绑定新对象的this并执行返回结果
  4. 判断返回结果是否为null,如果为null,返回新对象,否则直接返回执行结果。

1、创建了一个新对象 2、this指向了这个对象 3、构造函数的属性和方法都赋给了这个对象 4、返回这个新对象

 

二十二 defer和async区别

html文件都是按顺序执行的,script标签中没有加defer和async时,浏览器在解析文档时遇到script标签就会停止解析阻塞文档解析,先加载JS文件,加载完之后立即执行,执行完毕后才能继续解析文档。 而在script标签中写入defer或者async时,就会使JS文件异步加载,即html执行到script标签时,JS加载和文档解析同时进行,而async是在JS加载完成后立即执行JS脚本,阻塞文档解析,而defer则是JS加载完成后,在文档解析完成后执行JS脚本

 

二十三 promise是什么与使用方法

  1. 概念:异步编程的一种解决方案,解决了地狱回调的问题
  2. 使用方法:new Promise((resolve,reject) => { resolve(); reject(); })里面有多个resovle或者reject只执行第一个。如果第一个是resolve的话后面可以接.then查看成功消息。如果第一个是reject的话,.catch查看错误消息。

 

二十四 js实现异步的方法

回调函数,promise,

async/await,generator

执行顺序:所有异步任务都是在同步任务执行完成后,从任务队列中取出依次执行。

方式:回调函数、事件监听、setTimeout、Promise、生成器Generators/yield、async/awt

宏任务:

(1)定时器setTimeout,setTimeinterval

(2)ajax请求:XMLHttpRequest,fetch,axios

(3)事件监听:on...,addEventListener

微任务:

(1)Promise.then

(2)async/await

异步在于创建宏任务和微任务,通过事件循环机制实现异步机制,在调用栈中执行完一个宏任务后,接着统一处理执行该宏任务期间产生的所有微任务,之后页面会进行一次重绘,再接着处理下一个宏任务

 

二十五 cookie,SessionStorage,LocalStorage区别

【相同】

1⃣️都是存储在浏览器本地的

2⃣️都遵循同源原则(SessionStorage还限制必须是同一个页面)

【不同】

1⃣️写入方式不同:cookie由服务器端写入,而SessionStorage、 LocalStorage都由前端写入

2⃣️生命周期不同:cookie是由服务器端在写入的时候就设置好的,LocalStorage是写入就一直存在,除非手动清除,SessionStorage是页面关闭时自动清除。

3⃣️存储空间大小不同:cookie大概4KB,SessionStorage、 LocalStorage大概5M。

4⃣️前端给后端发送请求时会自动携带Cookie中的数据,但是SessionStorage、 LocalStorage不会

【应用】

1⃣️cookie 存储登陆验证信息(token/SessionID)

2⃣️SessionStorage 存储不易变动数据

3⃣️LocalStorage 检测用户是否是刷新进入页面(播放器恢复进度条)

 

二十六 如何实现可过期的 LocalStorage

一种是惰性删除:惰性删除是指获取数据的时候,拿到存储的时间和当前时间做对比,如果超过过期时间就清除Cookie。 另一种是定时删除:每隔一段时间执行一次删除操作,并通过限制删除操作执行的次数和频率,来减少删除操作对CPU的长期占用。 LocalStorage清空应用场景:token存储在LocalStorage中,要清空

 

二十七 token能放cookie中吗

1.实现上是可以的,功能上不推荐,容易产生csrf问题

2.token一般存储在sessionStorage/localStorage里面。 token的出现就是为了解决用户登录后的鉴权问题,如果采用 cookie + session 的鉴权方式,则无法有效地防止CSRF攻击,同时,如果服务端采用负载均衡策略进行分布式架构,session也会存在一致性问题,需要额外的开销维护session一致性。

不能。token本身就是用来鉴权的,防止CSRF攻击。如果将token放在cookie中,则token还是会随cookie自动携带至请求中,防止不了CSRF攻击。token一般存储在sessionStorage/localStorage里面。token的出现就是为了解决用户登录后的鉴权问题,如果采用cookie+session的鉴权方式,则无法有效地防止CSRF攻击,同时,如果服务端采用负载均衡策略进行分布式架构,session也会存在一致性问题,需要额外的开销维护session一致性。所以token是专门为了鉴权而生,常见的token为JWT(JSON Web Token),用户通过账户密码登入系统后,服务端会生成一个jwt。jwt一般包含三个部分header、payload和signature,header包括两个字段说明token的类型和采用的签名算法,payload包含用户的一些身份权限信息但不包含敏感信息,signature是服务端的签名由前两个部分采用base64编码后再经过签名算法加密生成,签名算法的私钥由服务器保管。服务端生成jwt后返回给客户端。客户端下次调用api的请求头中放入token用于鉴权,服务端会通过jwt的前两个部分和私钥经过签名算法生成一个签名,判断与jwt第三部分的签名是否一致,如果一致就认证通过。

 

二十八 axios拦截器原理及应用

  • axios拦截器分为响应和请求拦截器,
  • 请求拦截器:在请求发送前进行必要操作处理,例如添加统一cookie、请求体加验证、设置请求头等,相当于是对每个接口里相同操作的一个封装;
  • 响应拦截器:在请求得到响应之后,对响应体的一些处理,通常是数据统一处理等,也常来判断登录失效等。

 

二十九 创建ajax过程

1、创建XHR对象:new XMLHttpRequest(); 2、配置请求参数:request.open(method,服务器接口地址); 3、发送请求:request.send(data),如果是get请求,则不需要传递参数,如果是post则需要传递参数。 4、监听请求成功之后的状态变化:XHR.onreadystatechange()=function(){if(XHR.readyState==4&&XHR.state == 200){console.log(XHR.reponseText)}};

 

三十 fetch请求方式

fecth是一种HTTP数据请求的方式,是XMLHttpRequest的一种替代方式,Fetch函数就是原生js,没有使用XMLHttpRequest对象。Fetch对象返回一个promise解析response来自request显示状态的方法。

XMLHTTPRequest特点:

1、所有功能集中在一个对象上,写的代码可维护性不强且容易混乱。 2、不能适配新的promise API;

Fetch特点:

1、精细的功能分割:头部信息,请求信息,响应信息等均分布在不同的对象上,可以处理各种复杂的数据交互场景。 2、也可以适配promise API; 3、同源请求也可以自定义不带cookie,某些服务不需要cookie的场景下能少些流量。

 

三十一 保持前后端实时通信方法

  1. 轮询、长轮询、 iframe流、WebSocket、SSE

  2. 轮询是客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。

  3. 长轮询是对轮询的改进版,客户端发送HTTP给服务器之后,如果没有新消息,就一直等待。有新消息,才会返回给客户端。

  4. iframe流方式是在页面中插入一个隐藏的iframe,利用其src属性在服务器和客户端之间创建一条长连接,服务器向iframe传输数据(通常是HTML,内有负责插入信息的javascript),来实时更新页面。

  5. WebSocket是类似Socket的TCP长连接的通讯模式,一旦WebSocket连接建立后,后续数据都以帧序列的形式传输。

  6. SSE(Server-Sent Event)是建立在浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息。

  7. 应用:

轮询适用于:小型应用,实时性不高

长轮询适用于:一些早期的对及时性有一些要求的应用:web IM 聊天

iframe适用于:客服通信等

WebSocket适用于:微信、网络互动游戏等

SSE适用于:金融股票数据、看板等

 

三十二 浏览器输入URL发生了什么

1、URL解析:判断浏览器输入的是搜索内容还是URL;2、查找缓存:如果能找到缓存则直接返回页面,如果没有缓存则需要发送网络请求页面;3、DNS域名解析;4、三次握手建立TCP连接;5、发起HTTP请求;6、服务器响应并返回结果;7、通过四次挥手释放TCP连接;8、浏览器渲染;9、js引擎解析

【1、URL解析】:判断浏览器输入的是搜索内容还是URL

【2、查找缓存】:如果能找到缓存则直接返回页面,如果没有缓存则需要发送网络请求页面

【3、DNS域名解析】:将域名转换为IP地址的过程,得到了服务器具体的IP地址,才可以进行TCP链接以及数据的传输。

【4、三次握手建立TCP连接】:

  • 第一次握手:客户端主动链接服务器,发送初始序列号seq=x与SYN=1同步请求标志,并进入同步已发送SYN_SENT状态,等待服务器确认。

  • 第二次握手:服务端收到消息后发送确认标志ACK=1与同步请求标志SYN=1,发送自己的序列号seq=y以及客户端确认序号ack=x+1,此时服务器进入同步收到SYN_RECV状态。

  • 第三次握手:客户端收到消息后发送确认标志ACK=1,发送自己的序列号seq=x+1与服务器确认号ack=y+1,发送过后即确认链接已建立状态ESTABLISHED,服务端接收确认信息后进入链接已建立状态ESTABLISHED。

【5、发起HTTP请求】:浏览器构建HTTP请求报文,并通过TCP协议传送到服务器的指定端口,HTTP请求报文一共有三个部分

  • 报文首部,通常包含请求行与各种请求头字段等;

  • 空行,告诉服务器请求头部到此为止

  • 报文主体,即发送的数据信息,通常并不一定要有报文主体。

【6、服务器响应并返回结果】:服务端响应HTTP请求,返回响应报文,HTTP响应报文由四部分组成:响应行、响应头、空行、响应体。

【7、通过四次握手释放TCP连接】

【8、浏览器渲染】

【9、js引擎解析】

 

三十三 浏览器如何渲染页面

  1. HTML被HTML解析器解析成DOM树。
  2. CSS被CSS解析器解析成CSS规则树。
  3. 浏览器会将CSS规则树附着在DOM树上,并结合两者生成渲染树Render Tree。
  4. 生成布局(flow),浏览器通过解析计算出每一个渲染树节点的位置和大小,在屏幕上画出渲染树的所有节点。
  5. 将布局绘制(paint)在屏幕上,显示出整个页面

 

三十四 重绘重排区别,如何避免

重绘与回流是浏览器渲染的时候进行的操作,当页面内容发生改变的时候,就会触发重绘或者回流

【重绘】

当渲染树中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color,则就叫称为重绘(Repaint)

【回流】

当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建,这就称为回流(Reflow),有些地方也称为重排,可理解为重新布局。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建渲染树,回流一定触发重绘。(一般像页面初次渲染、带有动画的元素、添加/删除功能、图片放大缩小、浏览器窗口发生改变的时候都会触发回流。)

【二者关系】

  • 回流必定会触发重绘,而重绘则不一定会触发回流。一般来说,回流对性能的损耗更多一点

  • Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。在一些高性能的电脑上也许还没什么,但是如果reflow发生在手机上,那么这个过程就会卡顿与耗电。

如何避免:1.用visiability:hidden代替display:none, 2.用transform代替top 3.避免规则层级过多 4.避免使用table 5.避免将节点的外观属性值放入循环语句中

 

三十五 浏览器垃圾回收机制

有两种机制:

1、标记清除:对所有活动对象进行标记,清除阶段会将没有标记的对象清除;标记整理算法:标记结束后,算法将活动对象压入内存一端,则需要清理的对象在边界,直接被清理掉就行。(效率低)

2、引用计数:将对象是否不再需要简化定义为有没有其他对象引用它,如果没有引用指向这个对象,则会被垃圾回收机制回收。(内存空间不连续)

 

三十六 事件循环event loop,宏任务,微任务

JS是单线程,事件循环是JS执行机制。

任务分为同步任务和异步任务,异步任务又分为宏任务和微任务。

主线程会先执行同步任务,执行完了再执行异步任务。

执行异步任务的时候会先看有没有微任务,有的话先执行所有的微任务,再执行宏任务。

执行完一个宏任务之后会看还有没有微任务,有就执行微任务,没有就执行下一个宏任务。

promise对象函数中的代码属于同步任务,定时器,Ajax,script标签内部的代码属于宏任务,promise中的then,catch回调,process.nexttick属于微任务

 

三十七 跨域是什么,如何解决

跨域是指浏览器不能执行其他网站的脚本,只要协议、主机地址、端口其中之一不同就算跨域。这是由浏览的同源策略造成的,跨域访问是被各大浏览器所禁止的;

解决方案:1、jsonp在script标签中加入src 2、后端服务器的CORS(涉及预检options) 3、前端proxy代理+后端nginx反向代理。

三十八 vue钩子函数

Vue实例需要经过创建、初始化数据、编译模板、挂载DOM、渲染、更新、渲染、卸载等一系列过程,这个过程就是Vue的生命周期,在Vue的整个生命周期中提供很多钩子函数在生命周期的不同时刻调用,Vue中提供的钩子函数有:

挂载阶段:

  • beforeCreate:创建实例之前

  • created:实例创建完成(执行new Vue(options)),可访问data、computed、watch、methods上的方法和数据,可进行数据请求,未挂载到DOM结构上,不能获取el属性,如果要进行dom操作,那就要用nextTick函数

  • beforeMount:在挂载开始之前被调用,beforeMount之前,会找到对应的template,并编译成render函数

  • mounted:实例挂载到DOM上,此时可以通过DOM API获取到DOM节点,可进行数据请求

更新阶段:

  • beforeUpdate:响应式数据更新时调用,发生在虚拟DOM打补丁之前,适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器

  • updated:虚拟 DOM 重新渲染和打补丁之后调用,组件DOM已经更新

销毁阶段:

  • beforeDestroy:实例销毁之前调用,this仍能获取到实例,常用于销毁定时器、解绑全局事件、销毁插件对象等操作

  • destroyed:实例销毁之后

父子组件执行顺序:

  • 挂载:父created -> 子created -> 子mounted> 父mounted

  • 更新:父beforeUpdate -> 子beforeUpdated -> 子updated -> 父亲updated

  • 销毁:父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed

 

三十九 组件通信方式

1.答 Vue组件的通信方式分为两大类,一类是父子组件通信,另一类是任何关系类型组件通信(父子、兄弟、非兄弟)

2.父传子在子组件中定义props,子传父使用emit()emit()、refs

3.兄弟组件通信常用定义的公共事件bus的on(eventbus)on (eventbus)、emit;

4.祖先和子孙组件通信常用attrsattrs和listener、provide和inject;

5.复杂通信常用vuex/pinia

 

Vue的组件通信一般分为三大类。1:父子组件通信,最常见的使用props和emit,父组件通过props将数据传递给子组件,子组件通过emit触发父组件中的方法来修改数据。其次还可以通过refref、parent和child进行通信。2:祖孙组件之间的通信:这种通信一般也可以用propsemit只不过逐层传递会很麻烦,可以可以使用child进行通信。2:祖孙组件之间的通信:这种通信一般也可以用props和emit只不过逐层传递会很麻烦,可以可以使用listener和attrs来进行通信。3:兄弟组件之间的通信:可以创建eventBus事件总线,通过attrs来进行通信。3:兄弟组件之间的通信:可以创建eventBus事件总线,通过emit和$on的方式进行通信。其次还有全局数据通信,我们可以使用Vuex作为全局状态管理来实现。

 

四十 computed 和watch区别

computed是计算属性,依赖其他属性值,并且有缓存,只有当依赖的值发生了变化之后才会重新计算,不支持异步操作,而watch更多的是监听,当监听的值发生了变化,会立即执行回调进行操作,支持异步

 

1、功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。

2、是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。

3、是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。

4、computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)

5、使用场景:computed----当一个属性受多个属性影响的时候,使用computed-----购物车商品结算。watch–当一条数据影响多条数据的时候,使用watch-----搜索框.

 

四十一 v-if,v-show区别√

v-show是操作display:none,而v-if是操作Dom直接进行删除和添加,在性能上不及v-show

  1. v-if:是否渲染,耗费的性能大
  2. v-show:是否显示,相当于是否设置display:none

 

v-if和v-show都是用来控制DOM元素是否显示在页面上,v-if是通过从DOM树上插入和删除实现控制DOM元素的显示,而v-show通过设置元素的css属性display控制DOM元素的显示。

 

四十二 vue的keep-alive

1、keep-alive是vue的内置组件,能在组件切换过程中将状态保留在内存中,相当于缓存,防止DOM的重复渲染;

2、keep-alive有三个属性:include(只有名字匹配的才会被缓存)、exclude(任何名字匹配的都不会被缓存)、max(最多可以缓存多少个组件)。

3、在路由router的中:相应组件下规定meta属性,定义keep-alive:true;

4、可以结合Vue组件实例加载顺序讲解,VNode->实例化->_updata->真实Node,在实例化的时候会判断该组件是否被keep-alive保存过,是的话则直接拿其中的DOM进行渲染。

 

  1. 效果方面:能在内存保持其中的组件状态,放置重复渲染DOM,减少加载时间,从而提高性能。
  2. 使用方面:有三个属性:include:只有匹配的组件才会被保存。exclude:只有匹配的组件才不会被保存。max:最多能保存的组件数。
  3. 结合Router使用:可以在相应组件下规定mate属性,并将keep-alive设置为true。
  4. 源码实现方面:可以结合Vue组件实例加载顺序讲解,VNode->实例化->_updata->真实Node,在实例化的时候会判断该组件是否被keep-alive保存过,是的话则直接拿其中的DOM进行渲染。

 

四十三 $nextTick作用原理

vue中更新dom是异步操作,在修改完数据后,视图不会立刻更新,而是当同一事件循环中的所有数据变化完成之后,再进行统一的更新,所以有的时候在修改完数据后想要获取更新后的dom,则需要在nextTick中获取,nextTick的作用就是在当前渲染完成后执行,,解决了异步获取不到更新后dom问题,本质是反回promise

四十四 vue列表为什么加key

Vue列表加key的目的是为diff算法添加标识,因为diff算法判断新旧VDOM是否相同的依据是节点的tag和key。如果tag和key相同则会进一步进行比较,使得尽可能多的节点进行复用。此外,key绑定的值一般是一个唯一的值,比如id。如果绑定数组的索引index,则起不到优化diff算法的作用,因为一旦数组内元素进行增删,后续节点的绑定的key也会发生变化,导致diff进行多余的更新操作。

四十五 vue-router懒加载方法

懒加载的核心思想是按需加载,也叫做异步加载:只有请求到该组件的时候,才会对该组件进行网络请求并加载。懒加载有利于解决页面首次请求资源过多,导致白屏时间长的问题。 vue-router的懒加载即通过箭头函数的写法导入组件如 const Login = ()=> import('../路径'); 实现懒加载:import(ES6)require+resolve(Vue)

1、import形式:component:() => import("./home.vue");

2、异步组件形式:component:resolve => (require(['./home.vue'], resolve));

四十六 hashRouter,historyRouter原理区别

1.history和hash都是利用浏览器的2种特性实现前端路由,history是利用浏览历史记录栈的API实现,hash是监听location hash值变化事件来实现

2.history的url没有#号,hash有#号

3.相同的url,history会触发添加到浏览器历史记录栈中,hash不会触发,history需要后端配合,如果后端不配合刷新页面会出现404,hash不需要 hashRouter原理:通过window.onhashchange获取url中hash值 historyRouter原理:通过history.pushState,使用它做页面跳转不会触发页面刷新,使用window.onpopstate监听浏览器的前进和后退

四十七 vuex是什么,每个属性是干什么

  1. 概念:Vuex是全局状态管理仓库,相当于window对象挂载了一个全局变量。
  2. 属性:state(数据源)、actions(处理异步事件)、mutations(处理同步事件)、getters(过滤数据)和modules(模块)

四十八 vue2双向绑定的原理,缺陷

  • 原理:通过数据劫持defineProperty+发布订阅者模式,当vue实例初始化后observer会针对实例中的data中的每一个属性进行劫持并通过defineProperty()设置值后在get()中向发布者添加该属性的订阅者,这里在编译模板时就会初始化每一属性的watcher,在数据发生更新后调用set时会通知发布者notify通知对应的订阅者做出数据更新,同时将新的数据根性到视图上显示。
  • 缺陷:只能够监听初始化实例中的data数据,动态添加值不能响应,要使用对应的Vue.set()。 不能监听数组的push等方法,用其他方法实现的,defineProperty只能监听对象的单个属性,需要递归对所有属性的监听,VUE3已经改成proxy

四十九 vue3实现数据双向绑定的方法

在Vue2.0的基础上将Object.definedproperty换成了功能更强大的proxy,原理相同。在vue实例初始化的时候(vm._init()执行的时候)调用Observe类的实例方法observe,传入数据(若是对象则发生递归),将其中每个数据进行一遍数据劫持(get实现依赖收集,set实现事件派发(这里的模式为发布订阅模式))。

补充:相对vue2.0解决的问题:解决无法监听新增属性或删除属性的响应式问题、解决无法监听数组长度和index变化问题。

五十 diff算法

  1. Diff算法主要就是在虚拟DOM树发生变化后,生成DOM树更新补丁的方式,对比新旧两株虚拟DOM树的变更差异,将更新补丁作用于真实DOM,以最小成本完成视图更新;
  2. 框架会将所有的结点先转化为虚拟节点Vnode,在发生更改后将VNode和原本页面的OldNode进行对比,然后以VNode为基准,在oldNode上进行准确的修改。(修改准则:原本没有新版有,则增加;原本有新版没有,则删除;都有则进行比较,都为文本结点则替换值;都为静态资源不处理;都为正常结点则替换)

diff的过程实质就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁。diff算法无非做的就是三件事:1、创建节点(新节点有,旧节点没有)2、删除节点(新节点没有,旧节点有)3、更新节点

五十一 前端性能优化手段

前端资源比较庞大,包括HTML、CSS、JavaScript、Image、Flash、Media、Font、Doc等等,前端优化相对比较复杂,对于各种资源的优化都有不同的方式,按粒度大致可以分为两类: 一类是文件加载更快

  1. 让传输的数据包更小(压缩文件/图片):图片压缩和文件压缩
  2. 减少网络请求的次数:雪碧图/精灵图、节流防抖
  3. 减少渲染的次数:缓存(HTTP缓存、本地缓存、Vue的keep-alive缓存等) 另一类是文件渲染更快
  4. 提前渲染:ssr服务器端渲染
  5. 避免渲染阻塞:CSS放在HTML的head中 JS放在HTML的body底部
  6. 避免无用渲染:懒加载
  7. 减少渲染次数:对dom查询进行缓存、将dom操作合并、使用减少重排的标签 在用户角度前端优化可以让页面加载得更快,对用户的操作响应得更及时,能够给用户提供更为友好的体验,在服务商角度前端优化能够减少页面请求数,减小请求所占带宽,能够节省服务器资源。

五十二 性能优化指标,如何量化

1.性能评估 Chrome Performance选项卡 / Lighthouse 生成性能检测报告 2.值得关注的性能指标 (1)LCP (Largest Contentful Paint 最大内容绘制 ) (2)首屏渲染时间(也叫白屏时间) (3)FCP (Fitst Contentful Paint 首先内容绘制 ) (4)可交互时间 (Time to Interactive TTI) (5) Network请求时间(jax,js等) 3.浏览器开发者工具什么都能看得到,可以调用性能监测API 或建立 前端监控系统(无痕埋点)

五十三 服务端渲染

定义:服务器端生成HTML直接返回给浏览器 作用:

1.减少网络传输,提高传输效率

2.首屏渲染快

3.有利于SEO,提高搜索效率

    • 传统服务端渲染 : JSP/PHP等等(前后端不分离的旧时代,模板引擎😅,后端拿到前端页面替换数据,前后端耦合) - 现代服务端渲染 : (Vue->Nuxt.js,React->Next.js,自己做SSR , 前后端分离的同时实现SSR) 2. 服务端渲染(SSR)即是在服务器端渲染完整的HTML后返回给客户端(通常是浏览器)。 3. 客户端渲染(CSR),也就是常见的单页面应用(SPA),由服务器端返回的初始 HTML 页面,由 JS 去异步加载数据,完成页面的渲染。由于ajax请求是异步的,百度Google等搜索引擎抓取的页面是几乎空白的,可以右键查看网页源代码看是否完整的HTML结构判断是SSR还是CSR。 4. 同构渲染,就是将SSR和CSR结合在一起,服务端渲染首屏后,激活应用,按照SPA方式运行,结合CSR和SSR各自优点。 5. 从上可知,服务端渲染的优点是: - 搜索引擎爬虫能爬取完整HTML,有利于做搜索引擎优化(SEO),提高网站流量和曝光率 - 首屏渲染在服务端就完成,所以有更快的首屏加载速度 - 只是首屏快,其他页面不像SPA一样是无感刷新,切换页面通过超链接进行页面跳转,体验稍差(传统PHP/JSP) 6.服务端渲染的缺点: - 由于是在服务端进行渲染,需要占用更多服务器端资源(更多的CPU和内存)。 - 由于在服务端渲染,所以有些浏览器API无法使用,同样地 客户端一些生命周期在SSR也是不存在的。 - 部署和开发要求稍高,前后端耦合。

五十四 xss攻击

  1. XSS是跨站脚本攻击(Cross Site Scripting)
  2. 攻击者可以通过向Web页面里面插入script代码,当用户浏览这个页面时,就会运行被插入的script代码,达到攻击者的目的。
  3. 危害:泄露用户的登录信息cookie;恶意跳转:直接在页面中插入window.location.href进行跳转。
  4. 防止XSS攻击的方法:

1).对输入框内容进行过滤和转码。过滤掉用户输入的与事件有关的回调,如onclick,过滤掉用户输入的style标签,script标签。

3).设置cookie的httponly,告诉浏览器不对客户端开放访问权限。

1).对用户的输入做Html实体编码

3).不对html实体直接进行解码

五十五 csrf攻击是什么

  1. 概念:跨域请求伪造。
  2. 原理:诱导用户跳转到新的页面,利用 服务器的验证漏洞 和 用户之前的登入状态,来模拟用户进行操作。
  3. 防范:利用cookie的sameSite属性规定其他网站不能使用本网站的cookie。或者使用token验证,再去验证用户身份。

tech.meituan.com/2018/10/11/…