css
清浮动影响
1.父元素添加overflow:hidden。
overflow:hidden作用机制BFC:BFC(Block Formatting Context)全称是块级格式化上下文,用于对块级元素排版,默认情况下只有根元素(body)一个块级上下文,但是如果一个块级元素设置了float:left,overflow:hidden或position:absolute样式,就会为这个块级元素生产一个独立的块级上下文,使这个块级元素内部的排版完全独立。
独立的块级上下文可以包裹浮动流,全部浮动子元素也不会引起容器高度塌陷,就是说包含块会把浮动元素的高度也计算在内,所以就不用清除浮动来撑起包含块的高度。
那什么时候会触发 BFC 呢?常见的情况如下:
根元素;
float的值不为none;
overflow的值为auto、scroll或hidden;
display的值为table-cell、table-caption和inline-block中的任何一个;
position的值不为relative和static。
2.使用clear属性。使用伪类 :after
:after:{
content: '';
height: 0;
line-height: 0;
display: block;
visibility: hidden;
clear: both;
}
- 末尾添加额外标签设置
clear:both;
与上同原理 - 设置父元素为浮动元素
- 设置父元素position:absolute
css实现吸顶效果方式
flex布局
实现吸顶效果方式 详细查看
1.css
2.js:
-获取元素距离顶部距离(offsetTop)与滚动条滚动距离(scrollTop),后者>前者 就修改pisition为fix
-使用 el.getBoundingClientRect().top 实现,此方法获取元素距离视窗的顶部距离,小于0就修改pisition为fix
优化:
-吸顶的那一刻伴随抖动,在吸顶元素 position 变为 fixed 的时候,该元素就脱离了文档流,下一个元素就进行了补位。就是这个补位操作造成了抖动。
解决方案:为这个吸顶元素添加一个等高的父元素,我们监听这个父元素的值来实现吸顶效果
-吸顶效果不能及时响应,在 ios 系统上不能实时监听 scroll 滚动监听事件,在滚动停止时才触发其相关的事件。
解决方案:IOS 使用 position:sticky,Android 使用滚动监听 getBoundingClientRect().top 的值。
-频繁获取滚动元素信息,引起reflow
解决方案:1、牺牲平滑度满足性能,使用节流控制相关方法的调用;2、使用 IntersectionObserver 和节流结合,也牺牲了平滑度。
IntersectionObserverFun: function() { let self = this; let ele = self.$refs.pride_tab_fixed; if( !IntersectionObserver ){ let observer = new IntersectionObserver(function(){ let offsetTop = ele.getBoundingClientRect().top; self.titleFixed = offsetTop < 0; }, { threshold: [1] }); observer.observe(document.getElementsByClassName('title_box')[0]); } else { window.addEventListener('scroll', _.throttle(function(){ let offsetTop = ele.getBoundingClientRect().top; self.titleFixed = offsetTop < 0; }, 50)); } },
js
数据类型
- 基本类型:Symbol、Number、Undefined、null、String、Boolean
- 引用类型:Function、Object、Array
数组方法
- slice(int start,int end): Array
- splice(int start,int number, ...newItem): Array deleted 从start开始删除 number个元素,并添加 newItem。返回删除的元素数组,影响原数组
- join(separator):String 对于非字符串会调用其toString方法
- push(...items):int 末尾添加,返回数组长度,会改变原数组
- pop():any|undefined 删除末尾元素,返回被删除的元素,会改变原数组
- shift():any|undefined 删除首位元素,返回被删除的元素,会改变原数组
- unshift(...items):int 开头添加,返回数组长度,会改变原数组
- sort((item:any, item1:any)=>:int):Array 返回值小于0正序,大于0倒序,影响原数组
- reverse():Array 反转数据,改变原数组
- forEach(item:ant=>:int):undefined 不改变源数组
- filter(item:any=>:boolean):Array 不改变源数组,返回新数组
- reduce((total:any, currentValue:any, currentIndex:int, arr:Array)=>total:any, initialValue:any) 累加器,最终返回total,注意函数一定要return
- reduceRight 同上,只是遍历从后忘前
- indexOf(target:any):int 查找目标元素在数组中首次出现的下标,未找到返回-1
- lastIndexOf(target:any):int 查找目标元素在数组中最后一次出现的下标,未找到返回-1
- includes(target:any):Boolean 查找目标元素是否存在数组中
- find((item:any, index:int, arr:Array)=>:Boolean, thisValue:any):any|undefined 找到后不再执行其他元素
- findIndex((item:any, index:int, arr:Array)=>:Boolean, thisValue:any):int 找到后不再执行其他元素
- every((item:any, index:int, arr:Array)=>:Boolean, thisValue:any):Boolean 判断是否都返回true
- some((item:any, index:int, arr:Array)=>:Boolean, thisValue:any):Boolean 判断是否至少一个返回true
- fill(value:any, start=0:int, end=length:int):Array 填充元素,改变原数组
- keys():Array Iterator entries():Array Iterator 迭代器
字符串方法
- split(separator:any):Array 返回separator分割后形成的数组
- concat(...item:any):String 连接字符串,返回新的字符串
- substr(start=0:int, number=length:int):String 从start位置提取number个字符
- substring(start=0:int, stop=length:int):String 从start位置提取到stop之间的字符,不允许使用负数
- slice(start=0:int, stop=length:int):String 从start位置提取到stop之间的字符,允许使用负数
- replace(reg:RegExp|String, target:any):String 返回新字符串
- toUpperCase():String 返回新字符串
- toLowerCase():String 返回新字符串
- startsWith(target:any):Boolean
- endsWith(target:any):Boolean
- includes(target:any):Boolean
- indexOf(target:any):int 查找目标元素在首次出现的下标,未找到返回-1
- lastIndexOf(target:any):int 查找目标元素在最后一次出现的下标,未找到返回-1
- search(target:RegExp|String):int 匹配目标元素在首次出现的下标,未找到返回-1
- padStart(len:int, target:any):String 返回新字符串,在前面使用target补齐达到len长度
- padEnd(len:int, target:any):String 返回新字符串,在后面使用target补齐达到len长度
- match(reg:RegExp|String):null|Array 返回匹配情况
- matchAll(reg:RegExp|String):RegExp String Iterator 返回迭代器,注意reg不带g(全局匹配)回抛出异常
- charAt(int):String 返回指定下标的字符
- trim() 返回新字符串
- trimStart(别名:trimLeft) trimEnd (别名:trimRight) 返回新字符串
判断类型:Object.prototype.toString,可准确判断出具体类型
ES6新语法、属性、方法
Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
解构赋值
let [a=0, b, c] = [1, 2, 3];
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
const [a, b, c, d, e] = 'hello';
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
// 对象
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
扩展运算符 ...
// 该运算符主要用于函数调用。
function push(array, ...items) {
array.push(...items);
}
// 复制数组
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
//对象
let z = { a: 3, b: 4 };
let n = { ...z };
Symbol
Symbol 作为属性名,遍历对象的时候,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。
但是,它也不是私有属性,有一个Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。
const obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
const objectSymbols = Object.getOwnPropertySymbols(obj);
objectSymbols
// [Symbol(a), Symbol(b)]
Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。
Symbol.for("bar") === Symbol.for("bar")
// true
Symbol("bar") === Symbol("bar")
// false
Map
需要特别注意的是,Map 的遍历顺序就是插入顺序。
-Map.prototype.keys():返回键名的遍历器。
-Map.prototype.values():返回键值的遍历器。
-Map.prototype.entries():返回所有成员的遍历器。
-Map.prototype.forEach():遍历 Map 的所有成员。
// 对象转为 Map 可以通过Object.entries()。
let obj = {"a":1, "b":2};
let map = new Map(Object.entries(obj));
WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。典型场合就是 DOM 节点作为键名
const wm = new WeakMap();
let key = {};
let obj = {foo: 1};
wm.set(key, obj);
obj = null;
wm.get(key)
// Object {foo: 1}
// WeakMap注意场景也是避免内存泄漏, 上面代码中,e1和e2是两个对象,我们通过arr数组对这两个对象添加一些文字说明。这就形成了arr对e1和e2的引用。
// 一旦不再需要这两个对象,我们就必须手动删除这个引用,否则垃圾回收机制就不会释放e1和e2占用的内存。
const e1 = document.getElementById('foo');
const e2 = document.getElementById('bar');
const arr = [
[e1, 'foo 元素'],
[e2, 'bar 元素'],
];
// 不需要 e1 和 e2 的时候
// 必须手动删除引用
arr [0] = null;
arr [1] = null;
Set
-Set.prototype.add(value):添加某个值,返回 Set 结构本身。
-Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
-Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
-Set.prototype.clear():清除所有成员,没有返回值。
const set = new Set([1, 2, 3, 4, 4]);
// 去除数组的重复成员
[...new Set(array)]
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
// 去除字符串里面的重复字符。
[...new Set('ababbc')].join('')
遍历
Set.prototype.keys():返回键名的遍历器
Set.prototype.values():返回键值的遍历器
Set.prototype.entries():返回键值对的遍历器
Set.prototype.forEach():使用回调函数遍历每个成员
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
WeakSet
结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。首先,WeakSet 的成员只能是对象,而不能是其他类型的值。
WeakSet 不能遍历,是因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏。
const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set
Proxy代理器
用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
下面是 Proxy 支持的拦截操作一览,一共 13 种。
get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。
preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
异步编程
1、回调函数
2、Promise
ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
多个请求 Promise.all([]).then(resArr)
3、async await ES2017 标准引入了 async 函数,使得异步操作变得更加方便。
async 标识一个function是异步函数,await只能用在异步函数中,返回值是 Promise。
this问题
虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。
const target = {
m: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m() // true
Reflect
Reflect对象一共有 13 个静态方法。
Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。下面是对它们的解释。
原型
实现继承的几种方式和优缺点
此问题从能够操作原型方式解析
1、直接指定原型
1.1、通过构造函数实现继承,
优点:解决了子类实例共享父类引用属性的问题;创建子类实例时,可以向父类构造函数传参;
缺点:子类实例不能继承父类中原型对象上的属性或者方法;无法复用父类实例属性,
function Parent() {
this.name = 'name';
}
Parent.prototype.say = function () {
console.log('say');
}
function Child() {
Parent.call(this);
this.type = 'child';
}
const child = new Child();
child.say() // .sya is not a function
1.2、通过原型对象实现继承,原型参考
缺点:父类的属性是引用类型时,子类实例继承的是同一份属性,任一实例改变该属性都会引起全局变化,无法互相隔离
无法在不影响所有实例对象的情况下,给父类的构造函数传递参数
function Parent() {
this.name = 'name';
this.favor = ['blue', 'red'];
}
Parent.prototype.say = function () {
console.log('say');
}
function Child() {
this.type = 'child';
}
Child.prototype = new Parent();
const child = new Child();
1.3 组合方式,结合了上两种方式,避免了上面的缺点。但是这种方式在每次实例化子类实例时,都会调用两次父类构造函数,需要优化。
function Parent() {
this.name = 'name';
this.favor = ['blue', 'red'];
}
Parent.prototype.say = function () {
console.log('say');
}
function Child() {
Parent.call(this);
this.type = 'child';
}
Child.prototype = new Parent();
const child = new Child();
1.4 组合优化①
缺点:子类实例的constructor属性直接指向了父类构造函数,导致无法判断当前对象实例化自哪个构造函数。
function Parent() {
this.name = 'name';
this.favor = ['blue', 'red'];
}
Parent.prototype.say = function () {
console.log('say');
}
function Child() {
Parent.call(this);
this.type = 'child';
}
Child.prototype = Parent.prototype;
const child = new Child();
1.5 组合优化②
通过Object.create()方法指定了子类实例的proto属性,同时显式声明子类的constructor
function Parent() {
this.name = 'name';
this.favor = ['blue', 'red'];
}
Parent.prototype.say = function () {
console.log('say');
}
function Child() {
Parent.call(this);
this.type = 'child';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const child = new Child();
2、Class
class Parent {
constructor(){
this.name = 'name';
this.favor = ['blue', 'red'];
}
say(){
console.log('say');
}
}
class Child extends Parent {
constructor() {
super()
this.type = 'child';
}
}
const child = new Child();
多重继承:采用将多个父类属性赋值到新的类,让子类继承这个新的父类
链判断运算符
const firstName = message?.body?.user?.firstName || 'default';
// 下面是判断对象方法是否存在,如果存在就立即执行的例子。
iterator.return?.()
//Null 判断运算符??
//但是只有运算符左侧的值为null或undefined时,才会返回右侧的值。
const headerText = response.settings.headerText ?? 'Hello, world!';
this问题,call、apply、bind
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
func(){}.bind(thisValue)
闭包
new函数得到一个对象的过程
1、创建一个空对象
2、让空对象的proto(IE没有该属性)成员指向了函数的prototype成员对象
3、使用apply调用函数,属性和方法被添加到 this 引用的对象中
4、如果函数中没有返回其它对象,那么返回 this,即创建的这个的新对象,否则,返回构造函数中返回的对象
function _new() {
let obj= {}; // 创建的新对象
// 第一个参数是构造函数
let [constructor, ...args] = [...arguments];
// 执行 [[原型]] 连接 ;实际上就是生产了一个新的上下文
obj.__proto__ = constructor.prototype;
// 使用apply在obj作用域中调用构造器函数,属性和方法被添加到 this 引用的对象即obj中
let result = constructor.apply(obj, args);
if (result && (typeof (result) == "object" || typeof (result) == "function")) {
// 如果构造函数执行的结果返回的是一个对象,那么返回这个对象
return result;
}
// 如果构造函数返回的不是一个对象,返回创建的新对象
return obj;
}
结合下面的代码片段来具体分析下:
function Person(age){
this.age= age;
console.log(this);
return {age:age};//返回对象
}
Person.prototype.index = 1
var person1 = new Person(20); // 此处相当于var person1=_new(Person, 20)
var person2 = Person(18);
console.log(person1);
console.log(person2);
console.log('p1.index=', person1.index)
console.log('p2.index=', person2.index)
设计模式
- 发布订阅:如 vue 中的双向绑定实现
- 单例 如: 导出一个模块
- 装饰者:在不改变对象自身的基础上,动态的给某个对象添加额外的职责,不会影响原有接口的功能。如* react中的高阶组件
- 代理模式: 如vue中对data属性的代理,ES6的 Proxy
- 命令模式:指的是 一个执行某些待定事情的指令, 用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。如websocket发送请求与接收请求处理
- 外观模式:为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使子系统更加容易使用。外观模式在JS中,可以认为是一组函数的集合。
深拷贝实现方式 循环引用问题怎么解决
1、递归
2、迭代,参考
3、JSON.stringify JSON.parse
4、使用第三方库 如 lodash
循环引用可在外部定义一个数组保存已复制过的值,在深拷贝前判断是否在数组中,在的话就设置为这个值,不在再执行深拷贝
跨域
页面跨域通信几种方式
1、window.postMessage
2、window.name
3、document.domain (限制在一级域名一样)
4、Hash传参 (A修改B url Hash值,B监听onhashchange)
接口跨域问题
1、添加允许跨域头信息
2、服务端使用代理
3、JSONP
添加事件方法
- dom节点直接绑定
点击
- addEventListener(type, listener, {capture:捕获阶段触发, once:, })
- addEventListener(type, listener, useCapture:false(在捕获阶段执行))
- IE7/8: attachEvent('on'+type, listener)
阻止事件冒泡与默认行为
- 阻止事件冒泡:e.stopPropagation() IE: window.event.cancelBubble = true
- 阻止事件默认行为 e.preventDefault() IE: window.event.returnValue == false;
数组去重
一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?
检查是否有缓存->浏览器发出请求->dns解析->得到解析后ip机器端口->建立tcp/ip连接->浏览器发起HttpGet请求->WebServer接受到请求->原路返回->浏览器处理(302、404等)->展示
-可同步发起多少请求?是浏览器而定,同一个域名一般6-7个。所以为了加快资源下载速度,通常将静态资源放入单独的dns服务器
-如何利用缓存:
1. 强制缓存判断HTTP首部字段:cache-control,Expires。
-Expires是一个绝对时间,即服务器时间。浏览器检查当前时间,如果还没到失效时间就直接使用缓存文件。但是该方法存在一个问题:服务器时间与客户端时间可能不一致。因此该字段已经很少使用。
cache-control中的max-age保存一个相对时间。例如Cache-Control: max-age = 484200,表示浏览器收到文件后,缓存在484200s内均有效。如果同时存在cache-control和Expires,浏览器总是优先使用cache-control。
2. 协商缓存,每次发请求到服务器确认。对比缓存通过HTTP的last-modified,Etag字段进行判断。
-last-modified是第一次请求资源时,服务器返回的字段,表示最后一次更新的时间。下一次浏览器请求资源时就发送if-modified-since字段。服务器用本地Last-modified时间与if-modified-since时间比较,如果不一致则认为缓存已过期并返回新资源给浏览器;如果时间一致则发送304状态码,让浏览器继续使用缓存。
-Etag:资源的实体标识(哈希字符串),当资源内容更新时,Etag会改变。服务器会判断Etag是否发生变化,如果变化则返回新资源,否则返回304。
方案:html使用协商缓存,css&js&图片 使用强缓存,文件命名带上hash值
实际上单页应用:html强制设置不缓存,资源文件默认浏览器缓存,并设置了Last-Modified: 和 ETag
安全
XSS
跨站脚本攻击(Cross Site Scripting)是最常见和基本的攻击 WEB 网站方法,攻击者通过注入非法的 html 标签或者 javascript 代码,从而当用户浏览该网页时,控制用户浏览器。
- 通过表单保存恶意脚本后,在浏览器执行。获取用户cookie等信息或对网站正常功能进行破坏
- 防御
-cookie设置为httpOnly
-对输入编码后存在数据库
CSRF
跨站点请求伪造(Cross-Site Request Forgeries),也被称为 one-click attack 或者 session riding。冒充用户发起请求(在用户不知情的情况下), 完成一些违背用户意愿的事情(如修改用户信息,删初评论等)。
- 通过引诱用户点击恶意链接,发起恶意请求,此时会带上cookie
- 防御:
-验证码
-referer check
-token
点击劫持
点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中,并将 iframe 设置为透明,在页面中透出一个按钮诱导用户点击。
- 设置http响应头X-FRAME-OPTIONS,方式被嵌入其他网站
-DENY,表示页面不允许通过 iframe 的方式展示
-SAMEORIGIN,表示页面可以在相同域名下通过 iframe 的方式展示
-ALLOW-FROM,表示页面可以在指定来源的 iframe 中展示
SQL注入
就是后端依赖前端返回的参数直接拼接sql进行查询数据,导致sql不正常的拼接,造成的安全问题。
防御:对前端传递的信息进行层层校验,不直接使用。
TCP/IP
TCP:
1、有状态的连接
2、只能是一对一通信
3、适用于要求可靠传输的应用,例如文件传输
三次握手:
-客户端向服务端发送连接请求:SYN=1,Seq=随机数J
-服务端收到连接请求,确认可以连接,发送:SYN=1,ACK=1,ack=J+1,seq=随机数N
-客户端收到请求,确认连接:发送应答:ACK=1,ack=N+1
四次挥手(全双工在断开连接时两端都需要发送 FIN 和 ACK。)
-客户端发送关闭请求:FIN=J
-服务端确认关闭应答:ack=J+1,ACK=1 (此时客户端不可在发送数据,可以接受数据)
-服务端发送关闭请求:FIN=N
-客户端确认关闭应答:ack=N+1,ACK=1(此时连接互相关闭)
UDP:
1、无状态连接
2、有单播,多播,广播的功能
3、UDP是面向报文的
4、不可靠性
5、头部开销小,传输数据报文时是很高效的。
http2
https
vue
生命周期
vue: beforeCreate created mounted beforeUpdate updated activated deactivated beforeDestroy destroyed errorCaptured
vue-router: beforeRouteEnter, beforeRouteUpdate, beforeRouteLeave
双向绑定原理、如何实现数组的双向绑定
利用了Object.defineProperties 劫持了 get set。在get里收集依赖,在set里通知依赖更新,所以只支持IE9+
缺点:
- 需要对数组的一些方法改写
- 无法监测数组下标直接赋值和长度修改
- 无法监听到对象新增的属性和删除属性
vue-next 使用了新的 Proxy 实现双向绑定,只支持IE12+
Proxy优点有:
-对数据也能监测到
-性能更好,不需要2.x遍历属性然后再劫持
-有更全面的方法
keep-alive原理
key的作用
-使列表渲染时针对虚拟don的diff算法更有效,减少不必要的更新
-赋值不同的值,可强制使组件重新渲染
组件间通信
- prop、自定义事件
- refs、children等
- 自定义个发布订阅对象
自定义指令
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
component动态组件原理
自定义常见业务组件
react
生命周期
挂载
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
更新
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
卸载
componentWillUnmount()
错误处理
static getDerivedStateFromError()
componentDidCatch()
高阶组件
高阶组件是参数为组件,返回值为新组件的函数。
例如 Redux 的 connect
Hook
为函数式组件使用,与class组件对比的优缺点
useState
useEffect -- 相当于componentDidMount、componentDidUpdate 和 componentWillUnmount周期钩子
taro
flutter
获取微信用户id、用户名、授权
微信支付
支付宝支付
shell 3剑客
grep '字符' 文件 (过滤)
sed '命令' 文件 (实现数据的替换,删除,增加,选取等(以行为单位进行处理))
awk '条件{命令}' 文件 (AWK 是一种处理文本文件的语言,是一个强大的文本分析工具。)
awk [选项参数] 'script' var=value file(s) 或 awk [选项参数] -f scriptfile var=value file(s)
linux 资源查看
查看磁盘情况
df -a(所有文件系统,包括虚拟)
df -h 以K M G单位显示
du -sh xx 查看指定文件大小
查看内存情况
free -h
top
查看CPU情况
top
关闭进程 kill pid
查找nodejs进程,并查看进程相关资源信息
ps -ef | grep node
pidstat -r(内存)u(CPU)d(IO情况) -p 1875(pid) 1(刷新频率)
查看java进程
ps -ef | grep java
pidstat -r(内存)u(CPU)d(IO情况) -p 1875(pid) 1(刷新频率)