前端基础内功修炼之this、浏览器原理、地址栏中输入url到底发生了什么

450 阅读8分钟

js 上下文环境 -> this

this指向的问题

1.1 当函数作为构造函数,通过new调用时,this指向生成的实例

function Animal(name, color) {
    this.name = name
    this.color = color
}
const cat = new Animal('小黑', 'black')
console.log(cat.name)

1.2 当函数直接被调用的时候,this指向window对象,或者严格模式下this指向undefined

function Animal(name, color) {
    this.name = name
    this.color = color
}
Animal('喵喵', 'white')
console.log(window.name)  //喵喵

1.3 当函数被调用的时候,他是作为某个对象的方法 this指向这个对象 (谁调用它就指向谁)

//例一
let obj = {
   x: 10,
   fn: function(){
       console.log(this.x)
   }
}
let a = obj.fn
a() // undefined
obj.fn() // 10
// 例二
let obj = {
    x: 10,
    fn: function(){
        return function(){
            console.log(this.x)
        }
    }
}
obj.fn()() // undefined

实现一个call方法

// call会改变this指向 并且可以传入多个参数 会立即执行
function Person(name, age) {
    this.name = name
    this.age = age
}
Function.prototype.mCall = function(context, ...args){
   console.log(this === Person) // true
   // 解决变量名可能重复的问题
   let mySymbol = Symbol()
   context[mySymbol] = this
   // 解决call传多个参数的问题
   context[mySymbol](...args)
}
let chinese = {}
Person.mCall(chinese, 'phil', 26)

实现一个apply方法

// apply也会改变this指向 与call不同的是 apply参数是传入一个数组
function Person(name, age) {
    this.name = name
    this.age = age
}
Function.prototype.mApply = function(context, args){
   console.log(this === Person) // true
   // 解决变量名可能重复的问题
   let mySymbol = Symbol()
   context[mySymbol] = this
   // 解决call传多个参数的问题
   context[mySymbol](...args)
}
let chinese = {}
Person.mApply(chinese, ['phil', 26])

实现一个bind方法

用法: bind不会立即调用函数,只做this的绑定,并且返回一个新的函数,这个函数的逻辑与原函数一致,但是this会绑定之前绑定的对象

function Person(name, age) {
    this.name = name
    this.age = age
}
Function.prototype.mBind = function(context, ...arg1){
    let originFn = this
    return function fBind(){
        originFn.call(context, ...arg1, ...arguments)
    }
}
let chinese = {}
let p = Person.mBind(chinese, 'phil', 25)
p(26)

浏览器原理

浏览器组成结构

1) 用户界面(User Interface) 包括地址栏、前进/后退、菜单等等
2) 浏览器引擎(Browser engine) 在用户界面和渲染引擎之间传送指令
3) 渲染引擎(Rendering engine) 负责显示请求的内容。如果请求的内容是HTML,它就负责解析HTML和
CSS内容,将其显示到屏幕上。
4) 网络(Networking) 调试网络接口
5) 用户界面后端(UI Backend) 
6) JavaScript解释器 用于解释和执行JavaScript代码,如:V8
7) 数据存储( Data Persistence ) 比如cookie 或者 localStorage

浏览器是多进程的

1) Browser进程:浏览器的主进程,负责协调、主控,只有一个。
2) GPU进程:最多一个,用于3D绘制
3) 渲染进程:render进程 内部是多线程的 !important
    每个tab默认是一个进程,互不影响
    主要作用为页面渲染,脚本执行
4) 异步http请求线程
    检测到XHR对象状态变化时,将回调函数放入事件队列(event loop)

渲染进程是多线程的

 此处重点:渲染线程与JS引擎线程互斥!由于JavaScript是可操作DOM的,如果在修改这些元素属性的同
 时渲染界面,那么渲染线程和前后获得的数据元素就可能不一致了。
 1) GUI渲染线程
     负责浏览器界面,解析HTMLCSS,构建DOM树和Render树,当界面需要重绘或者由于某种操作引发
     回流的时候,改线程就会执行
 2) js引擎线程(JS内核,负责处理JavaScript脚本,如V8)
     JS引擎会等待任务队列中的数据然后进行处理,一个tab中只会有一个js线程在运行。因为js引擎和
     GUI渲染引擎是互斥执行的,所以js如果执行时间过长,会导致页面渲染阻塞。
 3) 事件触发线程 管理事件队列
 4) 定时器线程
     setInterval或者setTimeout在此线程中及时完毕之后,会把回调函数放入任务队列中。

浏览器重排(reflow)和重绘(repaint)

重排:是指重新计算页面布局,某个节点reflow时会重新计算节点的尺寸和位置,而且可能触发子节点,
或者祖先节点的重排.简单点说 元素的尺寸、位置变了会引起重排
    例如: text-alignoverflowfont-size and so on
重绘:遍历所有的节点检测各节点的可见性、颜色、轮廓等可见的样式属性,然后根据检测页面。简单点
说颜色、背景、边框变了会引起重绘。
    例如:colorvisibilitytext-decoration and so on

性能优化原则

1) 尽量减少HTTP请求
2) 使用内容传送网络CDN
3) 避免空src或者href值:会给服务器造成意外的流量负担,浪费服务器的计算资源
4) AJAX优化:合理使用get和post请求
post请求是不可以在客户端缓存的,每次请求都需要发送给服务器进行处理,每次都会返回状态码200
get请求是默认在客户端进行缓存的,除非指定了不同的地址,否则ajax不会重复发送请求,而是返回304
5) 合理使用缓存
6) 多分支判断的时候尽量使用switch而非if...else... switch只会进行一次匹配,而if...else...会
挨个比较。
这里安利两个优化工具:JsPref、pageSpeed

从输入url到页面展示的流程?

// DNS域名解析 --> 建立TCP连接 --> 发起HTTP请求 --> 接收响应结果 --> 浏览器解析HTML --> 浏览器布局渲染

DNS原理

DNS: 用来返回某个域名对应主机的ip的服务器
eg: www.shboka.com.
1.1 根DNS(.)
最后的这个'.' 只负责提供各类顶级DNS服务器的ip地址,是域名解析的入口。全球仅有13台域名根服务器,
一个主根域名服务器,其余12为辅根域名服务器。
1.2 顶级DNS
例子中的com就是顶级域名。负责提供二级域名的DNS服务器IP地址。每个顶级域名都有对应的DNS的服务器,通常由专门的机构来维护。
1.3 权威DNS
提供三级域名对应的主机IP地址,由域名购买者提供,对应上面的shboka。
获取域名对应服务器IP地址的流程如下:
1) 浏览器缓存:当用户通过浏览器访问某个域名的时候,浏览器首先会在自己的缓存中查找是否有该域名对应的IP地址
2) 系统Hosts: 当浏览器缓存中无域名对应IP,则先会自动检查用户计算机中的hosts文件是否有该域名对应的IP。
3) 本地域名服务器:当用户在hosts中也没有找到域名对应的IP地址的时候,则将进入本地DNS缓存中查询。
通常这个本地域名DNS会配置成运营商ISP指定的DNS。
4) 根域名服务器: 以上行为均没有找到对应IP的时候,则会由本地DNS开始进行迭代查询:
    向根域名服务器查询,得到顶级域名服务器的IP地址
    向顶级域名服务器查询,得到权威域名服务器的IP地址
    向权威域名服务器查询,得到域名的IP
5) 保存结果至本地缓存: 本地域名服务器会把返回的结果缓存到本地,以备下一次使用。 

TCP三次握手与四次挥手

三次握手:客户端发送syn报文,并将发送的报文号置为x,服务端接收到报文号为x的报文以后,响应请求,将
确认序列号置为x + 1之后,至此客户端接收到链接确认报文,客户端与服务端建立链接。提问:为什么TCP建
立链接不是两次握手,而是三次握手?这是因为当客户端链接建立的请求发送的很慢的时候,服务端过了很长
一段时间才接收到序号为x的请求报文,如果只有两次握手,那么服务端会直接响应这段请求报文,直接建
立链接,会导致浪费资源(有可能这段客户端已经放弃了这个请求)。

四次挥手:当有一方释放请求的时候,主动方会发送释放请求报文,序号为x,被动方接收到之后,会将确认序
号置为x+1,然后这段时候,被动方会确认是否还有报文需要发送,若没有再发送释放请求确认报文,主动方接
收到之后释放请求,四次挥手过程完毕。

浏览器渲染页面

1) 从HTML解析出DOM Tree( DOM 树 )
2) 从CSS解析出CSSOM Tree( CSS规则树 )
3) JavaScript代码有JavaScript引擎处理
4) DOM树建立后根据CSS样式进行构建内部绘图模型,生成渲染树
5) 根据网页层次结构构建渲染树(重排)
6) 重绘
  css的下载过程不会阻塞解析,但是js会等待其下载并执行完成后才会继续解析。
  重排:导致浏览器不得不重新计算元素的几何属性,并重新构建渲染树的过程
  重绘:完成重排以后,要将重新构建的渲染树渲染到屏幕上的过程
  summary: 重排必然带来重绘,重绘未必会带来重排。