DNS域名解析过程(比如要查www.baidu.com的ip地址)
- 1.首先会在浏览器的缓存中查找对应的IP地址,如果查找到直接返回,若找不到继续下一步
- 2.将请求发送给本地DNS服务器,在本地域名服务器缓存中查询,如果查找到,就直接将查找结果返回,若找不到继续下一步
- 3.本地DNS服务器向根域名服务器发送请求,根域名服务器会返回一个所查询域的顶级域名服务器地址(返回负责.com的顶级域名服务器的ip地址的列表)
- 4.本地DNS服务器向顶级域名服务器发送请求,接受请求的服务器查询自己的缓存,如果有记录,就返回查询结果,如果没有就返回相关的下一级的权威域名服务器的地址(返回负责.baidu的权威域名服务器的ip地址列表)
- 5.本地DNS服务器向权威域名服务器发送请求,域名服务器返回对应的结果
- 6.本地DNS服务器将返回结果保存在缓存中,便于下次使用
首先浏览器会先后在本地缓存查找,未找到就去本地DNS服务器缓存里递归查找,如果没查到,就依次从根域名服务器、顶级域名服务器、权威域名服务器依次迭代查找相应的结果,并将结果缓存。(递归查询对于被查询的域名服务器负担太大,因此采用迭代查询)
TCP三次握手1
发送端首先会发送一个带SYN标志的数据包给服务端,服务端回传一个带有SYN/ACK标志的数据包以示确认信息。最后,发送端再回传一个带ACK标志的数据包,代表握手结束。
- 1.一开始,客户端和服务端都处于CLOSED状态。先是服务端主动监听某个端口,处于LISTEN状态。
- 2.客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1 ,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。
业务
设计模式——发布订阅模式
Event = (function(){ const list = {} const listen = function(key,fn){ if(!list[key]){ list[key] = [] } list[key].push(fn) } const trigger = function(){ const key = Array.prototype.shfit.call(arguments) const fns = list[key]; if(!fns || fns.length === 0) return false for(let i=0,fn;fn=fns[i++];){ fn.apply(this,arguments); } } })() Event.listen('onBtnClick', function(state){ console.log('clicked', state) }) Event.trigger('onBtnClick', 'user') 复制代码
设计模式——模板方法模式:基于继承的设计模式
const GuideUi = function(param){ const findDom = function(){ } const scrollDomToVisible = function(){ } const F = function(){} F.prototype.init=function(){ findDom(); scrollDomToVisible() } return F } const BreathBase = GuideUi({ findDom = function(){ } }) const breath = new BreathBase() breath.init(); 复制代码
设计模式-装饰模式
React中的高阶组件使用了装饰模式,装饰模式的特点是不需要改变被装饰对象本身,而只是在外面套一个外壳接口。
数据类型
- ...
- Set(集合,成员之间不重复)
- Map(可以用任意数据类型作为键)
- Symbol(唯一值)
- BigInt(存储和操作大的数值,即使超出了Number能够表示的安全范围)
0.1+0.2!==0.3
原理:计算机中以二进制方式存储数据,而对于无限循环的二进制数,遵循IEEE 754标准,其存储空间是有限的,如果超出就会舍弃后面的数字。
解决方法:ES6中提出了Number.EPSILON属性,作为误差值大小,当两数之后小于此误差值,就可以任务这两个数相等。
function equal(a,b){ return Math.abs(a,b) < Number.EPSILON } equal(0.1+0.2,0.3) 复制代码
精度丢失-BigInt提案
js中,Number.MAX_SAFE_INTEGER表示最⼤安全数字,在此范围之内,不会出现精度丢失,超出就会出现计算不准确的情况,因此有了BigInt的提案。
isNaN与Number.isNaN
NaN !== NaN // true Object.is(NaN, NaN); // true 复制代码
- isNaN,会进行数据类型转换,如果能转换成数值,就返回为false。
- Number.isNaN,不会进行转换,先判断是否是number类型,如果不是number类型就返回false,是number再调用isNaN方法。
Number.isNaN = Number.isNaN || function(value) { return typeof value === "number" && isNaN(value); } 复制代码
箭头函数与new操作符
new操作符的实现步骤
- 1.创建一个新的空对象
- 2.将构造函数的作用域赋给新对象(即将对象的__proto__属性指向构造函数的prototype属性)
- 3.让构造函数中的this指向这个对象,并执行构造函数的代码(为新对象添加属性)
- 4.如果构造函数的返回值是值类型,就返回创建的对象,如果是引用类型,就返回这个引用类型的对象。
能否new一个箭头函数
箭头函数相比于普通函数:
- 箭头函数没有自己的this,且其继承来的this指向不会改变,this指向外层的执行环境
- call、apply、bind方法不能改变箭头函数this指向
- 箭头函数没有arguments、prototype,因此不能作为构造函数,因此不能new一个箭头函数
- 箭头函数不能用做generator函数
箭头函数不会改变this指向
const person = { name: "tom", sayHi: function () { console.log(`hi,my name is ${this.name}`); }, sayHello: () => { console.log(`hello,my name is ${this.name}`); }, sayHiAsync: function () { setTimeout(function () { console.log(`hi,my name is ${this.name}`); }, 1000); }, sayHiAsync2: function () { const _this = this; setTimeout(function () { console.log(`hi,my name is ${_this.name}`); }, 1000); }, sayHiAsyncWithArrow: function () { setTimeout(() => { console.log(`hi,my name is ${this.name}`); }, 1000); }, }; person.sayHi(); // 正常打印出name person.sayHello(); // 不能正常打印出name,显示为undefined person.sayHiAsync(); // 也不能正常打印出name,因为setTimeout会被放到全局作用域被调用,因此是拿不到当前作用域里的this person.sayHiAsync2(); // 能正常打印出name,基于闭包保存当前作用域的this person.sayHiAsyncWithArrow(); // 能正常打印出name,箭头函数不改变this 复制代码
Object.defineProperty与Proxy
在Vue3.0之前使用Object.defineProperty方式实现数据响应,完成双向数据绑定,而在Vue3.0开始使用proxy实现。
Object.defineProxy与Proxy都可以用来监视对象中的属性读写过程。Proxy中的第二个参数提供了get与set方法,分别用来监视属性的访问与设置过程。
Proxy比Object.defineProxy强大:
- 非侵入式去监听对象属性
- Object.defineProxy只能监视属性的读写,Proxy能监视更多的操作,比如delete
for...of、Iterable(可迭代)接口、迭代器模式、生成器
for...of与for...in
- for...in主要是为了遍历对象,for...of可以遍历数组、类数组对象、set、map等
- for...in会遍历原型链,for...of不会
- for...in获取的是对象的键名,for...of获取的是对象的键值 for...of相对于for、each等遍历方式,可以用break终止循环。
ES2015中提供了一种统一的遍历方式,即iterable接口,实现iterable接口是for...of的前提。即能够被for...of遍历的数据类型在内部都实现了interable接口。比如数组类可以用for...of遍历,普通对象就不能用for...of进行遍历。
迭代器模式的优势:对外提供统一的遍历接口。
生成器函数会自动返回一个生成器对象,调用这个对象的next方法才会让函数执行,执行过程中如果遇到了yield关键词,函数就会暂停执行,且yield后面的值会作为next的结果返回。生成器的最大特点就是惰性执行。
生成器应用:
- 生成自增的id
- 使用generator函数实现iterator方法
闭包与 V8 引擎与垃圾回收机制
react
性能优化
防抖与节流
- 防抖:事件触发n秒之后再执行回调,如果在此时间内又重新触发了,就重新计时。比如:表单提交(多次点击,只执行最后提交的一次)、搜索
- 非立即执行:触发事件后,不会立即执行,而是等n秒之后执行,如果在这n秒之后再次触发了事件就重新计时
- 立即执行:触发事件后立即执行,然后等到停止触发n秒之后,才可以重新触发执行
- 节流:规定时间内多次触发事件,只有一次生效。比如:scroll、拖拽、resize
- 非立即执行(定时器版):在持续触发事件的过程中,函数不会立即执行,并且每n秒执行一次,在停止触发事件后,函数还会再执行一次(在n秒之后第一次执行,且在事件停止触发后依然会再执行一次事件,无头有尾)
- 立即执行(时间戳版):在持续触发事件的过程中,函数会立即执行,并且在n秒之内执行一次(立即执行,有头无尾)
- 有头有尾版:事件触发能立即执行,停止触发时还能再执行一次
// 防抖:immediate参数表示非立即执行还是立即执行 function debounce(fn, wait, immediate){ let timer = null return function(){ let _this = this; let args = arguments; if(timer) clearTimeout(timer); if(immediate){ let callNow = !timer; // 如果已经执行过就不再执行 timer=setTimeout(()=>{ timer = null; },wait) if(callNow) fn.apply(this,args); }else{ timer = setTimeout(function(){ fn.apply(this,args) }, wait) } } } // 节流-定时器版:在持续触发事件的过程中,函数不会立即执行,并且每n秒执行一次,在停止触发事件后,函数还会再执行一次 function throttle(fn, wait){ const timer = null return function(){ let _this = this; if(!timer){ timer = setTimeout(()=>{ fn.apply(_this,[...arguments]) timer = null }, wait) } } } // 节流-时间戳版:在持续触发事件的过程中,函数会立即执行,并且在n秒之内执行一次 function throttle(fn,time){ let prev = 0; return function(){ let now = Data.now(); let _this = this; let args = arguments; if(now-prev>time){ prev = now; fn.apply(_this,args) } } } 复制代码
回流(重排)与重绘
触发回流时一定会触发重绘,重绘不一定引起回流。
- 重绘:当页面元素样式发生变化,但是不会影响其在文档流中的位置时,浏览器会对元素重新绘制。
- 回流:改变了元素的几何属性
渲染篇五:CSS的回流(Reflow)与重绘(Repaint)
容易忽视:当调用getCompotedStyle、currentStyle方法,或使用以下属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight,会发生回流,原因是浏览器为了“即时性”与“准确性”。
如何减少回流与重绘的影响:
- 缓存会引起回流的属性值
- Dom“离线”,即将元素设置为“dispaly:none”,适用于需要频繁更改元素几何样式时的情况
- 避免逐条改变样式,使用类名去合并样式
浏览器渲染原理
通常来说,渲染引擎会解析HTML文档来构建dom树,也会用CSS解析器解析CSS文档构建CSSOM树。DOM树与CSSOM树关联起来构成渲染树(RenderTree),这一过程称为Attachment,然后浏览器根据渲染树进行布局(Layout),最后一步通过绘制显示出整个页面。
当RenderTree中的一部分(或全部)因为元素的规模尺寸、布局、隐藏等改变而需要重新绘制这称为回流。
其他
this指向
为什么需要在 React 类组件中为事件处理程序绑定 this
在浏览器的多个 tab 页中共享 sessionStorage
实现多个标签页之间通信的几种方法(sharedworker)
react中的受控组件和非受控组件
- 针对输入类的dom元素,如果是通过setState将输入的值维护到state中,dom节点状态值就收到了state的控制,这种称为受控组件(缺点:如果想要同时获取到多个输入框全部的值就必须每个都编写事件处理函数)
- 针对输入类的dom元素,如果其状态值是直接存储在dom节点中这种就是非受控组件,这种可以用ref从dom节点中获取表单数据
DOMContentLoaded 事件与window.onload事件
当后端一次性丢给你10万条数据, 作为前端工程师的你,要怎么处理?
柯里化
// sum(1, 2)(3).sumOf(); //6 // sum(1)(2)(3).sumOf(); //6 // sum(1, 2, 3).sumOf(); //6 function sum() { const args = Array.prototype.slice.call(arguments); const add = function () { args.push(...arguments); return add; }; add.sumOf = () => args.reduce((a, b) => a + b); return add; } 复制代码
问到的:
- 委托机制的好处
- 对于对象的理解以及设计模式
- 伪类和伪元素的区别,伪元素可以绑定事件吗
- 如何写一个高性能的组件
- 去重的方法?如果数组里的元素里有个对象,能去重吗?
- class里有私有属性吗
- 链式调用是如何实现的
- 当前处于登陆状态,如果禁用cookie此时还处于登陆状态吗?cookie里的哪个属性设置能够让浏览器读取不到cookie 一文带你看懂cookie,面试前端不用愁
- 跨域请求的时候,会有个域检请求?
- find、apply、call的区别 彻底弄懂bind,apply,call三者的区别
- find、some、every的区别
- 如果需要请求1w条数据,有什么方法能够提高用户体验?懒加载?分页?pwa?
- 实现一个函数,输入一个数字,会每隔1s输出自减1之后的数字。timer->promise,不同实现方式
- router
- 对象遍历方法、速度、原型链
- 如何仅修改页面的 URL,而不发送请求? 深入理解前端中的 hash 和 history 路由
前端路由两种模式:hash与history