1、js数据类型是什么?
八种:number string boolean null undefined object symbol bigInt
其中Object引用数据类型有:array date function
2.变量增加是什么?
(1)分变量提升和函数提升
(2)变量提升只能提升到当前作用域的顶部,不会超过作用域
(3)函数声明会增加,但函数表达式不会增加
(4)具名函数表达式也不会改善
3、说说闭包
(1)封闭式域链的作用包括:自己的,包括他的函数和全局
(2)外层函数每次执行都会创建一个新的内存地址,不同执行获得的闭包不会影响
(3)其实函数不是在当前词法作用域执行的,而是在其他词法作用域执行的,会在自己的作用域链上添加当前的活动对象
4、==和===的区别
(1)===值和类型必须相同,例外 NaN === NaN ---false
(2)==会有一些类型的转换
null == undefined ---true
对比字符串和数值,字符串转数字
布尔值转 0 1 再比较
根据对象转基础类型,根据对象转基础类型tostring
5、说一说this
(1)默认绑定:独立函数调用,默认绑定到window
(2)隐式绑定:作为对象对象的方法,this是这个对象,但注意隐式丢失,函数别名
(3)new绑定:构造函数中的this,指向实例
(4)可通过 call aplly bind做一个显式绑定
(5)运行时绑定普通函数,定义时绑定箭头函数
6.数组和对象的遍历有哪些方法?
(1)数组:for循环、for of、forEach、map(最后两者的区别是:map可以直接return提供所需的数据,但请注意,新数组不会改变原数组,forEach不能return所以在遍历时不能直接操作原始数据,但是!回调可以改变,回调函数可以传输(item, index, arr),forEach返回值Undefined)
(2)对象:Obejct.keys Object.values for-in
箭头函数和普通函数的区别?
(1)箭头函数不能用作构造函数
(2)箭头函数中的this是父级函数的上下文
(3)没有箭头函数arguments对象,可用...args 获取到(args是参数组)
(4)箭头函数不能通过call apply bind绑定会忽略第一个参数,但可以传输第二个参数序列
(5)无原型属性
(6)不能当作Generator不能使用函数yield关键字
8何解决跨域问题?
(1)jsonp,利用script中的src标签告诉服务器前端回调函数的名称, 服务器通过拿到回调函数名,让改函数以执行的状态(eg: fn( data ))返回并将数据放入参数中

(2)document.domain iframe(主域名必须相同)
使用父窗frame标签嵌入子窗口,然后强制设置父窗和子窗的基本主域名 document.domain = "fordmc.com",这样,子集就可以通过了window.parent拿到父窗口的数据
(3)location.hash iframe
通过改变 iframe标签的src里面的hash然后在子窗监控值onhashchange可以获得事件location.hash值
(4)window.name iframe
利用window.name只要设置了这个属性,不管url如何改变这个值不会改变跨域
(5)window.postMessage
(6)CORS:access-control-allow-origin,要携带cookie还要加 access-control-allow-credentials
(7)配置proxy代理
(8)websocket:本质上没有使用Http因此,对响应头没有跨域限制
前端:
后端:
(9)Nigx反向代理:后端配置
9、ES6新增语法
对象简写,数组对象的解构赋值,let const(块级作用域,临时死区,const如果引用数据类型,常量只反映一层),模板字符串...rest...展开语法、symbol、bigInt、还有一些数组API,promise, async await,class关键字
10、attribute property区别
attribute是标签属性,property是dom属性通过某个dom的点运算取到,attribute通过getAttribute和setAttribute 这种API但是dom有原生的attribute,没有自定义
11、内存泄漏
常见的内存泄漏原因:意外的全局变量,无清空定时器和回调函数,关闭包装,脱离引用dom
12、script标签的 async defer有什么作用和区别?
(1)相似之处:两者都是为了不堵塞后续资源的加载,并将异步加载执行
(2)不同点:
async它是在加载完成后立即执行的,所以谁先加载,谁就没有顺序执行window的load事件前执行
defer按脚本顺序执行,会在document.DOMContentLoaded事件前执行
13、数组常用API
concat, copywithin, entries, every, fill, filter, find, findIndex, flat, flatMap, forEach, from, includes, indexof, isArray, join, keys, map, of, pop, push, shift, unshift, reduce, reverse, slice, splice, sort, some
14.如何实现深拷贝?
(1)深拷贝有哪些方法:es6的..、Json.parse(Json.stringfy(待复制对象),数组中slice, concat
(2)手写深拷贝
function deepCopy(obj) { if(typeof obj == 'object'){ let result = Array.isArray(obj) ? []:{} for(let key in obj){ result[key] = typeof obj[key] == 'object' ? deepCopy(obj[key]):obj[key] } return result }else{ return obj } } let arr = [1,2,3] let ans = deepCopy(arr) ans.push(4) console.log(ans, 'ans'); // [ 1, 2, 3, 4 ] ans console.log(arr, 'arr'); // [ 1, 2, 3 ] arr
什么方法可以实现异步编程?
回调:回调地狱
promise: then支持链式调用,如果返回promise那then函数返回的promise装态完全由promise如果决定不返回,Promise都会包装成成成功的promise返回,如果没有返回值,也会包装成成功promise只有成功的价值undefined
generator(通过调用返回迭代器next方法逐步实施,传入next生成器函数中的参数可以提供yield,同样类似于return,写到 yield后面的值也会返回next()函数,里面的value, next()函数的返回值是这个eg: { done: false, value: undefined })
async await: async包装的函数返回一个promise, 遇到await停止执行函数等await 后面的promise resolve然后直接得到值,本质上是语法糖,然后async await不能捕获异常,只能通过try catch来捕获,其实就是把 * 替换成了 async,把yeild替换成了await
前端性能优化
减少http请求次数,DNS查询次数,避免重定向,懒加载,减少操作dom、css避免使用计算属性CDN、使用expires cache control 配置Etags、合并压缩代码,使用精灵图打开Gzip acceptEncodng contentEncoding、配置webpack减少单独的模块解释,一些loader(babel排除node modules)的使用排除某些不需要的文件夹、缓存Lodaer的结果、多线程打包、热替换、动态加载组件。
18、说下作用域链
函数作用域、全局作用域、变量查找会先在当前作用域找、找不到就往上找直到window
Let const具有块级作用域 也就是说不是整个函数内声明都能找到 严格按照顺序不会有变量提升
eval能够改变词法作用域,传入eval的变量可以在eval执行的位置找到,尽管定义的时候并不是在执行eval位置定义的
with能够传入一个对象,然后作用域就会变成传入的对象,如果操作了传入对象没有的属性,小心内存泄漏
function test(str) {
eval(str);
console.log(a + b); //输出3
}
test("var a = 1; var b = 2");
function test (obj) {
with(obj) {
a = 2;
}
}
var o1 = {
a: 3
}
var o2 = {
b: 3
}
test(o1);
console.log(o1.a); //2
test(o2);
console.log(o2.a); //undefined
//接下来我们输出一下全局a的值,我们会发现a泄漏到了全局变量上
console.log(a);//2
19、原型链
实例的隐式原型指向构造函数的显示原型、构造函数的原型有个constructor属性指向构造函数
1、Foo(),Object(),Function()这三个构造函数,是通过构造函数Function()构造而来,所以Foo(),Object(),Function() 是 Function()的实例对象,因此Foo(),Object(),Function()的__proto__属性,都指向Function.prototype
2、f1,O1分别是Foo(),Object()的实例对象,因此f1,O1的__proto__属性分别指向Foo.prototype和Object.prototype
3、所有函数的原型对象都是一个Object空对象,因此Function.prototype实际上也是一个Object实例对象,所以Function.prototype.__ptoro__指向构造Function.prototype这个Object空对象的prototype,即 Object.prototype,同理:Foo.prototype.__ptoro__指向构造Foo.prototype这个Object空对象的prototype,即 Object.prototype.
20、继承的几种方式
(1)类式继承:父类实例对象挂载到子类prototype上
(2)构造函数继承:构造函数里面调用父类的构造函数通过call把当前this传进去
(3)组合继承:结合前两种继承
(4)原型式继承:在父类构造函数传入一个对象,声明过渡函数,设定过渡函数的原型为这个对象,然后生成一个此构造函数的实例,然后返回。
(5)寄生式继承:对原型继承的第二次封装,并且封装过程中进行方法属性的拓展
(6)寄生组合式继承:寄生继承+构造函数继承
21、js垃圾回收机制
(1)标记清除法:“进入环境” "离开环境"进行标记,离开环境的清除即可
(2)引用计数法:变量被引用一次就计数加一,为0代表没被引用,清楚即可
v8内存分两个区域:新生代和老生代,不同的区域采用不同的垃圾回收机制
(1)新生代:这个区域相对较小但是垃圾回收特别频繁。采用scavenge算法进行垃圾回收,
scavenge算法:把内存分两个区域,from, to,首先放入from, 然后回收时,如果from区域有活动对象,就会复制到to区域进行保存,然后清空from区域,然后 from, to角色切换。
(2)老生代:新生代中的对象在存活一段时间后就会被转移到老生代内存区(转移条件:是否经历过scavenge算法,to空间内存占比是否已经超过25%),此区域垃圾回收频率较低。采用:标记清除 和 标记整理算法进行垃圾回收
标记清除算法:垃圾回收期会在内部构建一个根列表(全局对象,函数局部变量和参数,当前前嵌套调用链上的其他函数的变量和参数),从根节点出发可以访问到的标记为活动的,访问不到的视为垃圾。
标记整理算法:就是遍历的时候把活动对象往堆内存的一端移动。
(引入增量标记去解决垃圾回收的全停顿问题)
参考:一文搞懂V8引擎的垃圾回收_gxhlh的博客-CSDN博客_v8垃圾回收
23、为什么0.1+0.2>0.3
JS中的number类型是采用双精度浮点数规范,采用64位存储,1位符号位,11位指数偏移值,剩下52位分数值,0.1在转换成二进制的时候会形成无限循环数,但是只能保留52位,因此存在一个四舍五入,因此导致大于0.3,解决方案就是Number.EPSILON(一个无线小的数)
24、判断数据类型的几种方法
(1)typeof: 输出的是数据类型的字符串,基本数据类型能分辨出来,typeof function 是 ‘function’,然后引用数据类型是 object,typeof null也是object(原因是最开始使用的是低位存储变量类型信息,000开头代表对象,null全是0所以判断为object) typeof undefined 是undefined
(2)instanceof: 主要用于判断引用数据类型是谁那种构造函数的实例来判断类型,会沿着原型链查找,如果一直沿着原型链都查找不到那就返回 false
实现以下instanceof
function myInstanceOf (L, R) {
let LP = L.__proto__
let RP = R.prototype
while (true) {
if (LP === null) {
return false
}
if (LP === RP) {
return true
}
LP = LP.__proto__
}
}
let ans = myInstanceOf({ a: 1 }, Array) // false
let ans2 = myInstanceOf([1, 2], Array) //true
console.log(ans);
console.log(ans2);
(3)constructor: 不稳定 重写prototype会丢失,null undefined无效
(4)Object.prototype.toString这个能最准确判断出是什么类型
25、手写call apply bind
call
Function.prototype.myCall = function(context, ...args){
if(context === undefined || context === null){
context = window
}else{
context = Object(context) // 如果传入的为原始值,要把上下文包装成对象
}
const fn = Symbol('fn') // 只所以使用symbol 是无法确定传入的context会不会出现同名属性情况
context[fn] = this
const res = context[fn](...args)
delete context[fn]
return res // 返回的是函数执行结果
}
const obj = {a:1}
function test(val){
console.log(this.a + val)
}
test.myCall(obj, 3)
apply
Function.prototype.myApply = function(context) {
if(typeof this !== 'function') {
throw new TypeError('Error')
}
context = context || window
context.fn = this
let result
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
let obj = {
c:1
}
function test(a,b){
console.log(a+b+this.c);
}
test.myApply(obj, [1,2]) // 4
bind
Function.prototype.myBind = function(context) {
if(typeof this !== 'function') {
throw new TypeError('Error')
}
const _this = this
args = [...arguments].slice(1)
return function F() {
if(this instanceof F){
return new _this(...args, ...arguments)
}
return _this.apply(context, args.concat(...arguments))
}
}
let obj = {
c:1
}
function test(a,b){
console.log(a+b+this.c);
}
let foo = test.myBind(obj)
foo([1,2]) // 4
26、字面量创建对象和New创建对象有什么区别?
1.字面量创建对象不会调用Object构造函数,而new是会实现一个原型链的继承
2..new关键字内部做了什么
(1)创建一个空对象
(2)将空对象的隐式原型指向构造函数的显示原型
(3)将this指向实例对象
(4)判断构造函数有没有返回值,如果有就返回,没有就返回之前创建的空对象
3..手写一个new
function myNew(fn, ...args){
let obj = {}
obj.__proto = fn.protptype
let result = fn.apply(obj, args)
return result instanceof Object ? result : obj
}
function Add(a,b) {
this.a = a
this.b = b
}
let instance = myNew(Add, 1,2)
console.log(instance.a); // 1
console.log(instance.b); // 2
4.new和Object.create()创建的对象有什么区别?
new创建的对象是会继承构造函数的属性和方法的,但是Object.create()创建出来的实例自身不会继承属性和方法,只是会把原型继承下来,所以构造函数的方法也能够找到(是通过原型找到的)
27、为什么JS是单线程?
js本身就是一种脚本语言实现浏览器的一些交互操作dom 如果多线程的话就会冲突,但是后来实现了web worker可以实现多线程,但是有些限制,不能操作dom 不能使用 window document对象,不能alert confirm等等,通过postMessage onMessage等与主线程去通信
28、手写一个promise
终于写完了.....
// 手写一个promise
// 面试题版本
class myPromise {
constructor(excutor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolveCallbacks = []
this.onRejectCallbacks = []
const resolve = (value) => {
if (this.state == 'pending') {
this.state = 'fulfilled'
this.value = value
this.onResolveCallbacks.forEach(fn => fn())
}
}
const reject = (reason) => {
if (this.state == 'pending') {
this.state = 'rejected'
this.reason = reason
this.onResolveCallbacks.forEach(fn => fn())
}
}
try {
excutor(resolve, reject)
} catch (error) {
reject(error)
}
}
then (onFulfilled, onRejected) {
if (this.state == 'fulfilled') {
onFulfilled(this.value)
}
if (this.state == 'rejected') {
onRejected(this.reason)
}
if (this.state == 'pending') {
this.onResolveCallbacks.push(() => onFulfilled(this.value))
this.onRejectCallbacks.push(() => onRejected(this.reason))
}
}
}
let p = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(200)
}, 2000)
})
p.then(res => {
console.log(res, '1');
})
p.then(res => {
console.log(res, '2');
})
// 详细版
class mypromise {
constructor(executor) {
// 存状态
this.state = 'pending'
// 存成功的值
this.value = undefined
// 存失败的值
this.reason = undefined
// 成功和失败的事件队列
this.onFulfilledCallback = []
this.onRejectedCallback = []
// resolve逻辑
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
// 等真正异步结束调resolve的时候,在依次执行成功回调队列
this.onFulfilledCallback.forEach(fn => fn())
}
}
// reject逻辑
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
// 等真正异步结束调resolve的时候,在依次执行失败回调队列
this.onRejectedCallback.forEach(fn => fn())
}
}
// 立即执行传入promise的函数:因此promise参数函数的函数体内语句是同步的
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then (onFulfilled, onRejected) {
// 实现链式调用,因此要返回一个promise
let promise2 = new mypromise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 异步是因为,如果同步的话,resolvePromise传入的promise2还拿不到
setTimeout(() => {
try {
// 为了下一次的then调用能够拿到上一次then函数里面的返回值
let x = onFulfilled(this.value)
// 为了实现then函数里面返回promise的情况
resolverPomise(x, resolve, reject, promise2)
} catch (error) {
reject(error)
}
}, 0)
}
if (this.state === 'rejected') {
setTimeout(() => {
try {
// 为了下一次的then调用能够拿到上一次then函数里面的返回值
let x = onRejected(this.reason)
// 为了实现then函数里面返回promise的情况
resolverPomise(x, resolve, reject, promise2)
} catch (error) {
reject(error)
}
}, 0)
}
// 处理异步得时候,就把要then里面要执行得函数暂存到队列
// 直到调用了 resolve或者reject再去执行then里面的函数
if (this.state === 'pending') {
this.onFulfilledCallback.push(() => {
setTimeout(() => {
try {
// 为了下一次的then调用能够拿到上一次then函数里面的返回值
let x = onFulfilled(this.value)
// 为了实现then函数里面返回promise的情况
resolverPomise(x, resolve, reject, promise2)
} catch (error) {
reject(error)
}
}, 0)
})
this.onRejectedCallback.push(() => {
setTimeout(() => {
try {
// 为了下一次的then调用能够拿到上一次then函数里面的返回值
let x = onRejected(this.reason)
// 为了实现then函数里面返回promise的情况
resolverPomise(x, resolve, reject, promise2)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promise2
}
}
function resolverPomise (x, resolve, reject, promise2) {
// 为了捕获return自己的错误情况
if (x === promise2) {
console.error('循环引用')
}
if (x instanceof mypromise) {
x.then((value) => {
resolve(value)
}, err => {
reject(err)
})
} else {
resolve(x)
}
}
// 四个API
mypromise.resolve = function (val) {
return new mypromise((resolve, reject) => {
resolve(val)
})
}
mypromise.reject = function (reason) {
return new mypromise((resolve, reject) => {
reject(reason)
})
}
mypromise.race = function (promises) {
return new mypromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
}
})
}
mypromise.all = function (promises) {
let arr = []
let count = 0
function processData (index, data, resolve) {
arr[index] = data
count++
if (count === promises.length) {
resolve(arr)
}
}
return new mypromise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(res => {
processData(i, res, resolve)
}, reject)
}
})
}
// 测试代码
let p1 = new mypromise((resolve, reject) => {
setTimeout(() => {
resolve(100)
}, 1000)
})
let p2 = new mypromise((resolve, reject) => {
setTimeout(() => {
resolve(200)
}, 2000)
})
let p3 = new mypromise((resolve, reject) => {
setTimeout(() => {
resolve(300)
}, 3000)
})
mypromise.all([p1, p2, p3]).then((res) => {
console.log(res)
})
29、宏任务、微任务执行顺序的一些例题
// // 第一题
console.log(1)
setTimeout(()=>{
console.log(2)
},0)
Promise.resolve().then(()=>{
console.log(3)
})
console.log(4)
// // 执行顺序:1 4 3 2
// // 第二题
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
new Promise((resolve, reject) => {
console.log(3);
resolve()
}).then(()=>{
console.log(4);
});
console.log(5);
// // 执行顺序:1 3 5 4 2
// // 第三题
console.log(1)
setTimeout(function(){
console.log(2);
let promise = new Promise(function(resolve, reject) {
console.log(3);
resolve()
}).then(function(){
console.log(4)
});
},1000);
setTimeout(function(){
console.log(5);
let promise = new Promise(function(resolve, reject) {
console.log(6);
resolve()
}).then(function(){
console.log(7)
});
},0);
let promise = new Promise(function(resolve, reject) {
console.log(8);
resolve()
}).then(function(){
console.log(9)
}).then(function(){
console.log(10)
});
console.log(11)
// // 执行顺序:1 8 11 9 10 5 6 7 2 3 4
// 第四题
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
// 执行顺序: 1 7 6 8 2 4 3 5 9 11 10 12
30、vue中webpack devserver 代理如何配置?
proxy里面配置,target,ws,changeOrigin,pathRewrite等
devServer: {
proxy: {
'/api': {
//要访问的跨域的域名
target: 'http://www.test.com',
ws: true, // 是否启用websocket
changOrigin: true, // 开启代理
pathRewrite: {
'^/api': ''
}
}
}
}
注意:pathRewirite如果不写,就只是修改代理域名,如果写会修改代理的域名及后面的路径
31、关于模块化commonjs ES6有什么区别?
commonjs:
以下代码文件名:a.js
// commonjs模块化
function funA () {
console.log('a');
}
function funB () {
console.log('b');
}
// 导出
// 分别导出
exports.funA = funA
exports.funB = funB
// 统一导出
module.exports = { funA, funB }
以下代码文件名:b.js
// 分别导入
const funcs = require('./a.js')
funcs.funA() // a
funcs.funB() // b
// 统一导入
const { funA, funB } = require('./a.js')
funA() //a
funB() //b
ES6:
以下代码文件名:a.mjs
// ES6模块化
//分别导出
export function funA () {
console.log('a');
}
export function funB () {
console.log('b');
}
// 统一导出
export { funA, funB }
// 默认导出
export default { name: 'hui' }
以下代码文件名:b.mjs
// 分别导入
import { funA, funB } from "./a.mjs";
funA()
funB()
// 统一导入
import * as funcs from './a.mjs'
funcs.funA()
funcs.funB()
// 默认导入
import name from './a.mjs'
console.log(name);
总结:
(1)es6模块化就是通过export关键字进行分别导出,通过 export { 变量标识符1,变量标识符2 }进行统一导出,通过 export defalt { key: value }进行默认导出(默认导出可匿名)。但是请注意变量标志符这几个字,也就说统一导出的并不是实际变量,export { key: value }这种写法错误!!里面只能写变量标识符。但是默认导入的是一个真实的对象,里面是可以写key:value的。然后对于导入的话,就用import关键字进行导入就好,有两点,一是可以起别名,二是统一导入和默认导入可以一起。
(2)commonjs模块化就是给exports对象添加属性来导出,分别导出就给exports添加不同的属性,统一导出就给 module.exports = { } 这个对象里面统一添加key:value。导入的话 就用require引入一下就好。注意一下exports 和 module.exports,其实都是指向同一块引用,所以别随便给 module.exports重新赋值一个对象,会导致导入的对象丢失。
(3)区别:commonjs是加载后立即执行,es6是静态加载,即运行时加载,node默认支持commonjs,如果node里想用es6,文件后缀名得是 .mjs。然后es6还支持再导出!export { a } from ''./a.js'这种,还支持动态导入,返回一个promise 即 import('a.js').then(a => { let b = a.func1 })这种,好高级!终于写完了这一条....
32、TCP三次握手,为啥不是四次,两次?
三次握手:
四次挥手:
(1)为什么是三次而不是两次:
因为第一次挥手:客户端发送SYN给服务器,服务器可以确定客户端发送消息和自己接收消息的能力
第二次挥手:服务器发SYN+ACK给客户端,客户端可以确认自己接收消息的能力和服务器发送消息的能力
至此,服务器并不能确认自己发送消息的能力是否正常,因此服务器需要第三次挥手来确认自己发出去的消息被正常接收。
(2)为什么不是四次呢?
没有必要,因为再多一次也并不能使链接更可靠,会造成资源浪费
(3)为什么握手是三次,而挥手是四次呢?
因为服务器接收到FIN报之后,很可能不会立即关闭socket,因此只需要一个ACK告知客户端我明白你要关闭的需求了,然后需要等到服务器端把还没发送完的数据发送完之后,再次发送FIN报,去主动断开连接,因此是四次。
33、HTTP的结构,有哪些头部字段,以及各种状态的含义
(1)结构:报文首部+空行+报文主体,其中,报文首部=请求行(或者状态行)+各种首部字段,其中请求行包含:请求方法,请求URI,http版本,状态行包含:http版本,状态码,原因短语
(2)各种首部字段:
a.通用首部字段:
cache-control: public,no-cache(请求:不要缓存的,响应:可缓存要向我确认),no- store,s-maxage(只适用公共缓存服务器),max-age,min-fresh(超过这个有效时间的就不返回了),max-stale(过期了但没超过这个时间,仍然接收),only-if-cached(是缓存的才返回),must-revalidate(会忽略max-stale),no-transform(防止缓存或代理压缩图片等行为)
connection:Upgrade(控制upgrade这个字段不再转发给代理),keep-alive(管理持久连接)
date:时间
trailer:expires(说明报文主体后记录了哪些首部字段,用于分块传输编码)
upgrade:可以升级协议版本,但仅限于邻接服务器,因此要和connection;uograde一起使用
via:记录传输路径,避免请求回环
b.请求首部字段:
accept: 告知媒体类型及优先级
authorization:用于携带认证信息,一般用于响应401返回码
from:电子邮件
host:
if-match,if-none-match: 匹配资源是否发生变化,看Etag值,无法使用弱Etag值
if-modified-since(if-unmodified-since):如果资源发生改变会响应304
if-range:次字段值如果跟Etag值或者更新资源时间一致,则匹配上了,响应206(partial content),如果没匹配上就返回所有
max-forwards:每次转发,数值减一
range:
referer:原始资源URI
c.响应首部字段
accept-range,age(这个缓存多久之前向服务器确认过),Etag(强Etag,弱Etag,弱的意思是只有发生根本改变才会变)
Location:配合3xx:redirection的响应,指定重定向的URI
vary:accept-language(只会持有相同自然语言的返回缓存,即对缓存做一个控制
d.实体首部字段
content-encoding.length....
expires:过期时间
last-modified
e.为cookie服务的首部字段
cookie:
setcookie:expires,path,domain,secure(仅在https才会发送cookie),httponly(加以限制,cookie不能被脚本访问)
(3)状态码
33、网络七层协议
34、http1.0 http1.1 http2的比较
http1.1优化内容:
(1)长连接:默认开启connection:kepp-alive,一个tcp连接可以并行多个http请求
(2)缓存优化:http1.0主要使用if-modified-since expires来控制缓存,1.1新增了Etag,if-match,if-none-match,if-unmodified-since等来优化缓存策略
(3)新增24个错误状态响应码:比如406(not acceptable),407(需要经过代理服务器授权),409(confict 状态冲突)
(4)新增请求方式:put delete trace options connect
(5)host优化:一个IP对应多个host虚拟主机
(6)增加range:充分利用带宽和连接
http2优化内容:
(1)二进制分帧:将传输信息分割为更小的消息和帧,采用二进制编码格式进行封装,兼容1.1 http1.1中的首部信息封装到headers帧中,request body封装到data帧中
(2)多路复用:允许单一http连接发起多重的请求-响应,由于二进制分帧机制,每个数据流都拆分成互不依赖的帧,并且可以乱序发送再重组(基于帧首部的流标识符)
(3)头部压缩:双方各缓存头部字段表,重复的头部字段只需要发送一次,如有变化再将变化的部分加入到header帧中即可,hpack头部压缩算法效果比之前的gzip好
(4)请求优先级:每个流可以带一个31比特的优先值,高优的帧先发送,但不绝对,以免阻塞
(5)服务端推送:可以把客户端需要的资源伴随index.html一起发送,所以静态资源通过服务器推送可极大提升速度
35、http https比较(还未完成、不是很懂)
(1)http缺点:明文通信,没有身份验证,没有完整性验证,因此https实际上就是:http+通信加密+证书+完整性保护,这些是基于SSL实现的
(2)SSL:SSL协议分两层,上层是:握手协议+密码变化协议+警告协议,下层是:记录协议(为高层协议提供数据封装压缩加密等基本功能)
SSL握手协议几个阶段:
第一阶段:clientHello(客户端支持的协议版本、客户端随机数、sessionID、加密套件、压缩方法),serverHello(服务器支持的协议版本、服务器随机数、加密套件(选客户端提供的)、sessionId、压缩算法(首选客户端提供的))。至此,两边都有两份随机数,以及确定了加密算法密钥交换和信息验证。
第二阶段:服务器单向发消息:数字证书和根CA链、服务器密钥交换、证书请求、服务器握手完成
第三阶段:客户端单向发送消息:证书、客户机密钥交换(发送预备主密钥(公钥加密的))、证书验证
第四阶段:.....
(3)https加密原理(未完成,不是很懂...)
36、浏览器存储
cookie(4k、字段有:key-value、path domain httpOnly secure expires ) localStorage(5M、持久化) sessionStorage(5M、仅限于当前窗口关了就没了) indexDb (对象方式存储)api:setItem getItem removeItem
37、get post区别
(1)get请求参数体现在url身上、而且只能用url编码,所以需要转码解析,post不会体现在url
(2)由于url大小的限制,get传输的数据量有限
(3)get产生一个TCP数据包,把header和data一起发送出去,而post产生两个数据包,第一次响应100 continue 第二次才响应200 ok
(4)get回退无害、post会再次提交请求
(5)get浏览器会主动cache 而Post不会除非手动设置
w3c标准答案:
38、对比TCP,UDP(未完成)
TCP原理,滑动窗口,拥塞窗口说清楚
39、从浏览器输入URL都发生了什么?
按照这个顺序讲,具体内容看本文其他地方
40、讲讲CDN(内容分发网)
(1)组成:
中心节点(智能调度服务器):根据传输距离、边缘节点负载情况分配最合适的边缘节点,做好边缘节点的负载均衡
边缘节点(代理服务器):对源服务器的缓存
(2)DNS和CDN的关系:
DNS有两种记录类型:域名:IP映射记录(A类型) 和 域名:域名映射记录(CNAME)
CNAME类型要访问域名应该访问哪台合适的服务器呢?就需要CDN来调度
(3)CDN工作过程
41、XSS CSRF攻击,OWASP Top10(未完成)
42、回流、重绘
回流:删除增加dom元素,元素位置发生改变、元素尺寸发生改变、页面初始渲染、浏览器窗口大小改变
重绘:仅仅是一些样式的改变,不会影响页面布局
如何减小回流重绘:可以先将元素脱离文档流、然后应用多重改变、再带回文档流。隐藏元素、应用修改、重新显示。使用文档片段在当前DOM之外构建一个子树,再把它拷贝回文档。将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后再替换原始元素
43、事件冒泡,事件捕获,事件委托
冒泡:目标元素先触发 addEventListener() 第三个参数为false
捕获:父元素先触发 addEventListener() 第三个参数为true
事件委托:利用冒泡,目标元素的事件发生也会冒泡到父元素,通过e.target判断来写逻辑就行
44、手写防抖节流
function debounce (fn, wait) {
let timer = null
return function () {
const context = this
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(context, argumens)
}, wait)
}
}
function throttle(fn, delay) {
let prev = Date.now();
return function() {
const context = this;
const curr = Date.now()
if (curr - prev >= delay) {
prev = curr;
return fn.apply(context, arguments);
}
}
}
45、函数柯里化原理(未完成)
46、requestAnimationFrame
类似于setTimeout,但是不用设置事件间隔,他的时间间隔紧跟浏览器刷新频率,把每一帧中所有dom操作集中起来在一次回流或重绘中完成,这样的话就不会丢帧,并且在隐藏或不可见的元素中requestAnimationFrame不会进行回流重绘,然后返回值是一个id,用于cancelAnimationFrame停止动画。
47、JS常见设计模式(未完成)
48、Vue双向绑定原理
有一个碎片化文档的数据劫持,在劫持下来的每个元素节点分别去compile处理,处理的时候就遇到v-model这种属性就把此元素的value值进行了一个绑定(数据来源是vm中的data),以上是简单的内容绑定,1、从view-model,vm中有个观察者类,具体就是用 Object.defineProperty写get set,起到监听的作用,然后在做碎片化处理的时候,是要即时取到最新的数据的,所以有事件回调来完成,把最新的值放到vm data里面去,2、model-view,(就是如何在改data中的数据时候,多个用到此数据的页面一起更新):订阅/发布模式(有个发布者,就是指定调啥函数名,这个函数名就是绑定在订阅者原型上的,所以一发布,订阅者就能执行响应的回调),然后具体怎么实现的:首先是需要给每个属性生成一个对象容器,然后再观察某个属性的时候的get里面将对应的一个watcher(这个watcher就绑定了是哪个节点,然后有update方法和get方法) push到这个对象容器里面去,这样就让这个容器里面装入了同一个属性用到的不同的节点且有update方法,然后再观察的时候触发set,就马上调用发布者的notify功能,触发dep里面每个watcher的update方法。
理解VUE2双向数据绑定原理和实现 - 简书
49、MVVM(感觉没啥好说的啊)
model-view-viewModel
50、Vue声明周期(父子生命周期)
父beforeCreate > 父created > 父beforeMount > 子beforeCreate > 子created > 子beforemount > 子mounted > 父mounted > 父beforeUpdate > 子beforeUpdate > 子updated > 父updated > 父beforeDestory > 子beforeDestory > 子destoryed > 父destoryed
51、Vue中nextTick
怎么用:当改变数据后,需要基于更新后的dom进行某些操作时,就写在nextTick回调那种
我使用过的场景:
(1)在做todo-list组件的时候,在点击编辑时候,需要把代办事项那一条展示为input框去编辑并且聚焦,这个Input框是根据v-show决定的,点击按钮的回调函数里刚把input的显示置为true但是,此时需要拿到更新后的input框才能做一个聚焦操作,因此用到了nextTick。
nextTick源码分析:
(1)首先nextTick是可以传两个参数: 回调函数 和 环境对象
(2)nextTick做了什么:把传入的函数放入callback数组,并且执行timerFunc函数
(3)timerFunc函数: 去判断当前环境是否支持原生Promise,原生MutationObserver,试图把回调放入微任务队列去执行,如果不支持,则检查是否支持setImmediate,不支持就用setTimeout。就是对环境进行一个降级处理,去执行flushCallbacks函数
(4)flushCallbacks函数:就是for循环执行callback队列
const callbacks = []
let pending = false
let timerFunc
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
_resolve(ctx)
}
})
if (!pending) {
pending = true
timerFunc()
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
// timerFunc
export let isUsingMicroTask = false
if (typeof Promise !== 'undefined' && isNative(Promise)) {
//判断1:是否原生支持Promise
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
//判断2:是否原生支持MutationObserver
let counter = 1
const observer = new MutationObserver(flushCallbacks)
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
//判断3:是否原生支持setImmediate
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
//判断4:上面都不行,直接用setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
52、computed和watch的区别
(1)computed是可以缓存的,只有依赖数据发生改变才会重新计算,watch不支持缓存
(2)computed是需要return拿到结果的,因此不支持异步,它返回实际上是通过调用getter,他也有setter,watch是针对某一个属性监听,然后调用回调,也不需要return什么值,因此支持异步操作。
(3)computed更适合于多对一,即一个数据依赖于多个数据,而watch适合于一对多,即一个数据改变惠影响多个数据。
53、vue-router两种模式(顺便把原生history对象那些API讲一讲区别啥的)
(1)vue-router这两种模式本质上是借助浏览器特性实现的前端路由,即为了实现SPA(单页面应用,跳转页面而不刷新的特性),然后有两种模式hash模式和history模式
(2)hash模式:原理是hashHistory对象上的push和replace方法,会触发onhashChange事件,URL会带上#,在实际发送请求的时候不会带上#后面的字符串,但是可以监听onhashChange事件,当hash值变化的时候进行响应的页面替换
(3)history模式:原理是H5新增的historyAPI,pushState和replaceState,这两个API参数分别是stateObj(这个是文档状态信息对象),title,url,会触发popState事件,来响应页面更新信息,这种模式必须要求服务器与每个url都有隐射,要不会报404,要解决这个问题可以在服务器上添加一个回退路由,当匹配不到任何资源时,提供一个index.html的资源就行了。
54、虚拟dom和Diff算法、vue中key的作用(感觉有点难,先跳过)
55、vue组件间通信方式
(1)props(父->子), $emit(子->父)
(2)ref(子->父)
(3)$bus(兄弟):用一个vue实例,提供了一个共享空间
(4)provide, inject(父->子):非响应式的
(5)$parent, $children(这是个数组且是无序的,访问的数据也不是响应式的)
(6)$attrs(除声明的props之外的属性), $listeners (所有监听器,通过v-on="$listeners"可以实现子组件继承父组件的事件)
56、Vuex中有哪些属性?
state:
getter(state, getters): 可以返回一个函数实现给getter传参,
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
mutation(state, payload): 必须是同步,用$commit触发,这个
action(context): 通过$dispatch来触发mutation中的函数,可以有异步操作,
这个图有条线没画,就是可以不经过actions直接commit触发mutations里面的操作
57、vueRouter钩子函数
(1)全局钩子
前置守卫:beforeEach((to, from, next) => {....}),一定要走next,确保走任一逻辑也要next,去执行管道内的下一个钩子,全部钩子执行完了,导航的状态才是confirmed
解析守卫:beforeResolve((to, from, next) => {....}): 在导航被确认之前,在组件内守卫以及异步组件被解析之后执行。
后置钩子:afterEach((to, from, next) => {....}): 没有next!
(2)路由独享守卫:
(3)组件内守卫
(4)完整的导航解析流程:
58、webpack常用的几个对象
(1)开发环境配置
/*
开发环境配置
*/
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// 处理css资源
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
// 处理less资源
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
// 处理图片资源
{
test: /\.(png|gif|jpg)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
esModule: false,
name: '[hash:10].[ext]',
outputPath: 'images'
}
},
// 处理html中的图片资源
{
test: /\.html$/,
loader: 'html-loader'
},
// 处理其他资源
{
exclude: /\.(html|js|css|less|jpg|png|gif)$/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins: [
// 处理html资源
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
// 搭建开发服务器 devServer
devServer: {
compress: true,
port: 3000,
open: true
},
mode: 'development'
}
(2)生产环境配置
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
// 复用loader
const commonCssLoader = [
// 把css文件提取出来
MiniCssExtractPlugin.loader,
'css-loader',
// 对css做兼容性处理
// 注意:还需要在package.json中定义browserslist
// browserslist默认使用production配置
// 想要使用development需要定义node环境变量
// process.env.NODE_ENV = 'development'
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
// 指示postcss以什么插件进行工作
plugins: () => [
require('postcss-preset-env')()
]
}
}
]
// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production'
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// 处理css
{
test: /\.css$/,
use: [
...commonCssLoader
]
},
// 处理less
{
test: /\.less$/,
use: [
...commonCssLoader,
'less-loader'
]
},
/*
正常来讲:一个文件只能被一个loader处理。
当一个文件要被多个loader处理,那么一定要指定loader执行顺序:
先执行eslint 再执行babel
*/
// 处理js
// js语法检查:需要在package.json中eslintConfig -->airbnb
{
test: /\.js$/,
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
// 自动修复
options: {
fix: true
}
},
// js兼容性处理
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
// 因为默认的babel不能识别一些语法,但是@babel/polyfill又会全部引入,体积太大
// 所以按需加载会比较好
[
'@babel/preset-env',
{
// 实现按需加载
useBuiltIns: 'usage',
corejs: { version: 3 },
// 具体兼容到哪个版本
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
// 处理图片
{
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
// "html-loader"使用的是commonjs模块化规范
// 所以要关闭掉'url-loader'的es6模块化
esModule: false
}
},
// 处理html中的图片
{
test: /\.html$/,
loader: 'html-loader',
},
// 处理其他文件
{
exlude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
// 压缩html
minify: {
// 去除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
}),
new MiniCssExtractPlugin({
filename: 'css/built.css'
}),
// css压缩
new OptimizeCssAssetsWebpackPlugin()
],
// 调成production, js自动压缩
mode: 'production'
}
59、flex布局
写一些关键词,然后大概讲讲就行:
div {
/* 生成的框体 */
display: flex;
display: inline-flex;
/* 方向 */
flex-direction: column;
flex-direction: column-reverse;
flex-direction: row;
flex-direction: row-reverse;
/* 换行 */
flex-wrap: nowrap;
flex-wrap: wrap;
/* 简写 */
flex-flow: column wrap;
/* 主轴排布 */
justify-content: end;
justify-content: start;
justify-content: space-around; /* 这个是两边更小 */
justify-content: space-between;
justify-content: space-evenly; /* 这个是均分 */
/* 垂轴对齐方式 */
align-items: center;
align-items: stretch;
align-items: start;
align-items: baseline;
/* 弹性因子简写 */
flex: 1 2 200px;
/* 修改显示顺序 */
order: 1;
}
(1)给父元素设定 display:flex属性,里面的元素就自动有了弹性,如果装不下就压缩 有空隙就拉宽。flex-flow 是 flex-direction flex-wrap的简写
(2)flex: 2 1 100px 最后的值最小宽度 也就是说 盒子的可用内容减去这个100px之后 再拿去分,2代表增长因子 1代表缩减因子
(3)align-items 默认值的strech 会拉伸 center不会改变原来的大小且置中,如果父盒子没高度,子元素谁最高就会把父盒子撑开,那strech属性的效果就会和最高的元素一样高。align-self可以单独在某个元素上设置而覆盖
order属性控制排布顺序
60、grid布局
自己写的一篇博客
CSDNhttps://mp.csdn.net/mp_blog/creation/editor/123227118
61、Px em rem vw vh rpx
62、结构伪类选择器?
only-child: 是父类的唯一子元素,
这块可以说一下
63、doctype标签和meta标签
(1)Doctype文档声明标签,H5不再基于于SGML,不需要对DTD进行引用了,因此 <!DOCTYPE html>就足够
(2)meta原信息标签:有几个属性,http-equiv(可以设定一些值,expires, charset, refresh,content-type), name, scheme,然后这些声明的属性的值放在 content属性里面。
http-equiv这个的作用:让服务器添加响应头,如果值是refresh,可以重定向
64、BFC
块级格式化上下文
BFC特性:
(1)内部的Box会在垂直方向,一个接一个地放置。 (2)Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠 (3)每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。 (4)BFC的区域不会与float box重叠。 (5)BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。
(6)浮动元素也会参与高度计算
如何触发:
(1)根元素 (2)float属性不为none (3)position不为static和relative (4)overflow不为visible (5)display为inline-block, table-cell, table-caption, flex, inline-flex
作用:
(1)利用不同的BFC外边距不会重叠,解决边距塌陷的问题
(2)利用开启BFC后,浮动元素也参数高度计算,清除浮动
65、清除浮动
(1)额外标签发:增加一个标签,增加clear类,.clear { clear: both }
(2)父级添加overflow: hidden触发BFC
(3)after伪元素法:是给父元素设置为元素
(4)双伪元素法:
66、link标签和impot
(1)link标签属于HTML标签,import是css提供的一种方式
(2)link在页面加载时,引入的css会被同时加载,import会在页面加载完了之后再加载,会出现闪烁现象
(3)link支持js操作dom
67、git常用命令
git add, git commit, git push, git branch, git checkout, git pull --rebase, git rebase --continue, git stash, git pop, git log
68、keep-alive
1、此标签包裹动态组件,会缓存不活动的组件实例,而不是销毁他们
2、有三个属性:
include: 字符串或正则,名称匹配的组件会被缓存
exclude: 字符串或正则,排除不要被缓存的组件
max: 数字,最多可以缓存多少实例
3、被keep-alive缓存的组件多了两个钩子函数(created就不会重复调用了):
activated: 组件激活时调用
deactivated: 组件停用时调用
4、使用方式:
1、inclue属性
// include 只缓存组件名字为home的组件,其他组件不会缓存,而exclude恰好相反
<keep-alive include="home">
<router-view />
</keep-alive>
2、meta+v-if
{
path: '/',
name: 'home',
meta:{
keepAlive:true
},
component: Home
}
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
69、单页面应用多页面应用的区别
70、为什么Vue中data必须是一个函数
组件复用的时候,如果是个对象,那个每个组件都会指向同一个data的引用,但是是函数的画,每次都会执行函数返回一个新的引用空间,使每个组件的数据独立开来
71、delete Vue.delete
删除数组的话,delete只是