个人介绍
【ok】js 基本数据类型
- 7 种原始类型
- Boolean、Number、String、Null、Undefined、Symbol(es6)、BigInt、
- Object
Symbol的理解
symbol是在es6中引入的基本数据类型、它表示的是一个独一无二的值,主要是来解决命名冲突的问题,它的使用场景基本都是在对象里去定义一个唯一的属性名、然后还可以去用它定义一些常量
// 用法
let sy = Symbol("key1");
syObject[sy] = "kk";
console.log(syObject); // {Symbol(key1): "kk"}
let a = Symbol("acc");
a.description // "acc"
// 它作为属性的时候不会出现在for in、for of、Object.keys()、如果要读取一个对象的symbol属性,可以用object.getOwnPropertySymbal()
Object.getOwnPropertySymbols(syObject); // [Symbol(key1)]
// symbol.for() 在全局中搜索是否有该字符串参数传入的symbol值,如果有就取该值,没有就在全局中新登记一个、keyfor是返回它的传参
BigInt的理解
bigint就是会比number类型有更大的整数范围、
number的范围(-(2^53-1))
到(2^53-1),超出的话会失去精度
// 使用方法
const a = 111n // +n
bigInt('123') // 构造函数
能用typeof检验数据类型:undefined,string,boolean,number,symbol(独一无二的),Object,Function,null, array
undefined派生自null,null == undefined //true
【ok】几种判断数据类型的优缺点
typeof
console.log(typeof 1) // number
console.log(typeof true) // boolean
console.log(typeof 'mc') // string
console.log(typeof function(){}) // function
console.log(typeof []) // object
console.log(typeof {}) // object
console.log(typeof null) // object
console.log(typeof undefined) // undefined
console.log(typeof Symbol()) // symbol
console.log(typeof 10n) // bigint
优点:能够快速区分基本数据类型
缺点:不能判断 object,array 和 null,都返回 object
instanceof
console.log(1 instanceof Number) // false
console.log(true instanceof Boolean) // false
console.log(10n instanceof BigInt) // false
console.log(Symbol() instanceof Symbol) // 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,适用于判断自定义的类实例对象
缺点:基本类型不能判断
Object.prototype.toString.call()
const { toString } = Object.prototype
console.log(toString.call(1)) // [object Number]
console.log(toString.call('str')) // [object String]
console.log(toString.call(true)) // [object Boolean]
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]
console.log(toString.call(Symbol())) // [object Symbol]
console.log(toString.call(10n)) // [object BigInt]
优点:精确判断数据类型
缺点:写法繁琐,需要封装
【ok】null 和 undefined 的区别
undefined 是访问一个未初始化的变量时返回的值,而 null 是访问一个尚未存在的对象时所返回的值
undefined 看作是空的变量,null 看作是空的对象
undefined是定义了变量没有使用;null是空值,占内存
【ok】对this的理解
我的理解是this是执行上下文的一个属性,它会指向一个作用域,它的绑定是在函数被调用的时候,它指向什么完全取决于在哪里被调用。
它会有四种绑定规则
默认绑定、隐式绑定、显式绑定、new绑定
默认绑定就是独立函数直接调用然后就会指向全局作用域、也是最频繁的
隐式绑定就是它调用的位置是否被包裹比如对象
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
也有可能发生隐式丢失的问题,就是我新起了一个变量去引用这个对象里的方法,这个时候执行这个变量的时候实际是在全局里直接执行、那么就会变成默认绑定指向全局作用域,它有一个挺常见的场景,就是setTimeout,它的第一个参数直接传入对象里的一个方法,它的作用域就直接会指向全局而不是那个对象
显式绑定就是用一些方法像call、apply直接进行绑定,像刚刚说的隐式丢失的问题就可以用显式绑定来解决,就是在中间加一层直接用call去调用方法并指定它的this指向,它有个中文名是叫硬绑定,在es5中就提供了bind方法就是这个硬绑定
bind和call、apply有个区别就是bind是等待执行,而call是立即执行的
最后还有个new绑定 ,就是使用new来调用函数,它的一个流程的话是这样的:
首先会创建一个全新的对象、然后这个对象会被执行原型的相关连接、再然后这个对象就会被绑定到函数调用的this、最后如果函数没有返回东西,那么就会返回这个对象
优先级的话是new、显式、隐式、默认
【ok】对call apply bind的理解
apply call
改变函数内部this的指向
区别就是接受的参数不同
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
es6的了解,有哪些新内容
- 新增let\const 声明方式
- 箭头函数
- 解构(数组解构和对象解构)
- Promise
- Class,继承的显示化
- 新增Symbol数据类型
- Set和Map集合
- 字符串模板
- Generator生成器
- 函数参数默认值
- 11.函数参数简写即字面量提升
【ok】cookie、session和localStorage的区别
- cookie:**服务端生成放在客户端存储,HTTP请求时会明文携带,即cookie在浏览器和服务器间来回传递,有长度限制,4k左右,**需手动清除,操作需开发人员手动封装。支持跨域名访问。若不设置时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就会消失。
- session:服务端存储,生命周期为一个会话,不支持跨域访问安全性高。像登录信息可以存放在这里,
- localStorage:是为了解决cookie的弊端生成的,长度限制为5M,具体根据浏览器不同,长期有效,需手动清除,有封装好的API使用,setItem,getItem等,它也是存在客户端存储,它的有效期在浏览器关闭后仍会存在,而sessionStorage关闭后就会消失
js捕获与冒泡的区别:
【ok】GET和POST区别
w3c给出的区别
分类 | GET | POST |
---|---|---|
后退按钮/刷新 | 无害 | 数据会被重新提交(浏览器应该告知用户数据会被重新提交 |
书签 | 可收藏为书签 | 不可收藏为书签 |
缓存 | 能被缓存 | 不能被缓存 |
编码类型 | application/x-www-form-urllencoded | application/x-www-form-urlencoded或multipart/from-data为二进制数据使用多重编码 |
历史 | 参数保留在浏览器历史中 | 不保存 |
对数据长度的显示 | GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符) | 无限制 |
对数据类型的限制 | 只允许ASCII字符 | 没有限制,允许二进制数据 |
安全性 | 与POST相比,GET的安全性较差,所发送的数据是URL的一部分 | POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。 |
可见性 | 数据在 URL 中对所有人都是可见的 | 数据不会显示在 URL 中 |
同: Get和Post只是HTTP协议中两种请求方式,而HTTP协议是基于TCP/IP的应用层协议,无论Get还是Post,用的都是同一个传输层协议,所以在传输上没有区别。
【ok】深浅拷贝
- 引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
浅拷贝:
1.Object.assign()
2.Array.prototype.concat()
3.Array.prototype.slice()
4…扩展
深拷贝:
1.JSON.parse(JSON.stringify()) //注意undefined、function、symbol不会去解析
2.手写递归
从深入到通俗:Object.prototype.toString.call()
function checkType(target) {
//[object Number]
return Object.prototype.toString.call(target).slice(8, -1);
}
function deepClone(source) {
if (['Object', 'Array'].includes(checkType(source))) {
let target = Array.isArray(source) ? [] : {};
for (let key in source) {
target[key] = deepClone(source[key]);
}
return target;
} else {
return source;
}
}
3.第三方库,lodash
【ok2】构造函数实例化成对象发生了什么
- 创建一个空对象
- 让空对象的 proto (IE 没有该属性) 成员指向构造函数的 prototype 成员对象
- 使用 apply 调用构造函数,属性和方法被添加到 this 引用的对象中
- 如果构造函数中没有返回其他对象,那么返回 this,即创建的这个新对象;否则,返回构造函数返回的对象
【ok2】WebSocket的连接方式和心跳检测
连接方式
const ws = new WebSocket(wsUrl)
ws.onopen = ()=>{}
ws.onmessage = ()=>{}
ws.onclose = ()=>{}
心跳检测
//心跳检测
var heartCheck = {
timeout: 3000, //每隔三秒发送心跳
num: 3, //3次心跳均未响应重连
timeoutObj: null,
serverTimeoutObj: null,
start: function(){
var _this = this;
var _num = this.num;
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
ws.send("123456789"); // 心跳包
_num--;
//计算答复的超时次数
if(_num === 0) {
ws.colse();
}
}, this.timeout)
}
}
ajax轮询 的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息
long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始
从上面很容易看出来,不管怎么样,上面这两种都是非常消耗资源的。
ajax轮询 需要服务器有很快的处理速度和资源。(速度)
long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)
【ok2】对象的定义、让一个对象不可改变
defineProperty:
var myObject = {};
Object.defineProperty( myObject, "a", {
value: 2,
writable: true,
configurable: true,
enumerable: true
} );
myObject.a; // 2
writable(可写)决定是否可以修改属性的值
enumerable(可枚举)
configurable(可配置)是否可以用defineProperty来配置,单向
Getter 和 Setter:
var myObject = {
// 给 a 定义一个 getter
get a() {
return this._a_;
},
// 给 a 定义一个 setter
set a(val) {
this._a_ = val * 2;
}
};
myObject.a = 2;
myObject.a; // 4
让对象不可变:
- 对象常量
结合writable: false和configurable: false就可以创建一个真正的常量属性(不可修改、重定义或者删除) - 禁止扩展
如果想要禁止一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions(…). - 密封
Object.seal(…)会创建一个“密封”的对象,实际上是会在一个现有对象上调用Object.preventExtensions(…)并把所有现有属性标记为configurable: false。
所以,密封后不仅不能添加新属性,也不能重新配置或者删除任何现有属性(但是可以修改属性的值)。
- 冻结
Object.freeze(…)会创建一个冻结对象,实际上会在一个现有对象上调用Object.seal(…)并把所有“数据访问”属性标记为writable: false,这样就无法修改它们的值了。
最后,注意一点,所有方法创建的都是浅不变性。就是说,它们只会影响目标对象和它的直接属性,如果目标对象引用了其他对象(数组、对象、函数等),其他对象的内容不受影响,仍然可变。
变量引用不可变const
【ok2】防抖与节流
防抖
动作停止后的时间超过设定的时间时执行一次函数。
注意:这里的动作停止表示你停止触发函数,从这个时间点开始计算,当间隔时间等于你设定的时间,才会执行里面的回调函数
防抖的使用场景
函数防抖一般用在什么情况之下呢?一般用在,连续的事件只需触发一次回调的场合。具体有:
- 搜索框搜索输入。只需用户最后一次输入完,再发送请求;
- 用户名、手机号、邮箱输入验证;
- 浏览器窗口大小改变后,只需窗口调整完后,再执行 resize 事件中的代码,防止重复渲染。
- 在前端中有一些事件会频繁的触发容易造成页面卡顿
function debounce(fn: () => void, delay: number) {
let timer: number | null = null
return function() {
if(timer) window.clearTimeout(timer)
timer = window.setTimeout(function() {
fn()
}, delay)
}
}
window.addEventListener('scroll', debounce(() => {
console.log('scroll')
}, 1000))
节流
一定时间内执行的操作只会执行一次,也就是预先设定一个执行周期,当调用动作的即刻大于等于执行周期时执行该动作,然后进入下一个周期
如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效。
function throttle(fn: () => void, delay: number) {
let flag = true
return function() {
if(!flag) return
flag = false
setTimeout(function() {
fn()
flag = true
}, delay)
}
}
【ok2】预编译
https://blog.csdn.net/Bombas/article/details/80827756
【ok2】一个页面从输入URL到加载显示完成,发生了什么
问题:从输入一个url发生了什么?可以讲缓存,dns,cdn加速。重排和重绘,url,三次握手,怎么加快一个文件加载速度,网络层面的优化,get/post的区别,跨域的解决方案。怎么加快dns加速,一次加载100张图片的优化方法。http协议。
基本流程
- 首先在浏览器的地址栏里输入url并按下回车访问
- 浏览器查找当前url的DNS缓存记录,如果没有就执行一个DNS查询并进行对URL的解析获取到IP
- 根据IP建立TCP连接(三次握手)
- HTTP发起请求
- 服务器处理请求,浏览器接收HTTP响应(获取静态资源文件)
- 对资源进行语法解析,渲染页面,构建DOM树
- 关闭TCP链接(四次挥手)
一、URL、同源策略、跨域
http://www.baidu.com
域名由三部分组成:协议名、域名、端口号
最常见的HTTP协议,还有HTTPS协议、FTP协议、FILE协议等
HTTP默认的端口是80、HTTPS默认的端口是443
同源策略:同源策略是一个重要的安全策略,不同源的客户端脚本在没有明确的授权情况下,不能读写对方的资源
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html | 同源 | 只有路径不同 |
https://store.company.com/secure.html | 失败 | 协议不同 |
http://store.company.com:81/dir/etc.html | 失败 | 端口不同 ( http:// 默认端口是80) |
http://news.company.com/dir/other.html | 失败 | 主机不同 |
跨域:
**1.JSONP跨域:**jsonp的原理就是利用<script>
标签没有跨域限制,通过<script>
标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。
2.跨域资源共享(CORS):CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
**3.nginx代理跨域:**nginx代理跨域,实质和CORS跨域原理一样,通过配置文件设置请求响应头Access-Control-Allow-Origin…等字段。
**4.nodejs中间件代理跨域:**node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。
**5.document.domain+iframe跨域:**此方案仅限主域相同,子域不同的跨域应用场景。实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
6.location.hash+ifame跨域: a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。
**7.window.name+iframe跨域:**window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
**8.postMessage跨域:**postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:
页面和其打开的新窗口的数据传递
多窗口之间消息传递
页面与嵌套的iframe消息传递
上面三个场景的跨域数据传递
**9.WebSocket协议跨域:**WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
小结:
以上就是9种常见的跨域解决方案,jsonp(只支持get请求,支持老的IE浏览器)适合加载不同域名的js、css,img等静态资源;CORS(支持所有类型的HTTP请求,但浏览器IE10以下不支持)适合做ajax各种跨域请求;Nginx代理跨域和nodejs中间件跨域原理都相似,都是搭建一个服务器,直接在服务器端请求HTTP接口,这适合前后端分离的前端项目调后端接口。document.domain+iframe适合主域名相同,子域名不同的跨域请求。postMessage、websocket都是HTML5新特性,兼容性不是很好,只适用于主流浏览器和IE10+。
二、DNS域名解析
地址栏输入的域名不是资源所在的真实位置,域名只是IP地址的一个映射。域名解析就是将域名还原成IP地址的过程
首先浏览器先检查本地hosts文件是否有这个网址映射关系,如果有就调用这个IP地址映射,完成域名解析。
如果没找到则会查找本地DNS解析器缓存,如果查找到则返回。
如果还是没有找到则会查找本地DNS服务器,如果查找到则返回。
最后迭代查询,按根域服务器 ->顶级域,.cn->第二层域,hb.cn ->子域,www.hb.cn的顺序找到IP地址。
三、TCP连接
在通过第一步的DNS域名解析后,获取到了服务器的IP地址,在获取到IP地址后,便会开始建立一次连接,这是由TCP协议完成的,主要通过三次握手进行连接。
简洁版
- 客户端:你是XXX服务端吗?
- 服务端: 我是XXX服务端,你是客户端吗?
- 客服端: 是的,我是客户端
- 建立连接成功后,接下来就可以进行正式的传输数据。
详细版
第一次握手: 建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;
第二次握手: 服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手: 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。
四、浏览器向服务器发送HTTP请求
完整的HTTP请求包含请求起始行、请求头部、请求主体三部分。
五、缓存
我们说说浏览器缓存,HTTP缓存有多种规则,根据是否需要重新向服务器发起请求来分类,我将其分为强制缓存,对比缓存。
强制缓存判断HTTP首部字段:cache-control,Expires。
Expires是一个绝对时间,即服务器时间。浏览器检查当前时间,如果还没到失效时间就直接使用缓存文件。但是该方法存在一个问题:服务器时间与客户端时间可能不一致。因此该字段已经很少使用。
cache-control中的max-age保存一个相对时间。例如Cache-Control: max-age = 484200,表示浏览器收到文件后,缓存在484200s内均有效。 如果同时存在cache-control和 Expires,浏览器总是优先使用cache-control。
对比缓存通过HTTP的last-modified,Etag字段进行判断。
last-modified是第一次请求资源时,服务器返回的字段,表示最后一次更新的时间。下一次浏览器请求资源时就发送if-modified-since字段。服务器用本地Last-modified时间与if-modified-since时间比较,如果不一致则认为缓存已过期并返回新资源给浏览器;如果时间一致则发送304状态码,让浏览器继续使用缓存。
Etag:资源的实体标识(哈希字符串),当资源内容更新时,Etag会改变。服务器会判断Etag是否发生变化,如果变化则返回新资源,否则返回304。
- 浏览器先根据这个资源的http头信息来判断是否命中强缓存。如果命中则直接加在缓存中的资源,并不会将请求发送到服务器。
- 如果未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效。若可以使用,则服务器并不会返回资源信息,浏览器继续从缓存加载资源。
- 如果未命中对比缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。
既生Last-Modified何生Etag?
你可能会觉得使用Last-Modified已经足以让浏览器知道本地的缓存副本是否足够新,为什么还需要Etag(实体标识)呢?HTTP1.1中Etag的出现主要是为了解决几个Last-Modified比较难解决的问题:
- Last-Modified标注的最后修改只能精确到秒级,如果某些文件在1秒钟以内,被修改多次的话,它将不能准确标注文件的修改时间
- 如果某些文件会被定期生成,当有时内容并没有任何变化,但Last-Modified却改变了,导致文件没法使用缓存
- 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形
Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回304。
F5刷新对强缓存无效,对比缓存有效
六、浏览器接收响应
服务器在收到浏览器发送的HTTP请求之后,会将收到的HTTP报文封装成HTTP的Request对象,并通过不同的Web服务器进行处理,处理完的结果以HTTP的Response对象返回,主要包括状态码,响应头,响应报文三个部分。
状态码主要包括以下部分
1xx:指示信息–表示请求已接收,继续处理。
2xx:成功–表示请求已被成功接收、理解、接受。
3xx:重定向–要完成请求必须进行更进一步的操作。
4xx:客户端错误–请求有语法错误或请求无法实现。
5xx:服务器端错误–服务器未能实现合法的请求。
响应头主要由Cache-Control、 Connection、Date、Pragma等组成。
响应体为服务器返回给浏览器的信息,主要由HTML,css,js,图片文件组成。
七、页面渲染
整个过程涉及两个方面:解析和渲染。在渲染页面之前,需要构建DOM树和CSSOM树。
在浏览器还没接收到完整的 HTML
文件时,它就开始渲染页面了,在遇到外部链入的脚本标签或样式标签或图片时,会再次发送 HTTP
请求重复上述的步骤。在收到 CSS
文件后会对已经渲染的页面重新渲染,加入它们应有的样式,图片文件加载完立刻显示在相应位置。在这一过程中可能会触发页面的重绘或回流。这里就涉及了两个重要概念:Reflow和Repaint。
Reflow,也称作Layout,中文叫回流,一般意味着元素的内容、结构、位置或尺寸发生了变化,需要重新计算样式和渲染树,这个过程称为Reflow。
Repaint,中文重绘,意味着元素发生的改变只是影响了元素的一些外观之类的时候(例如,背景色,边框颜色,文字颜色等),此时只需要应用新样式绘制这个元素就OK了,这个过程称为Repaint。
所以说Reflow的成本比Repaint的成本高得多的多。DOM树里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。
八、关闭TCP或继续保持连接
通过四次挥手关闭连接(FIN ACK, ACK, FIN ACK, ACK)。
简洁版:
- 主动方:我已经关闭了向你那边的信息发送通道,只能被动接受信息了;
- 被动方: 收到通道关闭的信息;
- 被动方: 我现在也关闭了向你那边发送信息的通道
- 主动方: 左后收到信息,连接断开,之后双方无法通信
详细版:
第一次挥手是浏览器发完数据后,发送FIN请求断开连接。
第二次挥手是服务器发送ACK表示同意,如果在这一次服务器也发送FIN请求断开连接似乎也没有不妥,但考虑到服务器可能还有数据要发送,所以服务器发送FIN应该放在第三次挥手中。
这样浏览器需要返回ACK表示同意,也就是第四次挥手。
【ok2】浏览器中的事件循环(宏微任务)
JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序以外,还依靠任务队列(task queue)来搞定另外一些代码的执行。整个执行过程,我们称为事件循环过程。一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。任务队列又分为 macro-task(宏任务)和 micro-task(微任务),在最新标准中,他们分别被称为 tasks 和 jobs。
macro-task(宏任务) 大概包括:
- script(整体代码)
- setTimeout
- setInterval
- setImmediate
- I / O
- UI render
micro-task(微任务) 大概包括:
- process.nextTick
- Promise.then
- async / await (等价于 Promise.then)
- MutationObserver(HTML5 新特性)
总体结论就是:
- 执行宏任务
- 然后执行宏任务产生的微任务
- 若微任务在执行过程中产生了新的微任务,则继续执行微任务
- 微任务执行完毕,再回到宏任务中进行下一轮循环
【ok2】SetTimeout 倒计时为什么有误差
上面讲了定时器是属于 宏任务(macrotask) 。如果当前 执行栈 所花费的时间大于 定时器 时间,那么定时器的回调在 宏任务(macrotask) 里,来不及去调用,所有这个时间会有误差。
setTimeout(function () {
console.log('biubiu');
}, 1000);
/某个执行时间很长的函数();
如果定时器下面的函数执行要 5秒钟,那么定时器里的log 则需要 5秒之后再执行,函数占用了当前 执行栈 ,要等执行栈执行完毕后再去读取 微任务(microtask),等 微任务(microtask) 完成,这个时候才会去读取 宏任务(macrotask) 里面的 setTimeout 回调函数执行。setInterval 同理,例如每3秒放入宏任务,也要等到执行栈的完成。
【ok2】mouseover和mouseenter的区别
-
不论鼠标指针穿过被选元素或其子元素,都会触发 mouseover 事件。对应mouseout
拥有冒泡特性
-
只有在鼠标指针穿过被选元素时,才会触发 mouseenter 事件。对应mouseleave
【ok2】对事件委托的理解
和原生HTML定义事件的唯一区别就是JSX采用驼峰写法来描述事件名称,大括号中仍然是标准的JavaScript表达式,返回一个事件处理函数。在JSX中你不需要关心什么时机去移除事件绑定,因为React会在对应的真实DOM节点移除时就自动解除了事件绑定。
React并不会真正的绑定事件到每一个具体的元素上,而是采用事件代理的模式:在根节点document上为每种事件添加唯一的Listener,然后通过事件的target找到真实的触发元素。这样从触发元素到顶层节点之间的所有节点如果有绑定这个事件,React都会触发对应的事件处理函数。这就是所谓的React模拟事件系统。
————————————————
版权声明:本文为CSDN博主「蒲噗噗」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014530721/article/details/52484434
数组的常用方法
shift 数组开始删除
unshift 数组开始追加
pop 数组末尾删除
push 数组末尾追加
slice(start,end):方法可从已有数组中返回选定的元素,返回一个新数组,
splice():该方法向或者从数组中添加或者删除项目,返回被删除的项目。(该方法会改变原数组)
CSS3 新增属性
transform
transition
box-shadow
border-radius
display:flex
@media only screen and (max-width:xxxx){}
animation
perspective
perspective-origin
transform-origin
text-overflow
word-break:break-all
word-wrap:break-word;
outline:
background-image:-webkit-linear-gradient(left top,red,blue);
background-size:cover;
-webkit-filter:滤镜
-webkit-text-stroke:文字描边
渐变色 动画 背景图平铺方式 文字溢出处理方式 弹性布局 媒体查询 旋转中心点
盒子模型
border-box 的宽度是包含padding、border
content-box 不包含
antd和bootstrap3以后都是用的border-box
但属性默认是content-box
微信小程序默认还是content-box
【ok2】CSS居中
水平居中
儿子为行内元素 行级块状元素
if(爸爸是块状元素)设置text-align:center
if(爸爸不是块状元素) 设置爸爸为块状元素,再设置text-align:center//设成inline-block不行
儿子为块状元素
方案一:分儿子宽度是否固定的两种情况
if(儿子宽度固定) 给儿子设置margin:0 auto
if(儿子宽度不固定) (默认子元素宽度和父元素一样)需要把儿子装换为inline或inline-block 再给爸爸text-align:center
方案二:使用定位属性
先给爸爸设为相对定位,儿子设为绝对定位
然后让儿子水平居中left:50%
if(儿子宽度固定) 设儿子 margin-left:-元素一半宽度px 或 transform:translateX(-50%)
if(儿子宽度不固定) 设儿子 transform:translateX(-50%)
方案三:使用flexbox布局实现(宽度定不定都可以)
给爸爸添加display:flex; justify-content:center;(只是水平居中)
垂直居中
行级元素
单行的行内元素
行高等于盒子的高度 (爸爸div的height和儿子span的line-height一样)
多行的行内元素
给爸爸设置display:table-cell; vertical-align:middle;
块级元素
方案一:使用定位
同上略
方案二:使用flexbox布局
给爸爸添加display:flex;align-items:center;
水平垂直居中
已知儿子爸爸都知道宽高
方案一:利用定位属性 auto
爸爸相对定位,儿子绝对定位
然后给儿子:top 0 right 0 bottom 0 left 0 margin auto
(儿子宽高缺失会继承爸爸的,然后设过的那个属性会居中)
方案二:利用定位属性 手动
爸爸相对定位,儿子绝对定位
left :50% top:50% margin-left:-一半宽度px; top同理
未知儿子的宽高,知道爸爸的
方案一:利用定位属性 translate
爸爸相对定位,儿子绝对定位
left :50% top:50% translateX(-50%) Y同理
方案二:flex布局
爸爸flex定位 justify-content: center; align-items: center;
【ok2】CSS 两栏左右固定布局
https://juejin.cn/post/6844903574929932301#heading-28
【ok2】CSS 选择器优先级
!important
- 内联样式(1000)
- ID 选择器(0100)
- 类选择器 / 属性选择器 / 伪类选择器(0010)
- 元素选择器 / 关系选择器 / 伪元素选择器(0001)
- 通配选择器(0000)
【】如何有效提升静态文件的加载速度
总的提高页面加载速度一般有三个方面:
1、代码逻辑结构的优化
2、SSR服务器渲染,将首屏所有内容在服务器端渲染成html静态代码后,直接输出给浏览器,可以有效加快用户访问站点时首屏的加载时间。
3、提升静态文件的加载速度
提升静态文件的加载速度的三个层面
-加快静态文件的下载速度
-减少静态文件的文件大小
-减少静态文件的请求数量(从而减少发起的请求次数,对于移动端来说请求的开销比网速的开销要大
具体方法
1.代码的压缩:
在生产发布的时候像JS脚本和CSS文件的一些命名规范和空行缩进都没什么必要了,可以使用工具(webpack)对这些代码进行混淆和压缩,减少静态文件的大小
2.文件合并:
-合并js脚本文件
-合并css样式文件
-合并css引用的图片,使用sprite雪碧图
使用webpack工具
3.gzip压缩
在webpack的配置中增加gzip压缩配置
4.CSN和缓存
CDN 是一个全球(或者只有国内,具体看供应商)分布式网络,它把网站内容更快地传递给服务范围内的一个具体位置,把你的(静态)数据用 CDN 放到澳大利亚(漠河)则会很大程度上提高用户访问网站的体验。
如果没有CDN服务,我们可以添加Expires头,减少DNS查找,配置ETag,使AjaX可缓存。
【】es6中require和import的区别
ES6学习笔记(二)—— 通过ES6 Module看import和require区别
- import是ES6标准中的模块化解决方案,require是node中遵循CommonJS规范的模块化解决方案
- 后者支持动态引入,也就是require(${path}/xx.js),前者目前不支持,但是已有提案
- 前者是关键词,后者不是
- 前者是编译时加载,必须放在模块顶部,在性能上会比后者好一些,后者是运行时加载,理论上来说放在哪里都可以
- 前者采用的是实时绑定方式,即导入和导出的值都指向同一个内存地址,所以导入值会随着导出值变化。而后者在导出时是指拷贝,就算导出的值变化了,导入的值也不会变化,如果想要更新值就要重新导入
- 前者会编译成require/exports来执行
还有使用上的区别
import
//普通
let a = 1;
export {a};
import {a} from 'file.js'
import * as all from 'file.js'
all.a
import {a as bieming} from 'file.js'
//带默认值
let a = 1;
export default {a};
import all from 'file.js';
import {default as all} from 'file.js'//两个是相等的语法糖
console.log(all)//{a:1}
require
// 普通
let a = 1;
module.exports = {a};
const target = require('file.js');
console.log(target)//{a:1}
// 直接导出
let a = 1;
exports.a = a;
const target = require('file.js');
console.log(target)//{a:1}
// 需要注意的是,module.exports导出之后,后面的代码就对导出的模块无效了,例如上例中
// test.js
module.exports = { firstName, lastName, year };
exports.name = '222';
// demo.js
const test = require('./test.js');
console.log(test); // {firstName: "Michael", lastName: "Jackson", year: 1958}
//特别说明一下,由于require是可以在任意地方引入的,所以,我们在开发中用~引入图片的方式实际上等效于require:
<img src="~assets/img/icon/red_logo.png" class="logo" alt="">
//等效于
<img :src="require('assets/img/icon/red_logo.png')" class="logo" alt="">
详解JavaScript对象中的getter和setter
通过getter和setter设置伪属性
【】箭头函数和匿名函数的区别
匿名函数就是没有名字的函数,在使用的时候可以作为变量额值、可以作为参数值、可以自调用,在ES6中,匿名函数包含了箭头函数
//匿名函数自调用:
(function(){
console.log('b'); //b
})();
(function(a,b){
console.log(a + b);//300
})(100,200);
区别:
- 在ES中,匿名函数是包含了箭头函数
- 写法上的不同,箭头函数有一些语法糖,当函数体只有一条返回语句的时候可以省略{}和return,当只有一个形参的时候,可以省略形参的括号
- *箭头函数没有原型,然后箭头函数内部是this是词法作用域,由上下文决定,this指向在定义的时候是继承外层第一个普通函数的this
【】Webpack3/4区别
【】前端安全
XSS(Cross Site Scripting)跨站脚本攻击
(1)原理:页面渲染的数据中包含可运行的脚本
(2)攻击的基本类型:反射型(url参数直接注入)和存储型(存储到DB后读取时注入)
(3)注入点:HTML节点内的内容(text);HTML中DOM元素的属性;Javascript代码;富文本
防御:
(1)浏览器自带防御机制,主要应对反射型攻击(HTML内容或属性):http响应头中自动添加x-xss-protection,值为0(关闭),1(打开),默认打开
(2)对特定字符做转义:内容注入替换尖括号( < => < > => > ) 属性注入替换单引号或双引号( " => " ’ => ’
(3)CSP(Content Security Policy)内容安全策略:用于指定哪些内容可执行
CSRF(Cross Site Request Forgy)跨站请求伪造
原理:在第三方网站向本网站发起请求
只要用户访问了b站的前端页面,b站就可以在用户完全不知道的情况下,带着a站的用户登录态(cookie)向a站发起请求
防御:
(1)禁止第三方网站携带本网站的cookie信息:设置same-site属性,same-site属性有两个值,Strict(所有的第三方请求都不能携带本网站的cookie)和Lax(链接可以,但是form表单提交和ajax请求不行)
(2)本网站前端页面添加验证信息:使用验证码或者添加token验证
(3)referer验证:禁止来自第三方的请求
点击劫持
原理:第三方网站通过iframe内嵌某一个网站,并且将iframe设置为透明不可见,将其覆盖在其他经过伪装的DOM上,伪装的可点击DOM(按钮等)与实际内嵌网站的可点击DOM位置相同,当用户点击伪装的DOM时,实际上点击的是iframe中内嵌的网页的DOM从而触发请求操作
特点:用户自己做了点击操作;用户毫不知情;
防御:
(1)Javascript禁止内嵌
(2)设置http响应头 X-Frame-Options:有三个值 DENY(禁止内嵌) SAMEORIGIN(只允许同域名页面内嵌) ALLOW-FROM(指定可以内嵌的地址)
能在所有的web服务器端预设好X-Frame-Options字段值是最理想的状态。
(3)一些辅助手段,比如添加验证码,提高用户的防范意识
【】React 各个版本
【】useCallback 和 useMemo
https://www.jb51.net/article/209192.htm
项目上问题怎么解决
作用域的小问题:
就是在用react hooks的useEffect的方法时候,我里面用了一个异步的方法,它需要传一个回调函数,然后我在回调函数里又去取了全局作用域中的一个变量,这个变量它会在异步方法执行后进行更新,我需要它更新后的数据,但我直接去调用取到的时候更新前的数据。
然后我后来想了一下可能就是作用域的问题,解决方法也很简单,直接将那个需要的变量通过回调函数的传参传过来。