把握铜三铁四,跟春招、校招说再见

97 阅读44分钟

前言

首先在这里祝大家在春招和校招都能找到一份自己如愿的工作,然后就是八股文是真的重要,尤其是js基础部分。然后就是希望我的文章能有帮到大家的地方

HTML5 新特性有哪些

  • 语义化标签
  • 音视频处理API(audio,video)
  • canvas / webGL
  • 拖拽释放(Drag and drop) API
  • history API
  • requestAnimationFrame
  • 地理位置(Geolocation)API
  • webSocket
  • web存储 localStorage、SessionStorage
  • 表单控件,calendar、date、time、email、url、search

HTML5 语义化的优点:

在没CSS样式的情况下,页面整体也会呈现很好的结构效果

代码结构清晰,易于阅读,

利于开发和维护 方便其他设备解析(如屏幕阅读器)根据语义渲染网页。

有利于搜索引擎优化(SEO),搜索引擎爬虫会根据不同的标签来赋予不同的权重

HTTP 状态码

  • 1xx:指示信息类,表示请求已接受,继续处理(临时响应)
  • 2xx:指示成功类,表示请求已成功接受
  • 3xx:指示重定向,表示要完成请求必须进行更近一步的操作
  • 4xx:指示客户端错误,请求有语法错误或请求无法实现
  • 5xx:指示服务器错误,服务器未能实现合法的请求

常见状态码

  • 【403】表示【服务器拒绝执行客户端的请求】
  • 【404】表示【服务器找不到客户端所请求的资源(网页)】
  • 【304】表示【所请求的资源并未修改(命中协商缓存)

介绍下 304 过程

缓存过期后向服务器发起请求验证缓存是否有效,有效的话则返回304。304多出现在对于静态资源的请求上面。对于静态资源来说:

当浏览器第一次发起请求时(请求头中没有If-Modified-Since),server会在响应中告诉浏览器这个资源最后修改的时间(响应头中的Last-Modified)。当你再次请求这个资源时,浏览器会询问server这个资源有没有被修改(请求头中If-Modified-Since)。

如果资源没有被修改,server返回304状态码,浏览器使用本地的缓存文件。

数组的常用方法有哪些?

  • join(separator):将数组的元素组起一个字符串,以separator为分隔符,省略的话则用默认用逗号为分隔符
  • push():将参数添加到原数组末尾,并返回数组的长度(修改原数组)
  • pop():删除原数组最后一项,并返回删除元素的值;如果数组为空则返回undefined(修改原数组)
  • shift():删除原数组第一项,并返回删除元素的值;如果数组为空则返回undefined
  • slice(start,end):可以截取出数组某部份的元素为一个新的数组,有两个必填的参数,第一个是起始位置,第二个是结束位置( 操作时数字减1 ) 原数组不改变
  • splice(start,deleteCount,val1,val2,…):从start位置开始删除deleteCount项,并从该位置起插入。(修改原数组)
  • fill():使用特定值填充数组中的一个或多个元素(修改原数组)
  • concat():可以将两个数组合并在一起,如果是使用ES6语法也可以用扩展运算符…来代替
  • indexOf():返回当前值在数组中第一次出现位置的索引
  • lastIndexOf():返回查找的字符串最后出现的位置,如果没有找到匹配字符串则返回 -1。
  • every():判断数组中每一项是否都符合条件
  • some():判断数组中是否存在满足的项
  • includes():判断一个数组是否包含指定的值
  • sort(orderfunction):按指定的参数对数组进行排序(修改原数组)
  • reverse():将数组反序(修改原数组)
  • forEach():循环遍历数组每一项(没有返回值)
  • map():循环遍历数组的每一项(有返回值)
  • copyWithin(): 从数组的指定位置拷贝元素到数组的另一个指定位置中(修改原数组)
  • find(): 返回第一个匹配的值,并停止查找
  • findIndex(): 返回第一个匹配值的索引,并停止查找
  • toLocaleString()、toString():将数组转换为字符串
  • flat()、flatMap():扁平化数组
  • entries() 、keys() 、values():遍历数组

CSS 选择器及优先级

选择器

  • id选择器(#myid)
  • 类选择器(.myclass)
  • 属性选择器(a[rel="external"])
  • 伪类选择器(a:hover, li:nth-child)
  • 标签选择器(div, h1,p)
  • 相邻选择器(h1 + p)
  • 子选择器(ul > li)
  • 后代选择器(li a)
  • 通配符选择器(*)

优先级:

  • !important
  • 内联样式(1000)
  • ID选择器(0100)
  • 类选择器/属性选择器/伪类选择器(0010)
  • 元素选择器/伪元素选择器(0001)
  • 关系选择器/通配符选择器(0000)

带!important 标记的样式属性优先级最高; 样式表的来源相同时: !important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性

== 和 === 区别

1. == 和 === 区别

== 表示相等 (值相等)

===表示恒等(类型和值都要相等)

js在比较的时候如果是 == 会先做类型转换,再判断值得大小,如果是===类型和值必须都相等。

2. == === 和 Object.is() 区别

== 相等,如果两边类型不一致,进行隐式转换后,再比较。+0 和 -0 相等, NaN 不等于任何数

=== 严格相等,如果类型不一致,就不相等。 +0 和 -0 相等, NaN 不等于任何数

Object.is() 严格相等,+0 和 -0 不相等, NaN 等于自身

script 标签放在 header 里和放在 body 底部里有什么区别?

放在 header 中

你能看到 html 第一时间被加载进来,但页面 body 内容迟迟没有渲染出来。因为在等待 header 标签中 script 脚本的加载,3 秒后,整个页面渲染完成。

放在 body 底部

这次 html 内容第一时间渲染完成,随后等待 js 的加载。

总结:

脚本会阻塞页面的渲染,所以推荐将其放在 body 底部,因为当解析到 script 标签时,通常页面的大部分内容都已经渲染完成,让用户马上能看到一个非空白页面。

另外你能看到多个脚本之间都是异步向服务器请求,他们之间不互相依赖,最终只等待 3 秒,而非 3+3+3 秒。

HTTP1、HTTP2、HTTP3 的区别 HTTP1.0:

HTTP1.0:

浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接

服务器完成请求处理后立即断开TCP连接。

简单来讲,每次与服务器交互,都需要新开一个连接

HTTP1.1:

引入了持久连接,即TCP连接默认不关闭,在同一个TCP连接里面,客户端可以同时发送多个请求

虽然允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的,服务器只有处理完一个请求,才会接着处理下一个请求。如果前面的处理特别慢,后面就会有许多请求排队等着

新增了一些请求方法,新增了一些请求头和响应头

HTTP2.0:

采用二进制格式而非文本格式

多路复用,只需一个连接即可实现并行

使用报头压缩,降低开销

服务器推送

HTTP3.0:

是 HTTP/3 中的底层支撑协议,该协议基于 UDP,又取了 TCP 中的精华,实现了即快又可靠的协议。

async

async 是异步的意思,await则可以理解为 async wait。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行

async

async函数返回一个promise对象,下面两种方法是等效的

function f() {return Promise.resolve('TEST');
}// asyncF is equivalent to f!
async function asyncF() {return 'TEST';
}

await

正常情况下,await命令后面是一个 Promise对象,返回该对象的结果。如果不是 Promise对象,就直接返回对应的值

示例 1

async function f(){
   // 等同于
  // return 123;
return await 123;
}
f().then(v => console.log(v)) 
  // 123

注:上面代码中,await 命令的参数是数值 123,这时等同于 return 123。

示例 2

async function f() {
        const p = new Promise((resolve,reject)=>{
            resolve(100)
        })
        const a = 1
    
        // await 等待promise的状态变化完成(pending->resolved, pending->rejected)
        // 取出promise的值
        const b = await p 

        console.log(a,b)//(1,100)
    }
    f()

不管await后面跟着的是什么,await都会阻塞后面的代码

async function fn1 (){
    console.log(1)await fn2()
    console.log(2) // 阻塞
}async function fn2 (){
    console.log('fn2')
}fn1()
console.log(3)

上面的例子中,await 会阻塞下面的代码(即加入微任务队列),先执行 async外面的同步代码,同步代码执行完,再回到 async 函数中,再执行之前阻塞的代码

所以上述输出结果为:1fn232

CSS3 新特性

  • 过渡

/所有属性从原始值到制定值的一个过渡,运动曲线ease,运动时间0.5秒/

transition:all,.5s

  • 动画

//animation:动画名称,一个周期花费时间,运动曲线(默认ease),动画延迟(默认0),播放次数(默认1),是否反向播放动画(默认normal),是否暂停动画(默认running)

/执行一次logo2-line动画,运动时间2秒,运动曲线为 linear/

animation: logo2-line 2s linear;

  • 形状转换

//transform:适用于2D或3D转换的元素

//transform-origin:转换元素的位置(围绕那个点进行转换)。默认(x,y,z):(50%,50%,0)

transform:translate(30px,30px);

transform:rotate(30deg);

transform:scale(.8);

  • 选择器:nth-of-type()

  • 阴影 文字阴影: text-shadow: 2px 2px 2px #000;(水平阴影,垂直阴影,模糊距离,阴影颜色) 盒子阴影: box-shadow: 10px 10px 5px #999

  • 边框 border-image: url(border.png);

  • 背景

  • 文字

  • 渐变-

  • Filter(滤镜)

  • 弹性布局、栅格布局、多列布局

  • 媒体查询

加密算法

对称加密:加密和解密都用相同的密钥。
优缺点:效率高,算法简单,系统开销小,适合加密大量数据。安全性差。

  • DES:分组式加密算法,以64位为分组对数据加密,加解密使用同一个算法。
  • 3DES:三重数据加密算法,对每个数据块应用三次DES加密算法。
  • AES:高级加密标准算法,目前已被广泛应用 非对称加密:采用公钥和私钥两种不同的密码来进行加解密。公钥和私钥是成对存在,公钥是从私钥中提取产生公开给所有人的,如果使用公钥对数据进行加密,那么只有对应的私钥(不能公开)才能解密,反之亦然。
  • RSA:两个大素数相乘十分容易,但那时想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥,可用于加密,也能用于签名。 (应用场景: 由于RSA算法的加密解密速度要比对称算法速度慢很多,在实际应用中,通常数据本身的加密和解密使用对称加密算法(AES)。 用RSA算法加密并传输对称算法所需的密钥。)
  • DSA:数字签名算法,仅能用于签名,不能用于加解密。
  • DSS:数字签名标准,可用于签名,也可以用于加解密。

JS 中的 8 种数据类型及区别

在JS中,我们已知有5种基本数据类型:String、Number、Boolean、Undefined、Null。

当ES6问世,新增基本数据类型:Symbol(ES6) BigInt(ES10)

一、Number类型:

专门保存数字的类型,可用于进行数学计算等的数值.

二、String类型:

专门用来保存字符串的类型;" ",用来存储字符串类型的文本.

三、Boolean类型:

专门用来保存真或者假的类型,值二选一,true or false

四、undefined类型:

只有一个值undefined,没有赋值的变量的默认值.

五、null类型:

不知向任何地址,手动赋值,清空内容等....

六、object / Symbol

Symbol在ES6中新定义,符号类型是唯一的并且不可修改。Symbol 指的是独一无二的值。

position 属性的值有哪些及其区别

position属性取值:static(默认)、relative、absolute、fixed、inherit、sticky。

float属性取值:none(默认)、left、right、inherit。

display属性取值:none、inline、inline-block、block、table相关属性值、inherit。

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

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

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

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

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

GET 与 POST 的区别

1.针对数据操作的类型不同.GET对数据进行查询,POST主要对数据进行增删改!简单说,GET是只读,POST是写。

2.参数大小不同. GET请求在URL中传送的参数是有长度的限制,而POST没有限制

3.安全性不同. GET参数通过URL传递,会暴露,不安全;POST放在Request Body中,相对更安全

4.浏览器回退表现不同 GET在浏览器回退时是无害的,而POST会再次提交请求

5.浏览器对请求地址的处理不同 GET请求地址会被浏览器主动缓存,而POST不会,除非手动设置

6.浏览器对响应的处理不同GET请求参数会被完整的保留在浏览器历史记录里,而POST中的参数不会被保留

var, cosnt, let 三者之间的区别

ES6之前创建变量用的是var,之后创建变量用的是let/const

  • var 存在变量提升,而const和let不存在变量提升;
  • let 不能重复定义 ; 关键字允许值的修改;
  • const 不能重复定义 ; 关键字不允许的修改;

WebSocket

实现原理: 实现了客户端与服务端的双向通信,只需要连接一次,就可以相互传输数据,很适合实时通讯、数据实时更新等场景。

Websocket 协议与 HTTP 协议没有关系,它是一个建立在 TCP 协议上的全新协议,为了兼容 HTTP 握手规范,在握手阶段依然使用 HTTP 协议,握手完成之后,数据通过 TCP 通道进行传输。

Websoket 数据传输是通过 frame 形式,一个消息可以分成几个片段传输。这样大数据可以分成一些小片段进行传输,不用考虑由于数据量大导致标志位不够的情况。也可以边生成数据边传递消息,提高传输效率。

优点: 双向通信。客户端和服务端双方 都可以主动发起通讯。 没有同源限制。客户端可以与任意服务端通信,不存在跨域问题。 数据量轻。第一次连接时需要携带请求头,后面数据通信都不需要带请求头,减少了请求头的负荷。 传输效率高。因为只需要一次连接,所以数据传输效率高。

缺点: 长连接需要后端处理业务的代码更稳定,推送消息相对复杂; 兼容性,WebSocket 只支持 IE10 及其以上版本。 服务器长期维护长连接需要一定的成本,各个浏览器支持程度不一; 【需要后端代码稳定,受网络限制大,兼容性差,维护成本高,生态圈小】

Socket 连接的步骤

建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket ,另一个运行于服务器端,称为ServerSocket 。

1、服务器监听:处于等待连接的状态,实时监控网络状态,等待客户端的连接请求
2、客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。
客户端的套接字:指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。 3、连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。
而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

谈谈盒子模型?

标准盒子模型中,width 和 height 指的是内容区域的宽度和高度。增加内边距、边框和外边距不会 影响内容区域的尺寸,但是会增加元素框的总尺寸。
IE盒子模型中,width 和 height 指的是内容区域+border+padding的宽度和高度。

BFC(块级格式上下文)

BFC的概念

BFC 是 Block Formatting Context的缩写,即块级格式化上下文。BFC是CSS布局的一个概念,是一个独立的渲染区域,规定了内部box如何布局, 并且这个区域的子元素不会影响到外面的元素,其中比较重要的布局规则有内部 box 垂直放置,计算 BFC 的高度的时候,浮动元素也参与计算。

BFC的原理布局规则

  • 内部的Box会在垂直方向,一个接一个地放置
  • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠
  • 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反
  • BFC的区域不会与float box重叠
  • BFC是一个独立容器,容器里面的子元素不会影响到外面的元素
  • 计算BFC的高度时,浮动元素也参与计算高度
  • 元素的类型和display属性,决定了这个Box的类型。不同类型的Box会参与不同的Formatting Context。

如何创建BFC?

  • 根元素,即HTML元素
  • float的值不为none
  • position为absolute或fixed
  • display的值为inline-block、table-cell、table-caption
  • overflow的值不为visible

BFC的使用场景

  • 去除边距重叠现象
  • 清除浮动(让父元素的高度包含子浮动元素)
  • 避免某元素被浮动元素覆盖
  • 避免多列布局由于宽度计算四舍五入而自动换行

数据类型判断 instanceof 和 typeof 的区别

1.typeof

console.log(typeof 1);               // number
console.log(typeof true);            // boolean
console.log(typeof 'mc');            // string
console.log(typeof Symbol)           // function
console.log(typeof function(){});    // function
console.log(typeof console.log());   // function
console.log(typeof console.log);     // undefined
console.log(typeof []);              // object 
console.log(typeof {});              // object
console.log(typeof null);            // object
console.log(typeof undefined);       // undefined 

优点:能够快速区分基本数据类型
缺点:不能将Object、Array和Null区分,都返回object

2.instanceof

console.log(1 instanceof Number);                    // false
console.log(true instanceof Boolean);                // false 
console.log('str' instanceof String);                // false  
console.log([] instanceof Array);                    // true
console.log(function(){} instanceof Function);       // true
console.log({} instanceof Object);                   // true 

优点:能够区分Array、Object和Function,适合用于判断自定义的类实例对象
缺点:Number,Boolean,String基本数据类型不能判断

3.Object.prototype.toString.call()

var toString = Object.prototype.toString;
console.log(toString.call(1));      //[object Number]
console.log(toString.call(true));   //[object Boolean]
console.log(toString.call('mc'));   //[object String]
console.log(toString.call([]));     //[object Array]
console.log(toString.call({}));     //[object Object]
console.log(toString.call(function(){})); //[object Function]
console.log(toString.call(undefined));  //[object Undefined]
console.log(toString.call(null)); //[object Null] 

优点:精准判断数据类型
缺点:写法繁琐不容易记,推荐进行封装后使用

隐藏页面元素的方法

1.opacity:0,该元素隐藏起来了,但不会改变页面布局,并且,如果该元素已经绑定 一些事件,如click 事件,那么点击该区域,也能触发点击事件的

2.visibility:hidden,该元素隐藏起来了,但不会改变页面布局,但是不会触发该元素已 经绑定的事件 ,隐藏对应元素,在文档布局中仍保留原来的空间(重绘)

3.display:none,把元素隐藏起来,并且会改变页面布局,可以理解成在页面中把该元素。 不显示对应的元素,在文档布局中不再分配空间(回流+重绘)

该问题会引出 回流和重绘

DNS 协议是什么?

DNS服务器通过多层查询将解析域名为IP地址
域名劫持:是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回假的IP地址或者什么都不做使请求失去响应,其效果就是对特定的网络不能访问或访问的是假网址。

深拷贝浅拷贝的区别

浅拷贝: 如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址。即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址

  • Object.assign
  • Array.prototype.slice(), Array.prototype.concat()
  • 使用拓展运算符实现的复制

深拷贝: 深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性

常见的深拷贝方式有:

  • _.cloneDeep()
  • jQuery.extend()
  • JSON.stringify()
  • 手写循环递归

浏览器的主要功能

是向服务器发出请求,在浏览器窗口中展示所选择的网络资源。这里所说的资源一般是指 HTML 文档,也可以是 PDF、图片或其他的类型。资源的位置由用户使用 URI(统一资源标示符)指定。

浏览器的主要组成部分是什么?
1、用户界面 - 包括地址栏、前进/后退按钮、书签菜单等。
2、浏览器引擎 - 在用户界面和呈现引擎之间传送指令。
3、呈现引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
4、网络 - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
5、用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
6、JavaScript 解释器。用于解析和执行 JavaScript 代码。
7、数据存储。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。

闭包

闭包的特性:

  • 闭包让你可以在一个内层函数中访问到其外层函数的作用域 函数嵌套函数
  • 延长变量的生命周期 在内存中维持一个变量
  • 一般函数的词法环境在函数返回后就被销毁,但是闭包会保存对创建时所在词法环境的引用,即便创建时所在的执行上下文被销毁,但创建时所在词法环境依然存在,以达到延长变量的生命周期的目的

闭包形成的条件

  • 函数的嵌套
  • 内部函数引用外部函数的局部变量,延长外部函数的变量生命周期

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

闭包应用场景

闭包的两个场景,闭包的两大作用:保存/保护。 在开发中, 其实我们随处可见闭包的身影, 大部分前端JavaScript 代码都是“事件驱动”的,即一个事件绑定的回调方法; 发送ajax请求成功|失败的回调;setTimeout的延时回调;或者一个函数内部返回另一个匿名函数,这些都是闭包的应用。

Rem 布局

首先 Rem 相对于根(html)的 font-size 大小来计算。简单的说它就是一个相对单例 如:font-size:10px; 那么(1rem = 10px)了解计算原理后首先解决怎么在不同设备上设置 html 的 font-size 大小。其实 rem 布局的本质是等比缩放,一般是基于宽度。
优点:可以快速适用移动端布局,字体,图片高度
缺点
①目前 ie 不支持,对 pc 页面来讲使用次数不多;
②数据量大:所有的图片,盒子都需要我们去给一个准确的值;才能保证不同机型的适配;
③在响应式布局中,必须通过 js 来动态控制根元素 font-size 的大小。也就是说 css 样式和 js 代码有一定的耦合性。且必须将改变 font-size 的代码放在 css 样式之前。

浏览器是如何渲染 UI 的?

浏览器是如何渲染UI的?
1、浏览器获取HTML文件,然后对文件进行解析,形成DOM Tree
2、与此同时,进行CSS解析,生成Style Rules
3、接着将DOM Tree与Style Rules合成为 Render Tree
4、接着进入布局(Layout)阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标
5、随后调用GPU进行绘制(Paint),遍历Render Tree的节点,并将元素呈现出来
DOM Tree是如何构建的?
1、转码: 浏览器将接收到的二进制数据按照指定编码格式转化为HTML字符串
2、生成Tokens: 之后开始parser,浏览器会将HTML字符串解析成Tokens
3、构建Nodes: 对Node添加特定的属性,通过指针确定 Node 的父、子、兄弟关系和所属 treeScope
4、生成DOM Tree: 通过node包含的指针确定的关系构建出DOM

浏览器重绘与回流

浏览器是如何渲染UI的?

1、浏览器获取HTML文件,然后对文件进行解析,形成DOM Tree
2、与此同时,进行CSS解析,生成Style Rules
3、接着将DOM Tree与Style Rules合成为 Render Tree
4、接着进入布局(Layout)阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标
5、随后调用GPU进行绘制(Paint),遍历Render Tree的节点,并将元素呈现出来

DOM Tree是如何构建的?

1、转码: 浏览器将接收到的二进制数据按照指定编码格式转化为HTML字符串
2、生成Tokens: 之后开始parser,浏览器会将HTML字符串解析成Tokens
3、构建Nodes: 对Node添加特定的属性,通过指针确定 Node 的父、子、兄弟关系和所属 treeScope
4、生成DOM Tree: 通过node包含的指针确定的关系构建出DOM重排/回流(Reflow):当DOM的变化影响了元素的几何信息,浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。表现为重新生成布局,重新排列元素。
回流:1、布局引擎会根据各种样式计算每个盒子在页面上的大小与位置。
2、改变元素的位置和尺寸大小都会引发回流

123.png

重绘(Repaint): 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。表现为某些元素的外观被改变。
重绘:1、当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制。
2、『回流』必定会发生『重绘』,『重绘』不一定会引发『回流』。

124.png

重绘与重排的区别?

重排/回流(Reflow):当DOM的变化影响了元素的几何信息,浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。表现为重新生成布局,重新排列元素。

重绘(Repaint): 当一个元素的外观发生改变,但没有改变布局,叫做重绘。表现为某些元素的外观被改变

『重绘』不一定会出现『重排』,『重排』必然会出现『重绘』。

如何触发重排和重绘?

  • 添加、删除、更新DOM节点
  • 通过display: none隐藏一个DOM节点-触发重排和重绘
  • 通过visibility: hidden隐藏一个DOM节点-只触发重绘
  • 移动或者给页面中的DOM节点添加动画
  • 添加一个样式表,调整样式属性
  • 用户行为,例如调整窗口大小,改变字号,或者滚动。

如何避免重绘或者重排?

  • 集中改变样式,不要一条一条地修改 DOM 的样式。

  • 不要把 DOM 结点的属性值放在循环里当成循环里的变量。

  • 尽量只修改position:absolute或fixed元素,对其他元素影响不大

  • 提升为合成层

    优点

  1. 合成层的位图,会交由 GPU 合成,比 CPU 处理要快
  2. 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层
  3. 对于 transform 和 opacity 效果,不会触发 layout 和 paint
    方式:是使用 CSS 的 will-change 属性
    #target {
        will-change:transform
    }

Cookie、sessionStorage、localStorage 的区别

相同点
存储在客户端
不同点

  • cookie数据大小不能超过4k;sessionStorage和localStorage的存储比cookie大得多,可以达到5M+
  • cookie设置的过期时间之前一直有效;localStorage永久存储,浏览器关闭后数据不丢失除非主动删除数据;sessionStorage数据在当前浏览器窗口关闭后自动删
  • cookie的数据会自动的传递到服务器;sessionStorage和localStorage数据保存在本地

indexDB

seesionStorage只能存储字符串的数据,对于JS中常用的数组或对象却不能直接存储,我们可以通过JSON.stringify()将json数据类型转化成字符串,再存储到storage中就可以了,获取数据时再使用JSON.parse()将读取的字符串转换成对象即可。

  1. 新打开的标签页会复制父级的 sessionStorage,类似于深拷贝,之后 sessionStorage 的变更不会同步。
  2. 相同 url 的多个 tab 页,sessionStorage 并不会同步,也就是说它们不属于同一个 session。

应用场景:

  • 标记用户与跟踪用户行为的情况,推荐使用cookie

  • 适合长期保存在本地的数据(令牌),推荐使用localStorage

  • 敏感账号一次性登录,推荐使用sessionStorage

  • 存储大量数据的情况、在线文档(富文本编辑器)保存编辑历史的情况,推荐使用indexedDB

JS 中 this 的情况

1、普通函数调用:通过函数名()直接调用:this指向全局对象window(注意let定义的变量不是window属性,只有window.xxx定义的才是。即let a =’aaa’; this.a是undefined)
2、构造函数调用:函数作为构造函数,用new关键字调用时:this指向新new出的对象
3、对象函数调用:通过对象.函数名()调用的:this指向这个对象
4、箭头函数调用:箭头函数里面没有 this ,所以永远是上层作用域this(上下文)
5、apply和call调用:函数体内 this 的指向的是 call/apply 方法第一个参数,若为空默认是指向全局对象window。
6、函数作为数组的一个元素,通过数组下标调用的:this指向这个数组
7、函数作为window内置函数的回调函数调用:this指向window(如setInterval setTimeout 等)

箭头函数有哪些特征,请简单描述一下它?

  • 箭头函数没有自己的this,this指向定义箭头函数时所处的外部执行环境的this
  • 即时调用call/apply/bind也无法改变箭头函数的this
  • 箭头函数本身没有名字
  • 箭头函数不能new,会报错
  • 箭头函数没有arguments,在箭头函数内访问这个变量访问的是外部执行环境的arguments
  • 箭头函数没有prototype

CSS 预处理器 Sass、Less、Stylus 的区别

什么是CSS预处理器?
CSS预处理器是一种语言用来为CSS增加一些变成的特性,无需考虑浏览器兼容问题,例如你可以在CSS中使用变量,简单的程序逻辑、函数等在编程语言中的一些基本技巧,可以让CSS更加简洁,适应性更强,代码更直观等诸多好处
基本语法区别
Sass是以.sass为扩展名,Less是以.less为扩展名,Stylus是以.styl为扩展名
变量的区别
Sass 变量必须是以$开头的,然后变量和值之间使用冒号(:)隔开,和css属性是一样的。 Less 变量是以@开头的,其余sass都是一样的。 Stylus 对变量是没有任何设定的,可以是以$开头或者任意字符,而且变量之间可以冒号,空格隔开,但是在stylus中不能用@开头

前端性能优化

1、减少http请求数;2、图片优化;3、使用CDN;4、开启GZIP;5、构建优化;

  1. 减少http请求数
    1)合并图片。当图片较多时,可以合并为一张大图,从而减少http请求数。
    2)合并压缩css样式表和js脚本。
    一般我们会把css样式表文件放到文件的头部。比如,放到标签中,这样可以让CSS样式表尽早地完成下载。对应js脚本文件,一般我们把他放到页面的尾部。
    3)充分利用缓存。

  2. 图片优化
    1)尽可能的使用PNG格式的图片,它相对来说体积较小。
    2)图片的延迟加载,也叫做懒加载。

  3. 使用CDN
    CDN即内容分发网络,可以使用户就近取得所需内容,解决网络拥挤的状况,提高用户访问网站的响应速度。

  4. 开启GZIP
    GZIP即数据压缩,用于压缩使用Internet传输的所有文本资源。开启GZIP的方法很简单,到对应的web服务配置文件中设置一下即可。以Apache为例,在配置文件httpd.conf中添加。

  5. 构建优化
    使用 Tree-shaking、Scope hoisting、Code-splitting
    Tree-shaking是一种在构建过程中清除无用代码的技术。使用Tree-shaking可以减少构建后文件的体积。

元素水平垂直居中

仅居中元素定宽高适用

  • absolute + 负margin
  • absolute + margin auto
  • absolute + calc

居中元素不定宽高

  • absolute + transform
  • lineheight
  • writing-mode
  • table
  • css-table
  • flex
  • grid
  • PC端有兼容性要求,宽高固定,推荐absolute + 负margin
  • PC端有兼容要求,宽高不固定,推荐css-table
  • PC端无兼容性要求,推荐flex
  • 移动端推荐使用flex

一个页面从输入 URL 到页面加载显示完成,这个过程中都发生 了什么?

  • 01.浏览器查找域名对应的IP地址(DNS 查询:浏览器缓存->系统缓存->路由器缓存->ISP DNS 缓存->根 域名服务器)
  • 02.浏览器向 Web 服务器发送一个 HTTP 请求(TCP三次握手
  • 03.服务器 301 重定向(从 example.com 重定向到 www.example.com)
  • 04.浏览器跟踪重定向地址,请求另一个带 www 的网址
  • 05.服务器处理请求(通过路由读取资源)
  • 06.服务器返回一个 HTTP 响应(报头中把 Content-type 设置为 'text/html')
  • 07.浏览器进 DOM 树构建
  • 08.浏览器发送请求获取嵌在 HTML 中的资源(如图片、音频、视频、CSS、JS等)
  • 09.浏览器显示完成页面
  • 10.浏览器发送异步请求

跨域

明跨域访问的限制并不是浏览器限制发送请求,而是浏览器阻止了请求后数据的加载渲染

  1. 定义:
  • 跨域:是由浏览器的同源策略造成的。
  • 同源策略,是浏览器对 JavaScript 实施的安全限制,只要协议、域名、端口有任何一个不同,都被当作是不同的域。
  • 跨域原理,即是通过各种方式,避开浏览器的安全限制。
  1. 解决方案: 最初做项目的时候,使用的是 jsonp,但存在一些问题,使用 get 请求不安全,携带数据较小,后来通过了解和学习发现使用 proxy 代理使用比较方便--在开发中使用 proxy,在服务器上使用 nginx 代理,这样开发过程中彼此都方便,效率也高;现在 h5 新特性还有 windows.postMessage()
  • JSONP: ajax 请求受同源策略影响,不允许进行跨域请求,而 script 标签 src 属性中的链接却可以访问跨域的 js 脚本,利用这个特性,服务端不再返回 JSON 格式的数据,而是 返回一段调用某个函数的 js 代码,在 src 中进行了调用,这样实现了跨域。
  • 步骤: ① 去创建一个 script ② script 的 src 属性设置接口地址 ③ 接口参数,必须要带一个自定义函数名,要不然后台无法返回数据 ④ 通过定义函数名去接受返回的数据
//动态创建 script
var script = document.createElement('script');

// 设置回调函数
function getData(data) {
    console.log(data);
}

//设置 script 的 src 属性,并设置请求地址
script.src = 'http://localhost:3000/?callback=getData';

// 让 script 生效
document.body.appendChild(script);
  • 缺点: JSON 只支持 get,因为 script 标签只能使用 get 请求; JSONP 需要后端配合返回指定格式的数据。
  • CORS CORS:使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin上的Web应用被准许访问来自不同源服务器上的指定的资源。服务器设置对CORS的支持原理:服务器设置Access-Control-Allow-Origin HTTP响应头之后,浏览器将会允许跨域请求
  • 最方便的跨域方案 proxy代理+ Nginx nginx是一款极其强大的web服务器,其优点就是轻量级、启动快、高并发。 跨域问题的产生是因为浏览器的同源政策造成的,但是服务器与服务器之间的数据交换是没有这个限制。
  • 反向代理就是采用这种方式,建立一个虚拟的代理服务器来接收 internet 上的链接请求,然后转发给内部网络上的服务器,并将从服务器上得到的结果,返回给 internet 上请求链接的客户端。现在的新项目中nginx几乎是首选,我们用node或者java开发的服务通常都需要经过nginx的反向代理。
111.png

Event Loop 的执行顺序

宏任务

  • Task Queue
  • 常见宏任务:setTimeout、setInterval、setImmediate、I/O、script、UI rendering

微任务

  • Job Queue
  • 常见微任务:
  • 浏览器:Promise、MutationObserver
  • Node.js:process.nextTick

执行顺序

  • 首先执行同步代码,宏任务

  • 同步栈为空,查询是否有异步代码需要执行

  • 执行所有微任务

  • 执行完,是否需要渲染页面

  • 重新开始 Event Loop,执行宏任务中的异步代码

什么是原型 ? 什么是原型链 ?

JavaScript 一种基于原型的语言——每个函数对象都有一个 prototype 属性,这个属性指向函数的原型对象。 原型关系:

  • 每个 class都有显示原型 prototype
  • 每个实例都有隐式原型 proto
  • 实例的 proto 指向对应 class 的 prototype

当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

原型链:函数的原型链对象 constructor 默认指向函数本身,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针 proto, 该指针是指向上一层的原型对象,而上一层的原型对象的结构依然类似。因此可以利用__proto__一直指向Object的原型对象上,而 Object 原型对象用 Object.prototype.__ proto__ = null 表示原型链顶端。如此形成了js的原型链继承。

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

什么是作用域 ? 什么是作用域链 ?

创建函数的时候,已经声明了当前函数的作用域 ==>当前创建函数所处的上下文。

  • 定义:简单来说作用域就是变量与函数的可访问范围,由当前环境与上层环境的一系列变量对象组成
  1. 全局作用域:代码在程序的任何地方都能被访问,window 对象的内置属性都拥有全局作用域。
  2. 函数作用域:在固定的代码片段才能被访问
  • 作用域:作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突
  • 作用域链:一般情况下,变量到 创建该变量 的函数的作用域中取值。但是如果在当前作用域中没有查到,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。

图片懒加载是怎么实现的?

就是我们先设置图片的data-set属性(当然也可以是其他任意的,只要不会发送http请求就行了,作用 就是为了存取值)值为其图片路径,由于不是src,所以不会发送http请求。 然后我们计算出页面 scrollTop的高度和浏览器的高度之和, 如果图片距离页面顶端的坐标Y(相对于整个页面,而不是浏览 器窗口)小于前两者之和,就说明图片就要显示出来了(合适的时机,当然也可以是其他情况),这时 候我们再将 data-set 属性替换为 src 属性即可。

谈谈set 、 map 是什么?

set 是es6 提供的一种新的数据结构,它类似于数组,但是成员的值都是唯一的。 map 是es6 提供的一种新的数据结构,它类似于对象,也是键值对的集合,但是键的范围不仅限于字符 串,各种类型的值都可以当做键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供 了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

v-for 循环为什么一定要绑定key ?

页面上的标签都对应具体的虚拟dom对象(虚拟dom就是js对象), 循环中 ,如果没有唯一key , 页面上删除 一条标签, 由于并不知道删除的是那一条! 所以要把全部虚拟dom重新渲染, 如果知道key为x标签被删除 掉, 只需要把渲染的dom为x的标签去掉即可!

组件中的data为什么要定义成一个函数而不是一个对象?

每个组件都是 Vue 的实例。组件共享 data 属性,当 data 的值是同一个引用类型的值时,改变其中一 个会影响其他

vue实例是挂载到那个标签上的?

vue实例最后会挂载在body标签里面,所以我们在vue中是获取不了body 标签的,如果要使用body标 签的话需要用原生的方式获取

js的执行机制是怎么样的?

  • js是一个单线程、异步、非阻塞I/O模型、 event loop事件循环的执行机制
  • 所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。异步 任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程, 某个异步任务可以执行了,该任务才会进入主线程执行。

vue中computed 和watch 的区别是什么?

computed计算属性就是为了简化template里面模版字符串的计算复杂度、防止模版太过冗余。它具有 缓存特性

computed用来监控自己定义的变量,该变量不在data里面声明,直接在computed里面定义,然后就 可以在页面上进行双向数据绑定展示出结果或者用作其他处理;

watch主要用于监控vue实例的变化,它监控的变量当然必须在data里面声明才可以,它可以监控一个 变量,也可以是一个对象,一般用于监控路由、input输入框的值特殊处理等等,它比较适合的场景是 一个数据影响多个数据,它不具有缓存性

  • watch:监测的是属性值, 只要属性值发生变化,其都会触发执行回调函数来执行一系列操作。
  • computed:监测的是依赖值,依赖值不变的情况下其会直接读取缓存进行复用,变化的情况下才 会重新计算。

除此之外,有点很重要的区别是:计算属性不能执行异步任务,计算属性必须同步执行。也就是说计算 属性不能向服务器请求或者执行异步任务。如果遇到异步任务,就交给侦听属性。watch也可以检测 computed属性。

什么vuex ,谈谈你对它的理解?

  1. 首先vuex的出现是为了解决web组件化开发的过程中,各组件之间传值的复杂和混乱的问题
  2. 将我们在多个组件中需要共享的数据放到store中,
  3. 要获取或格式化数据需要使用getters,
  4. 改变store中的数据,使用mutation,但是只能包含同步的操作,在具体组件里面调用的方式 this.$store.commit('xxxx')
  5. Action也是改变store中的数据,不过是提交的mutation,并且可以包含异步操作,在组件中的调 用方式 this.$store.dispatch('xxx') ; 在actions里面使用的commit('调用mutation')

后台管理系统中的权限管理是怎么实现的?

  • 登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token, 拿到token之后(我会将这个token存贮到cookie中,保证刷新页面后能记住用户登录状态),前端会 根据token再去拉取一个 user_info 的接口来获取用户的详细信息(如用户权限,用户名等等信息)。

  • 权限验证:通过token获取用户对应的 权限,动态根据用户的 权限算出其对应有权限的路由,通过 router.addRoutes 动态挂载这些路由。

  • 具体思路: 登录成功后,服务端会返回一个 token(该token的是一个能唯一标示用户身份的一个key),之后我 们将token存储在本地cookie之中,这样下次打开页面或者刷新页面的时候能记住用户的登录状态,不 用再去登录页面重新登录了。

    ps:为了保证安全性,我司现在后台所有token有效期(Expires/Max-Age)都是Session,就是当浏览器关 闭了就丢失了。重新打开游览器都需要重新登录验证,后端也会在每周固定一个时间点重新刷新 token,让后台用户全部重新登录一次,确保后台用户不会因为电脑遗失或者其它原因被人随意使用账 号。

    用户登录成功之后,我们会在全局钩子 router.beforeEach 中拦截路由,判断是否已获得token,在 获得token之后我们就要去获取用户的基本信息了

    页面会先从 cookie 中查看是否存有 token,没有,就走一遍上一部分的流程重新登录,如果有token, 就会把这个 token 返给后端去拉取user_info,保证用户信息是最新的。 当然如果是做了单点登录得功 能的话,用户信息存储在本地也是可以的。当你一台电脑登录时,另一台会被提下线,所以总会重新登 录获取最新的内容。

    先说一说我权限控制的主体思路,前端会有一份路由表,它表示了每一个路由可访问的权限。当用户登 录之后,通过 token 获取用户的 role ,动态根据用户的 role 算出其对应有权限的路由,再通过 router.addRoutes 动态挂载路由。但这些控制都只是页面级的,说白了前端再怎么做权限控制都不是 绝对安全的,后端的权限验证是逃不掉的。

    我司现在就是前端来控制页面级的权限,不同权限的用户显示不同的侧边栏和限制其所能进入的页面(也 做了少许按钮级别的权限控制),后端则会验证每一个涉及请求的操作,验证其是否有该操作的权限,每 一个后台的请求不管是 get 还是 post 都会让前端在请求 header 里面携带用户的 token,后端会根据 该 token 来验证用户是否有权限执行该操作。若没有权限则抛出一个对应的状态码,前端检测到该状 态码,做出相对应的操作。

    使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件。

    具体实现:

    创建vue实例的时候将vue-router挂载,但这个时候vue-router挂载一些登录或者不用权限的公用的页 面。

    当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路 由表。

    调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。

    使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件。

平时都是用那些工具进行打包的?babel是什么?

WebPack 是一个模块打包工具,你可以使用WebPack管理你的模块依赖,并编绎输出模块们所需的静 态文件。它能够很好地管理、打包Web开发中所用到的HTML、Javascript、CSS以及各种静态文件(图 片、字体等),让开发过程更加高效。对于不同类型的资源,webpack有对应的模块加载器。webpack 模块打包器会分析模块间的依赖关系,最后 生成了优化且合并后的静态资源

babel可以帮助我们转换一些当前浏览器不支持的语法,它会把这些语法转换为低版本的语法以便浏览 器识别。