为21届学弟学妹秋招护驾(前端复习总结)
双学位两个专业答辩全都通过了,也拿到了心仪的offer。把自己在春招期间整理的1万字资料供21届学弟学妹作为大纲性内容自查哈,希望你们在特殊时期也能和20届一起奋斗。以下几个是我感觉非常不错的复习资料,这些题目看完后找工作都挺稳的了。希望你们在秋招能获得心仪的offer
2019前端面试系列——JS高频手写代码题
2019前端面试系列——CSS面试题
2019前端面试系列——JS面试题
2019前端面试系列——Vue面试题
2019前端面试系列——HTTP、浏览器面试题
仅作大纲性参考,有错误请自行翻阅文献哈
实现居中
水平居中:
text-align:center;
margin: 0 auto;
display:flex; justify-content: center;
position:absolute;left: 50%; transform: translateX(-50%);
垂直居中:
- line-height等于高度
- position:absolute; top: 50%; transform: translateY(-50%);
- display:flex; align-items:center;
- 父元素display:table; 子元素display:table-cell;vertical-align:middle;
https://juejin.im/post/58f818bbb123db006233ab2a
闭包的概念
概念:闭包就是能读取其他函数内部变量的函数
优点:1、避免全局变量的污染;2、希望一个变量长期存储在内存(缓存变量);
缺点:1、容易造成内存泄露
内存泄露例子:
let getMoney = function() { let money = 10 return function() { return money } } let f = getMoney() // f函数存在的话,money会一直得不到释放
深拷贝和浅拷贝
浅拷贝:
Object.assign() Array.prototype.slice()
深拷贝:
JSON.parse(JSON.stringify()) // 无法序列化函数
递归函数:
function cloneObject(obj) { var newObj = {} //如果不是引用类型,直接返回 if (typeof obj !== 'object') { return obj } //如果是引用类型,遍历属性 else { for (var attr in obj) { //如果某个属性还是引用类型,递归调用 newObj[attr] = cloneObject(obj[attr]) } } return newObj } // 无法解决循环引用问题
数组去重
ES6中的set:
let arr = [1,2,3,1,2,3] let arr2 = [...new Set(arr)]
新数组去重:
let newArr = [] let arr = [1,2,3,1,2,3] arr.forEach(item => { if (newArr.indexOf(item) === -1) { newArr.push(item) } })
JS执行机制、事件循环
事件循环:同步任务进入主线程,异步任务进入EventTable中并注册回调函数中。当所有同步任务执行完毕时,主线程从事件队列中读取回调函数并执行。上述过程不断重复,称为事件循环
宏任务和微任务
异步任务会分成宏任务与微任务
微任务是在宏任务执行完后立即执行的任务
宏任务:主代码块,setTimeout,setInterval等
微任务:Promise,process.nextTick等
当宏任务执行完时,如果有可执行的微任务,则执行所有微任务后再执行新的宏任务。如果没有可执行的微任务,那么直接开始执行新的宏任务
https://www.jianshu.com/p/184988903562
函数提升与变量提升
在作用域中,函数声明与变量声明都会提升到顶部。首先声明变量整体,然后再声明函数整体。
function v() { console.log(a) var a = 1; function a() {} } v() // 输出函数a // 上面函数相当于: function v() { var a; function a() {} console.log(a) a = 1; } v()
function v() { var a = 1; function a() {} console.log(a) } v() // 输出1 // 相当于: function v() { var a; function a() {} a = 1; console.log(a) } v()
注意:函数表达式不会被提升
常见内存泄露
1、全局变量滥用
当不用var声明变量的时候,比如b=1,相当于挂载到全局变量。
解决方法:使用严格模式
2、没有清理DOM元素的引用
3、闭包
4、定时器被遗忘
性能优化
1、减少请求数量
2、事件委托:减少DOM操作,每个新增的元素都能拥有该事件
3、压缩资源大小
4、减少重绘回流
5、innerHTML代替dom操作
6、图片预加载
Vue性能优化
1、路由懒加载
2、v-if与v-show正确使用
3、v-for中设置唯一key
4、图片懒加载
实现EventBus
function EventEmitter() { this.events = new Map() this.addListener = function (name, fn) { this.events.set(name, fn) } this.emit = function (name) { let fn = this.events.get(name) fn(...[...arguments].slice(1)) } } var bus = new EventEmitter() bus.addListener('age', (age, time) => { console.log(age, time) }) bus.emit('age', 18, '2019')
引用类型有哪几种
- Object
- Array
- Date
- RegExp
- Function
圣杯布局、双飞翼布局的实现
这个请自查哈
CSS盒模型
Margin、Border、Padding、Content
IE定义的盒模型中元素的宽高包括了padding和border
W3C盒模型元素宽高为Content
box-sizing: content-box; // W3C盒模型
box-sizing: border-box; // IE盒模型
伪类和伪元素的区别
它们之间的根本区别在于是否创造了新的元素
伪类不会创造新的元素,比如:hover :active
伪元素会创造新的元素,比如:after :before
伪类:表示已存在的某个元素处于某种状态
伪元素:用于将特殊的效果添加到某元素
实现快速排序
function quickSort(arr) { if (arr.length < 1) { return arr } var left = [] var right = [] var pivot = arr[0] for (var i = 1; i < arr.length; i++) { if (arr[i] > pivot) { right.push(arr[i]) } else { left.push(arr[i]) } } return [...quickSort(left), pivot, ...quickSort(right)] } var arr = [0, 3, 4, 2, 6, 1, -1, 123] console.log(quickSort(arr)) // ---------------------- console.log('------------') // ---------------------- // 单指针循环法 function quickSort2(arr) { if (arr.length < 1) { return arr } var pivot = arr[0] var mark = 0 for (var i = 0; i < arr.length; i++) { if (arr[i] < pivot) { mark++ let temp = arr[i] arr[i] = arr[mark] arr[mark] = temp } } let temp = arr[0] arr[0] = arr[mark] arr[mark] = temp return [...quickSort2(arr.slice(0, mark)), arr[mark], ...quickSort2(arr.slice(mark+1, arr.length))] } var arr = [0, 3, 4, 2, 6, 1, -1, 3] console.log(quickSort2(arr))
堆排序
// 最小堆,父节点小于两个子节点 function downAjust(arr, parentIndex, length) { let childIndex = 2 * parentIndex + 1 while (childIndex < length) { if (childIndex + 1 < length && arr[childIndex + 1] < arr[childIndex]) { childIndex ++ } if (arr[parentIndex] < arr[childIndex]) { return } let temp = arr[parentIndex] arr[parentIndex] = arr[childIndex] arr[childIndex] = temp parentIndex = childIndex childIndex = 2 * parentIndex + 1 } } function buildHeap(arr) { for (let i = parseInt((arr.length -1) / 2); i >= 0; i--) { downAjust(arr, i, arr.length) } console.log(arr) } var arr = [7, 1, 3, 10, 5, 2, 8, 9, 6] buildHeap(arr) heapSort(arr) function heapSort(arr) { for (let i = arr.length - 1; i >= 0; i--) { let temp = arr[i] arr[i] = arr[0] arr[0] = temp downAjust(arr, 0, i) } console.log(arr) }
归并排序
function merge(left, right) { let arr = [] while (left.length && right.length) { if (left[0] < right[0]) { arr.push(left.shift()) } else { arr.push(right.shift()) } } arr.push(...left, ...right) return arr } function mergeSort(arr) { if (arr.length > 1) { let mid = parseInt(arr.length / 2) let left = arr.slice(0, mid) let right = arr.slice(mid) return merge(mergeSort(left), mergeSort(right)) } return arr } var arr = [8, 2, 4, 1, 3, 9, 0, -1, -2] console.log(mergeSort(arr))
实现二分查找
function binarySearch(arr, target) { let low = 0 let high = arr.length - 1 while (low <= high) { let mid = parseInt((low + high) / 2) if (arr[mid] > target) { high = mid - 1 } else if (arr[mid] < target) { low = mid + 1 } else { return mid } } return -1 } var arr = [0, 1, 2, 3, 3, 4, 5] console.log(binarySearch(arr, 5))
数组和链表的区别
数组:
在内存中连续存储
优点是具有高效的随机访问能力,常量时间内可以找到对应元素
缺点是在插入和删除元素时,可能会导致大量元素被迫移动,影响效率
数组适合在读操作多,写操作少的场景
链表:
在内存中是随机存储
优点是能够灵活地进行插入删除操作
缺点是查找元素的性能较差
URI和URL
URI就是统一资源标识符,URL就是统一资源定位符
URI有两种形式,URL和URN
URL由协议、主机、端口、路径四个部分组成,唯一标识资源位置
URN是统一资源名,它使得资源与位置无关,只使用资源名就能够获取资源,这种形式处于试验阶段,尚未被大范围使用
TCP与UDP
TCP是一种可靠的传输方式,能够将数据按序无差错传输。HTTP使用TCP来传输其报文数据。
TCP的建立需要经过三次握手
UDP是一种不可靠的传输方式,它不保证数据能准确传输。在允许丢包的情况下使用UDP传输效率会更高,比如视频、音频,个别丢包不影响整体画面
访问一个网址时浏览器经历的步骤
1、浏览器从URL中解析主机名和端口
2、DNS解析出IP地址
3、建立TCP连接
4、浏览器发送HTTP请求报文
5、服务器返回HTTP响应报文
6、关闭连接,浏览器开始解析文档
7、浏览器首先解析出DOM树
8、再次解析出CSSOM树
9、根据DOM树和CSSOM树来构造render tree渲染树
10、浏览器重排、重绘,绘制每个节点
重排、重绘
浏览器默认采用流式布局模型
重排也叫回流
重排:根据渲染树中每个对象的信息,计算渲染对象各自的几何信息(位置、尺寸大小),并将其安置在界面正确位置。
因此呢,如果发生改变了位置、尺寸大小,会引发回流,重新计算全局布局(从根节点重新布局)或者局部布局。例如,改变窗口大小会引发全局布局的重新计算;
引起重排的操作:
- 页面首次渲染
- 窗口大小改变
- 元素尺寸或位置改变
- 字体大小改变
- 添加或删除DOM元素
- 获取offsetTop、offsetLeft等属性触发重排,应缓存起来
重绘:当页面某元素样式的改变并不影响其在文档流中的位置。
例如改变背景颜色、字体颜色等操作
强缓存与协商缓存
强缓存:服务器通过设置response header中的cache-control字段来控制浏览器对资源缓存,max-age用于设置缓存的过期时间。如果是cache-control: no-cache,代表跳过设置强缓存,会走协商缓存。
协商缓存:当强缓存过期后,客户端每次访问资源时会先看下缓存有无过期,如果过期了,则询问服务器资源是否已更改,如果已更改,服务器会返回资源以及对应的etag和last-modified;如果没有更改,则服务器返回一样的etag和last-modified,状态码为304(资源无变更),这时客户端之后访问缓存都要走一次协商缓存。
etag:每个文件的hash
last-modified:资源最后更改时间
https://www.jianshu.com/p/9c95db596df5
- 强制缓存是我们在第一次请求资源时在 http 响应头设置一个过期时间,在时效内都将直接从浏览器进行获取,常见的 http 响应头字段如 Cache-Control 和 Expires
- 协商缓存是我们通过 http 响应头字段 etag 或者 Last-Modified 等判断服务器上资源是否修改,如果修改则从服务器重新获取,如果未修改则 304 指向浏览器缓存中进行获取
call、apply、bind
这三个函数都是改变this的指向
call、apply第一个参数都是this所指向的对象,它们之间的区别是传参的方式不同。
apply的传参方式是以数组的方式传参,而call是以逐个传参的方式
bind函数的第一个参数也是this所指向的对象,它和上面call、apply不一样,它返回一个新的函数。
完全二叉树与满二叉树
满二叉树:深度为k,并且有2^k-1个节点的二叉树
完全二叉树:完全二叉树的n个节点的编号都能对应相同深度满二叉树的1~n个节点一一对应
Promise对象
Promise有三个状态:
1、进行中pending;2、成功态fulfilled;3、拒绝rejected;
Promise构造函数接受一个参数为resolve和reject的函数
resolve、reject能改变promise的状态。
通过then方法为promise注册完成时的处理程序
Vue生命周期
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestory、destoryed
created周期在模板渲染成HTML前调用,能够获取到实例的data,通常初始化某些属性值
mounted周期在模板渲染成HTML后调用,通常是初始化页面完成后对DOM进行一些操作。
生命周期 | 是否获取dom节点 | 是否可以获取data | 是否获取methods |
---|---|---|---|
beforeCreate | 否 | 否 | 否 |
created | 否 | 是 | 是 |
beforeMount | 否 | 是 | 是 |
mounted | 是 | 是 | 是 |
web worker
JavaScript是单线程语言,WebWorker是为了创造多线程环境,允许主线程创建Worker线程,这样worker线程完成任务后会自动返回结果给主线程。
需要注意以下几点:
1、主线程与worker线程必须同源
2、worker无法读取主线程的Dom对象
3、不能直接通信,需要通过消息postMessage来完成
封装ajax
function ajax(url, type, data, success) { let aj = new XMLHttpRequest() if (type === 'get') { if (data) { url += '?' let arr = [] Object.keys(data).forEach(key => { arr.push(key + '=' + data[key]) }) url += arr.join('&') } aj.open(type, url) aj.send(data) } else if (type === 'post') { aj.open(type, url) aj.setRequestHeader('Content-type', 'x-www-form-urlencoded') if (data) { aj.send(data) } else { aj.send() } } aj.onreadystatechange = function() { if (aj.readyState === 4 && aj.status === 200) { success(aj.responseText) console.log(aj.responseText) return aj.responseText } } }
for..in与for..of
for..in循环用于获取键名
for..of循环用于获取键值
例如
var arr = ['a','b','c'] for (var i in arr) { console.log(i) } // 0, 1, 2 //遍历数组就是获取下标 for (var i of arr) { console.log(i) } // a b c //遍历数组就是获取值 var obj = { a: 1, b: 2, c: 3 } for (var i in obj) { console.log(i) } // a b c //遍历对象就是获取键名 for (var i of arr) { console.log(i) } // 用在对象会报错
防篡改对象
主要使用三个方法Object.preventExtensions、Object.seal、Object.freeze
分别对应为不可扩展、密封、冻结
增 | 删 | 改 | |
---|---|---|---|
不可扩展 | ✖️ | ✔️ | ✔️ |
密封 | ✖️ | ✖️ | ✔️ |
冻结 | ✖️ | ✖️ | ✖️ |
通过 Object.isExtensible([Object]) 可以确定对象是否可以扩展。 true 可以扩展, false 不可以扩展。
通过 Object.isSealed([Object]) 可以确定对象是否被密封了。
通过 Object.isFrozen([Object]) 来检测对象是否被冻结。
数组扁平化
var arr = [1, 2, [3,4], [1, [2, 3, [3, 4]]]] function fn(arr) { let newArr = [] for (var i of arr) { if (i instanceof Array) { newArr.push(...fn(i)) } else { newArr.push(i) } } return newArr } console.log(fn(arr))
判断数据类型
判断数组:Array.isArray()
typeof:能判断的数据类型有String、number、objcet、function、boolean、undefined。不能判断null和array
typeof Symbol(); // symbol 有效 typeof ''; // string 有效 typeof 1; // number 有效 typeof true; //boolean 有效 typeof undefined; //undefined 有效 typeof new Function(); // function 有效 typeof null; //object 无效 typeof [] ; //object 无效 typeof new Date(); //object 无效 typeof new RegExp(); //object 无效
instanceof:判断是否某个原型的实例,不能检测null和undefined
[] instanceof Array; //true {} instanceof Object;//true new Date() instanceof Date;//true new RegExp() instanceof RegExp//true null instanceof Null//报错 undefined instanceof undefined//报错
Object.prototype.toString.call():这个较为准确,不能检测自定义类型
Object.prototype.toString.call('') ; // [object String] Object.prototype.toString.call(1) ; // [object Number] Object.prototype.toString.call(true) ; // [object Boolean] Object.prototype.toString.call(undefined) ; // [object Undefined] Object.prototype.toString.call(null) ; // [object Null] Object.prototype.toString.call(new Function()) ; // [object Function] Object.prototype.toString.call(new Date()) ; // [object Date] Object.prototype.toString.call([]) ; // [object Array] Object.prototype.toString.call(new RegExp()) ; // [object RegExp] Object.prototype.toString.call(new Error()) ; // [object Error]
BFC
块格式化上下文,BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素
如何创建BFC:
1、display为inline-block table-cell flex
2、浮动元素float不是none
3、定位元素:absolute、fixed
4、overflow除了visible以外的值
BFC的作用:
1、避免margin重叠(放在不同的BFC)
2、避免高度塌陷
3、包裹浮动元素
BFC布局规则:
1、在BFC中,盒子(块级元素)会在垂直方向排列
2、box垂直方向的距离有margin决定,同一个BFC的margin会重叠(以最大值为准)
https://blog.csdn.net/sinat_36422236/article/details/88763187
https://www.cnblogs.com/chen-cong/p/7862832.html
清除浮动
1、clear:both(通过after伪元素清除浮动)
2、父级div设置overflow:hidden(触发BFC)
async和await
async函数返回一个Promise对象
await用于等待一个异步函数执行结果
这两个配合使用能使得代码看起来更像同步代码
await处理错误
把await的代码放在try..catch块中或者使用catch注册函数
async function MyAsync() { try{ await doSomething() } catch(err) { console.log(err) } await doAnotherThing().catch((err) => { console.log(err) }) } // 以上两种方法皆可以
在数组中找到两数之和等于n的下标
利用二分查找
// var arr = [0, 5, 4, 1, 3, 2, 3] function findN(arr, n) { arr = arr.sort() for (let i = 0; i <= parseInt(arr.length / 2); i++ ) { let result = binarySearch(arr, n - i) if (result !== -1) { console.log(`下标${i}值${arr[i]}, 下标${result}值${arr[result]}`) } } } findN(arr, 6)
实现Promise.all
function PromiseAll(arr) { let result = [] return new Promise((res, rej) => { for (let i = 0; i < arr.length; i++) { let p = arr[i] p.then(r => { result.push(r) if (result.length === arr.length) { res(result) } }, rej) // 有一个失败就reject } }) } let p1 = new Promise((res, rej) => { res(3) }) let p2 = new Promise((res, rej) => { res(2) }) PromiseAll([p1, p2]).then(res => { console.log(res) })
主要是需要为每个promise注册then回调函数,在每个回调函数里判断下结果数组的长度是否和promise对象数组的长度相同,如果相同的时候就可以resolve了,注意Promise.all返回的是一个promise
JS事件循环
同步任务先进入主线程,异步任务进入EventTable并注册回调函数进入事件队列,当主线程中的任务执行完毕后,主线程会进入事件队列中取出回调函数执行
XSS和CSRF
CSRF:跨站请求伪造,攻击者诱导用户访问危险网站,在危险网站伪造用户请求,服务器以为是合理请求;
XSS:跨站脚本攻击。攻击者将恶意代码植入到页面中,用户访问页面时会自动执行恶意代码
大数求和
function addNum(a, b) { let n = a.split('').reverse() let m = b.split('').reverse() let maxLength = Math.max(n.length, m.length) let p = 0 // 进位 let result = [] for (let i = 0; i < maxLength; i++) { let curN = n[i] || 0 let curM = m[i] || 0 let temp = Number(curN) + Number(curM) if (p) { temp += p } p = temp >= 10 ? 1 : 0 result.push(temp % 10) if (i === maxLength) { if (p) { result.push(1) } } } return result.reverse().join('') } console.log(addNum('122665522626555223', '9728728727827827')) console.log(addNum('555223', '11111111111927827'))
Cookie的弊端
1、每个特定的域名下最多生成20个cookie
2、最大存储空间为4K
3、安全性问题。被恶意获取到时他人只需原样转发cookie便可以达到目的
CSS中 link 和@import 的区别是?
link是HTML标签,无兼容性问题
@import是CSS提供的,需要IE5以上才能识别
页面加载时link会同时被加载,而import会在页面被加载后再加载
CSS 选择符有哪些?哪些属性可以继承?优先级算法如何计算?
css选择器:
- id选择器#id
- 类选择器.class
- 属性选择器a[href="bb"]
- 后代选择器h2 p
- 子代选择器div > p
- 相邻选择器div + h1
- 伪类选择器a:hover
- 标签选择器div,p
可继承样式:
- font-size
- font-family
- color
不可继承样式:
- margin
- padding
- border
- width
- height
优先级:
- !important > 内联 > id > class > tag
- !important比内联优先级高,内联优先级比id高
HTML语义化
1、当样式丢失时,页面能够呈现出清晰 的结构
2、有利于SEO搜索爬取有效信息
3、方便其他设备解析(屏幕阅读器、盲人阅读器)
new操作符的作用
1、创建空对象,并将this指向该对象
2、属性和方法被加入到this指向的对象中
3、最后隐式返回this
深拷贝与浅拷贝
区别:
浅拷贝只拷贝基本类型的值,如果对象中属性值为引用类型,也只是复制指针,而深拷贝能另外创造一个一模一样的对象。以下代码就是浅拷贝
var obj = { p: { name: 1 }, q: 2 } var newObj = Object.assign({}, obj) newObj.p.name = 2 console.log(obj.p.name) // 变为2
浅拷贝:
let copy = {...{x:1}} let copy2 = Object.assign({}, {x:1})
深拷贝代码:
let copy = JSON.parse(JSON.stringify({x:1})) // 递归拷贝 function cloneDeep(obj) { let newobj = obj instanceof Array ? [] : {} for (let key in obj) { newobj[key] = typeof obj[key] === 'object' ? cloneDeep(obj[key]) : obj[key] } return newobj } var a = {b: 1, c: [1,2], d: {dd: 1}, e: [{ee:1}]} console.log(cloneDeep(a))
创建对象几种方式
工厂模式:不能解决对象类型识别问题
function Person(name) { var o = new Object() o.name = name return o }
构造函数模式:缺点是每个方法都要在实例中创建一遍
原型模式:缺点是所有实例只能共享属性值
组合使用构造函数与原型模式
动态原型模式:在构造函数当中去初始化原型、
function Person(name) { this.name = name // 在构造函数中初始化原型的某些属性 if (typeof this.showName !== 'function') { Person.prototype.showName = function() { return name } } }
JS继承
原型链继承:通过改写prototype属性实现继承。该方法的缺点是无法向超类型构造函数传递参数
借用构造函数:在构造函数中,改变超类型的this指向,实现向超类型构造函数传递参数。缺点是超类型的原型中的方法不可复用
组合继承:结合了原型链继承和借用构造函数的优点,缺点是调用了两次构造函数
function superType(name) { this.name = name } superType.prototype.sayName = function() { console.log(this.name) } function subType() { superType.call(this, 'haha') // 继承属性 } subType.prototype = new superType() // 继承方法 var a = new subType() console.log(a) a.sayName() a.name = '123' a.sayName()
原型式继承:借住已有对象创建新对象,它不需要为新对象额外创建自定义类型
实际上改写prototype时执行了一次浅复制。缺点是会共享引用类型值的属性
function create(obj){ function F() F.prototype = obj return new F() }
类型转换比较
undefined | |
---|---|
null | true |
[] | false |
{} | false |
'' | false |
0 | false |
false | false |
null | |
---|---|
undefined | true |
'' | false |
[] | false |
P | false |
0 | false |
[] | |
---|---|
{} | false |
'' | true |
0 | true |
false | true |
'' | |
---|---|
0 | true |
{} | false |
false | true |
false | |
---|---|
0 | true |
{} | {} ==false报错,而false=={}为false |
{} | |
---|---|
0 |
相等操作符的比较
==操作符会将操作数进行数据类型的强制转换
1、如果有操作数为布尔值,则布尔值转为数字,false转0,true转1
2、如果有操作数为字符串,则字符串转为数字
3、如果有操作数为对象,则调用valueOf, 没有valueOf就有toString,都没有就返回NaN
反正就是都转为数字来进行比较
对象转字符串或数字
var a = { toString: () => '1', valueOf: () => 0 } a == '1' // false a == 0 // true var b = { toString: () => '1' } b == '1' // true
对象转字符串:
1、判断对象是否有toString,有则调用
2、没有toString,判断有无valueOf,有则调用
3、都没有,报错
对象转数字:
1、判断有无valueOf,有则调用
2、没有valueOf,判断有无toString,有则调用
3、都没有。报错
HTTP各版本
HTTP/1.1:
1、持久连接:只要任意一端没有提出断开连接则保持TCP连接状态,这样不用每次发请求都建立并断开TCP连接
2、管道化连接:不需要等待前一个事务(HTTP请求与响应)完成,就可以发送多个请求
Http1.1:利用Connection保持连接
Connection:Keep-Alive
Keep-Alive:max=5,timeout=120
以上两行,connection指明该Http1.0需要保持持久连接,keep-alive选项则 规定了最多max个事务保持连接,空闲timeout秒后断开
在HTTP1.1中改进了keep-alive,用了更好的设计,称之为持久连接,他是默认的。当客户端和服务器可以在报文中添加connection:close对连接进行关闭
Cookies机制
cookies分为会话cookies和持久cookies,当没有设置max-age或者expires来扩展过期时间的时候,那么它就是会话cookies。
会话cookies:当用户退出浏览器的时候,会自动删除
持久cookies:存储在硬盘里
工作机制
服务器在Http响应头中添加set-cookie选项,如set-cookie: id="123"; domain:"joes-hardware.com",键值对之间用分号隔开。
浏览器收到set-cookies选项,会进行存储,并在每次请求中自动添加cookies选项
Domain属性
Set-cookie中添加domain属性,指定哪些域可以查看这些cookies。
例如:domain=".airtravel.com" 指明以.airtravel.com结尾的站点都可以查看
域名相同,端口不同的站点都可以共享cookies
缓存机制
因为某些资源变动较少,服务器可以允许客户端对资源缓存到硬盘中,以此减少网络资源流量的传输,提高响应速度
1、通过Cache-Control:max-age=100 来设置100秒后缓存过期,或者Expires设置绝对过期日期例如2020年3月5日18:00时资源过期。
2、当资源过期后,客户端要询问服务器资源是否已修改,如果服务器再验证发现资源已修改,则返回已修改的资源给客户端。服务器再验证发现没有修改的话,那么在Http响应中更新缓存过期时间即可
通过两个条件首部进行条件验证:
1、If-Modified-Since:\:这个首部通常配个Last-Modified一起使用,它指明了资源最后修改时间,如果时间对不上,说明资源已经修改了
2、If-None-Match::这个首部通常配合ETag进行使用,它指明了资源的序列号,如果序列号对不上,说明资源已经修改了
当服务器再验证发现资源没修改时,返回304状态码(Not Modified)资源未修改
no-store和no-cache
在Cache-Control选项里可以选这两个值
no-store:彻底禁用缓存,浏览器将不缓存资源
no-cache:不缓存过期资源。使用缓存前强制向服务器发送请求进行再验证对比ETag,只有服务器返回304未修改才使用缓存
虚拟DOM
虚拟DOM其实就是用JavaScript去模拟真实的DOM,当DOM变化的时候,先在虚拟DOM上进行新旧对比,然后将差异部分更新到真实的DOM上。
Vue中key值的作用
key值主要出现在使用v-for的时候,需要为每个渲染元素指定key值。当v-for更新列表时,默认采用就地复用的策略。如果顺序被打乱,vue不会移动DOM元素来匹配,而是用就地复用每个元素。key的作用是为了更高效的更新虚拟DOM
为每个元素指定key值,以便vue能跟踪节点的身份
https://www.jianshu.com/p/4bd5e745ce95
Vue组件通信
1、props和emit向父组件派发事件来进行传值。
2、vuex:这是vue配套的状态管理插件,通过抽离部分状态为全局状态,使整个数据流变得清晰可追踪。
Vue的prop单向数据流
父级prop的更新将向下流动到子组件中,反过来不行。但是如果传入的prop是引用类型比如数组或者对象,那么子组件对prop的改动会影响父组件的状态
vuex有哪几种属性
state、getter、mutation、action、module
vue中的v-model
v-model实际上是默认利用了名为value的prop属性和名为input的事件,通过model选项来指定prop和事件的名称
Vue.component('base-checkbox', { model: { prop: 'checked', event: 'change' }, props: { checked: Boolean }, template: ` <input type="checkbox" v-bind:checked="checked" v-on:change="$emit('change', $event.target.checked)" > ` })
vue的插槽
使用插槽可以将组件内的slot替换为任何模板代码
<navigation-link url="/profile"> Your Profile </navigation-link>
然后你在 <navigation-link>
的模板中可能会写为:
<a v-bind:href="url" class="nav-link" >
注意:如果组件内没有slot元素,那么组件起始标签和结束标签之间的任何内容都会被抛弃
当含有多个插槽时,为每个插槽指定一个name,这是就可以指定希望往哪个具名插槽内添加模板代码了
各种跨域技术
JSONP:利用script没有跨域限制,来对服务器发起请求。
function jsonp(url, params, callback) { return new Promise((res, rej) => { let script = document.createElement('script') window[callback] = function(data) { // 注册callback函数 res(data) document.body.removeChild(script) } let arrs = [] for (let key in params) { arrs.push(`${key}=${params[key]}`) } arrs.push('callback='+callback) script.src = `${url}?${arrs.join('&')}` document.body.appendChild(script) }) } jsonp('http://localhost:3000/api', { num: 123 }, 'show').then(data => { console.log(data) }) // http://localhost:3000/api?num=123&callback=show // callback名为show
CORS:跨域资源共享
后端需要设置Access-Control-Allow-Origin来设置允许跨域的源。
前端发起请求需要自动携带Origin字段
postMessage:跨文档消息传递。允许多窗口间的消息传递
例如父页面与iframe的通信
Websocket:这是一个全双工的通信,是持久化的协议。HTTP返回101状态码来更改为upgrade选项中指明的协议
Node代理服务器:同源策略只针对浏览器,而服务器与服务器之间没有同源限制。开启一个Node服务器作为代理,设置CORS跨域资源共享,接受到前端请求后转发给服务器,并将服务器返回的数据转发给前端。webpack中就提供了后台转发的功能
判断宿主对象
宿主对象:由宿主环境提供的对象,在浏览器中是window对象及其子对象(location、document、navigator、history、screen),在nodejs中是global对象及其子对象
try { if (window) { console.log('window') // 在浏览器 } } catch (e) {} try { if (global) { console.log('global') // 在nodejs } } catch(e) {}
移动端原生事件
orientationchange:设备纵横变换事件
触摸事件:
- touchstart:触摸屏幕时触发
- touchmove:滑动时连续触发
- touchend:触摸结束
- touchcancel:停止跟踪触摸
手势事件:
- gesturestart:手势开始
- gesturechange
- gestureend
箭头函数
箭头函数没有this、arguments、new.target等属性
他只能通过作用域查找来寻找this
如果被包含在非箭头函数时,自动绑定到最近一层非箭头函数。否则自动绑定到全局对象
class和构造函数的区别
1、class不能被提升
new Foo(); // 报错 class Foo{}
2、class中所有方法默认是不可枚举的
3、class必须使用new调用否则会报错
in和hasOwnProperty
in:能够检测对象是否含有某个属性。它能够检测到继承的属性
hasOwnProperty:检测对象自身是否含有某个属性。它不能检测到继承的属性
进程和线程
进程:是程序的一次执行过程。是系统分配资源的基本单位。
线程:是任务调度和执行的基本单位,线程之间可以共享内存。
线程是进程的一部分,一个线程只能属于一个进程,而一个进程可以有多个线程。
进程切换的开销比较大,线程之间切换开销比较小。
检测是否通过new调用
new.target元属性
该属性是来自ES6中,旨在检测一个构造函数是否通过new调用
如果不是通过new调用,new.target为undefined
如果是new调用,new.target为该构造函数
function Person(name) { if (new.target === Person) { console.log('new 调用') } else if (new.target === undefined) { console.log('请通过new调用') } }
通过this instanceof [[constructor]]判断
当使用new的时候,new会创建新的对象,并将this指向该对象
function Person(name) { if (this instanceof Person) { console.log('new 调用') } else { console.log('请通过new调用') } }
this指向问题
情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。
情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
情况3:如果一个函数中有this,这个函数被多个对象包含,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
this永远指向的是最后调用它的对象
https://www.cnblogs.com/pssp/p/5216085.html
一句话:如果一个函数没有被一个对象所调用,那么它指向window。只有被对象所调用,才会指向对象
HTTP常见状态码
302:临时重定向。展示最新的网页,但网址仍然是旧网址
301:永久重定向。网页将永久转移到另一个网址上
https://www.cnblogs.com/unixcs/p/10668960.html
304:未修改
401:未验证登录
403:拒绝访问
500:服务器错误
五层网络模型
自查哈
#笔试题目#