先列题目,然后加答案。
JS
1. Eventloop
主要是分三部分:主线程、宏任务队列(macrotask)、微任务队列(microtask)
主线程,就是访问到的script标签里面包含的内容,或者是直接访问某一个js文件的时候,里面的可以在当前作用域直接执行的所有内容(执行的方法,new出来的对象等)
宏队列(macrotask),setTimeout、setInterval、setImmediate、UI rendering等
微队列(microtask),promise.then、mutaionObserver、process.nextTick等
执行顺序:
先执行主线程 ,遇到宏任务放到宏队列(macrotask),遇到微任务放到微队列(microtask。主线程执行完毕,调用栈被清空,这个时候就会从微队列里取出位于首位的回调放入执行栈开始执行,微队列长度-1,然后依次执行队列里的回调任务直到所有任务被执行完毕,此时微队列为空,调用栈也为空,这时再从宏队列里取出位于首位的一个任务,然后放入调用栈执行,执行完毕之后,再去取微队列里的任务,按照之前的步骤循环。
2. js为什么要实现成单线程的,有什么好处?
原因:作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。
单线程的好处:设想,如果 Javascript 被设计为多线程的程序,那么操作 DOM 必然会涉及到资源的竞争,那么这门语言必然会被实现的非常臃肿。在客户端中跑这么一门语言的程序,资源消耗和性能都将是不乐观的,同时在客户端实现多线程不是刚需。
设计成单线程,并辅以完善的异步队列来实现,那么运行成本就会比多线程的设计要小很多了。
3. 进程线程的区别
- 进程是资源分配的最小单位,线程是程序执行的最小单位(资源调度的最小单位)
- 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。
而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。 - 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
- 但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。
4. 闭包的含义、原理、优缺点、使用场景、如何回收
含义:闭包是函数和声明该函数的词法环境的组合。这个环境包含了这个闭包创建时所能访问的所有局部变量。
闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量,是函数内部和外部之间的桥梁。
原理:函数的作用域是一条作用域链,而且作用域链是有顺序的,我们称之为链式作用域结构。在js中,外部不能读取到内部的变量,而内部可以读取到外部的变量。利用这个特性,闭包可以将读取到的属性扔到外部使用。
优点:可以读取函数内部的变量,让这些变量的值始终保持在内存中。
缺点:闭包对脚本性能有负影响,不能滥用。并且闭包会在父函数外部,改变父函数内部变量的值。
使用场景:
- 给对象设置私有变量并且利用特权方法去访问私有属性
- 采用函数引用方式的setTimeout调用,原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果
- 单例模式的实现,确保全局只有一个实例对象
- module仿模块化,使用闭包封装“私有”状态和组织。只用返回一个公有的API,其他的所有规则都在私有闭包里,防止泄露到全局作用域,并且可以减少与别的开发人员的接口发生冲突。
5. JS数据类型有哪些,怎么做类型判断?
基本类型:string、number、boolean、null、undefined、symbol、bigInt
复杂类型:Object
普通基本类型:undefined、null、symbol
特殊基本包装类型:string、number、boolean
引用类型:Object、Data、function、Array
判断方法:
- typeof
- instanceof
- Object.prototype.toString.call(data)
6. 类型转换有哪些?
- 转string: toString(),String(),拼接空字符创
- 转Boolean: Boolean(),!(),!!()
- 转数字:Number(),parseInt(),+ data
7. 执行上下文
执行上下文是评估和执行 JavaScript 代码的环境的抽象概念。每当 Javascript 代码在运行的时候,它都是在执行上下文中运行。
JavaScript 中有三种执行上下文类型:
全局执行上下文 — 这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
函数执行上下文 — 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。每当一个新的执行上下文被创建,它会按定义的顺序执行一系列步骤。
Eval 函数执行上下文 — 执行在 eval 函数内部的代码也会有它属于自己的执行上下文。
创建执行上下文分为两个阶段:1.创建阶段 2.执行阶段
在创建阶段会发生三件事:
- this 值的决定,即我们所熟知的 This 绑定。
- 创建词法环境组件。
- 创建变量环境组件。
8. new操作符做了什么
- 创建一个空对象
var o=new Object();
- 将空对象的原型赋值为构造函数A的原型
o.__proto__=A.prototype;
- 执行构造函数中的代码,为空对象添加属性
A.call(o);
,也可以理解为将构造器函数内部this指向新建的空对象。 - 返回添加属性后的对象。
function New(obj){
var o = new Object(),
Constructor = obj;
o.__proto__ = Constructor.prototype;
Constructor.call(o);
return o;
}
9. 解释构造函数、对象、原型链之间的关系
每个构造函数都有一个原型对象,原型对象上包含着一个指向构造函数的指针,而实例都包含着一个指向原型对象的内部指针。通俗的说,实例可以通过内部指针访问到原型对象,原型对象可以通过constructor找到构造函数。
10. 继承的几种方式以及优缺点
-
原型链继承
缺点:引用类型的属性被所有实例共享;在创建 Child 的实例时,不能向Parent传参function Parent () { this.names = ['11', '22']; } function Child () {} Child.prototype = new Parent(); var child1 = new Child(); child1.names.push('33'); console.log(child1.names); // ["11", "22", "33"] var child2 = new Child(); console.log(child2.names); // ["11", "22", "33"]
-
构造函数继承
优点:避免了引用类型的属性被所有实例共享,并且可以在 Child 中向 Parent 传参
缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法。function Parent (name) { this.name = name; } function Child (name) { Parent.call(this, name); } var child1 = new Child('11'); console.log(child1.name); // 11 var child2 = new Child('22'); console.log(child2.name); // 22
-
组合继承
优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式
缺点:调用两次父级的构造函数function Parent (name) { this.name = name; this.colors = ['red', 'blue', 'green']; } Parent.prototype.getName = function () { console.log(this.name) } function Child (name, age) { Parent.call(this, name); this.age = age; } Child.prototype = new Parent(); var child1 = new Child('kevin', '18'); child1.colors.push('black'); console.log(child1.name); // kevin console.log(child1.age); // 18 console.log(child1.colors); // ["red", "blue", "green", "black"] var child2 = new Child('daisy', '20'); console.log(child2.name); // daisy console.log(child2.age); // 20 console.log(child2.colors); // ["red", "blue", "green"]
-
原型式继承
function createObj(o) { function F(){} F.prototype = o; return new F(); }
缺点:包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。
-
寄生继承
缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法function createObj (o) { var clone = object.create(o); clone.sayName = function () { console.log('hi'); } return clone; }
-
寄生组合式继承
优点:这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。function object(o) { function F() {} F.prototype = o; return new F(); } function prototype(child, parent) { var prototype = object(parent.prototype); prototype.constructor = child; child.prototype = prototype; }
11. 实现原型式继承
function createObj(o) {
function F(){}
F.prototype = o;
return new F();
}
12. 实现构造函数继承
function Parent (name) {
this.name = name;
}
function Child (name) {
Parent.call(this, name);
}
var child1 = new Child('11');
console.log(child1.name); // 11
var child2 = new Child('22');
console.log(child2.name); // 22
13. 面向对象的属性
多态,封装,继承
14. 设计模式有哪些,项目中使用到哪些
- 单例模式,参考vuex,loading,全局弹窗等
- 策略模式,封装不同的计算方法,封装调用接口,常见购物车商品价格计算
- 观察者模式,参考webpack内部plugin的调用
- 发布订阅模式,参考vue响应式原理
- 迭代器模式,参考forEach,map方法,引申Iterator
15. 浏览器事件有哪些过程? 为什么一般在冒泡阶段, 而不是在捕获阶段注册监听? addEventListener 参数分别是什么 ?
过程:事件捕获阶段、处在目标阶段、冒泡阶段
IE低版本不支持捕获,为了兼容性,一般都写冒泡
addEventListener可接受3个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。布尔值若为true,表示在捕获阶段调用事件处理程序;若为false,表示在冒泡阶段调用事件处理程序
16. new String('a') 和 'a' 是一样的么?
let data = 'a' // 定义的是一个基本类型字符创,入栈
let data = new String('a') // 定义的是一个包装类型字符串,入堆,
17. js中的装箱和拆箱了解吗?
装箱:把基本数据类型转化为对应的引用数据类型的操作。每当读取一个基本类型的时候,后台就会创建一个对应的基本包装类型对象,从而让我们能够调用一些方法来操作这些数据。
所以直接定义一个基础类型字符串也能调用方法。
var s1 = "abc";
var s2 = s1.indexOf("a")
拆箱:将引用类型对象转换为对应的值类型对象
如果是自定义的对象,你也可以自定义它的valueOf()
或者toString()
方法,实现对这个对象的拆箱。
var objNum = new Number(123);
var objStr =new String("123");
console.log( typeof objNum ); //object
console.log( typeof objStr ); //object
console.log( typeof objNum.valueOf() ); //number
console.log( typeof objStr.valueOf() ); //string
console.log( typeof objNum.toString() ); // string
console.log( typeof objStr.toString() ); // string
18. 节流与防抖原理
防抖与节流函数是一种最常用的 高频触发优化方式,能对性能有较大的帮助。
防抖 (debounce): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。
function debounce(fn, wait, immediate) {
let timer = null
return function() {
let args = arguments
let context = this
if (immediate && !timer) {
fn.apply(context, args)
}
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}
节流(throttle): 每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms执行一次即可。
function throttle(fn, wait, immediate) {
let timer = null
let callNow = immediate
return function() {
let context = this,
args = arguments;
if (callNow) {
fn.apply(context, args)
callNow = false
}
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args)
timer = null
}, wait)
}
}
}
19. 面向对象和非面向对象有什么区别
面向对象是对事件处理的抽象,方便扩展设计,非面向对象一般是针对流程的处理,没有抽象概念。
20. 高阶函数是什么,怎么去写一个高阶函数
只需满足以下任意一个条件,即是高阶函数:
- 接受一个或多个函数作为输入
- return 返回另外一个函数
21. 模块化介绍一下,什么是编译时优化?模块化的演化过程?
AMD / CMD / ES6 module 主要是用于前端,都是属于异步并行加载
AMD是依赖前置,在define里先定义好依赖模块,然后在回调函数里做引用
CMD是依赖后置,define了一个模块,在里面什么时候用到模块了,什么时候再require
Commonjs一般运行在node端,通过复制module.exports这个属性,然后使用require对module.exports属性做复制,require属于是同步加载,require的模块是拷贝值,不会对引用模块产生任何改变。webpack里我们写的import最终也都是被转换成了commonjs的形式
es6 module 就是export和import,但是export出来的是一个接口,import只是用过接口对其内容进行引用,所以是可以修改起内容的。commonjs require是动态编译,运行时才加载,import是编译时就要加载,属于静态编译。但是目前已经有import()函数在提案内,可以实现动态加载了,此外还有更新的提案,await提升,之前await只能在标记async的函数内使用,现在的提案是await可以单独使用,import()动态加载时还需要写then做后续处理,现在不用了,写法简化很多。
22. 简单说一下PWA
PWA并不是单指某一项技术,你更可以把它理解成是一种思想和概念,目的就是对标原生app,将Web网站通过一系列的Web技术去优化它,提升其安全性,性能,流畅性,用户体验等各方面指标,最后达到用户就像在用app一样的感觉。
PWA中包含的核心功能及特性如下
- Web App Manifest
- Service Worker
- Cache API 缓存
- Push&Notification 推送与通知
- Background Sync 后台同步
- 响应式设计
23. call/apply/bind原理实现
call
Function.prototype.mycall = function(context) {
// this 指向调用 mycall 的函数对象,调用 mycall 的若不是函数则报错
if (typeof this !== 'function') {
throw new TypeError('Not Function')
}
// context 为 null 或者 undefined 或者不传,则指向 window
context = context || window
// 将调用 mycall 的函数对象添加到 context 的属性中
context.fn = this
// 获取除第一个参数之外的其余参数,然后执行
const args = [...arguments].slice(1)
// mycall 的函数对象的 this 就指向了 context
const result = context.fn(...args)
// 删除该属性
delete context.fn
return result
}
apply
Function.prototype.myapply = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Not Function')
}
if (arguments[1] instanceof Array) {
throw new TypeError('Arguments Need To Array')
}
context = context || window
context.fn = this
let result = null
// 如果不传参的话,arguments[1] 为 undefined,解构将会报错
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
return result
}
bind
Function.prototype.mybind = function(context) {
if (typeof this !== 'function') {
throw new TypeError('Not Function')
}
context = context || window
const originFunc = this
const args = [...arguments].slice(1)
return function() {
// 处理函数成为构造函数的情况
// new.target 属性允许你检测函数是否是通过 new 运算符被调用的。在普通的函数调用中,new.target 的值是 undefined
if (new.target) {
// 构造函数 return 的是一个引用值的话,则 new 操作符返回的就是这个引用值
return new originFunc(...args, ...arguments)
} else {
return originFunc.call(context, ...args, ...arguments)
}
}
}
- 操作dom有哪些方法?
- 怎么用原生js实现一个轮播图,以及滚动滑动?
- 怎么实现上传下载的功能?
- Map有哪些方法?
- 知不知道伪数组对象?
29. 如何用原生JS来读写Cookie?
const cookieArray = document.cookie; //获取cookie存储字符串
const arr = cookieArray.split(";"); //将获得的cookie字符串以;分割开获得单独每个cookie名/值对
// 读取
//假设要获取的cookie的名字是name,则遍历数组找到名字所对应的值
let result = ''
for(var i=0;i<arr.length;i++){
var arr1 = arr[i].split("="); //将名/值对以“=”分割开
if(arr1[0]==name){
result = arr1[1]; //如果名为name,则结果result为名对应的值
}
}
// 设置
document.cookie="name=value;expires=GMT_String";
30. 本地存储
31. 怎么打断点,如何确定一个结果来自于哪个函数
debugger
32. 简述自定义事件实现方法
33. parseInt() 和 array 的 map 方法的参数
34. JSON 对象的深度克隆
35. objec.freeze和Object.seal的区别
36. 编写函数 convert(money) ,传入金额,将金额转换为千分位表示法
37. js浮点数运算不精确,如何解决
我们进行运算的时候,实际上是把数字转换为了二进制进行的,所以我们把0.1和0.2转换为二进制:
0.1 => 0.0001 1001 1001 1001..(无限循环)
0.2 => 0.0011 0011 0011 0011…(无限循环)
这里可以看出转换为二进制是一个无限循环的数字,单在计算机中对于无限循环的数字会进行舍入处理的,进行双精度浮点数的小数部分最多支持52位。然后把两个2进制的数进行运算得出的也是一个二进制数值,最后再把它转换为十进制。保留17位小数,所以0.1+0.2的值就成了 0.30000000000000004。 0.1+0.1的值成了0.20000000000000000,全是0的时候可以省略,就成了0.2。
解决办法最简单的是直接toFixed,有时结果也是不准确的,所以我们还可以直接截取小数点后面的值直接乘以10的倍数,得到相加结果再除以相同的10的倍数。
38. 实现函数的柯里化
39. 前端存储方式,以及它们之间的优缺点
40. 怎么从十万个节点中找到想要的节点,怎么快速在某个节点前插入一个节点?
41. 如何找到一个字符串中最长的两个字符串?
42. 正则用过吗?exec, 匹配一个手机号?去空格?
43. 给你一亿个数,是连续的,怎么找出两个不存在的数
44. 怎么实现一个sleep ,手写一个promise
45. 做一个表格,一分钟刷新一次,怎么实现
46. 有了解WebWorker的实现原理吗,私下有实践过吗
直接来说,WebWorker可已开启多线程操作,但是不能操作dom。多线程之间可以通过postMessage传递信息,监听onMessages获取信息。
47. 平时做项目有没有考虑过内存的问题?怎么解决的?
48. 异步处理的方案有哪些?
数组
1. Array的unshift() method的作用是什么?如何连接两个Array?如何在Array里移除一个元素?
2. 数组去重
// set
function unique (arr) {
return Array.from(new Set(arr))
}
// indexOf
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array = [];
for (var i = 0; i < arr.length; i++) {
if (array .indexOf(arr[i]) === -1) {
array .push(arr[i])
}
}
return array;
}
// includes
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array =[];
for(var i = 0; i < arr.length; i++) {
if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
array.push(arr[i]);
}
}
return array
}
// filter+indexOf
function unique(arr) {
return arr.filter(function(item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
3. 数组乱序
function shuffle(arr) {
let newArr = arr.map(item\=>({val:item,ram:Math.random()}));
newArr.sort((a,b)=>a.ram-b.ram);
let newArr = newArr.map(i=>i.val)
return newArr;
}
function shuffle(arr) {
let m = arr.length;
while (m > 1){
let index = Math.floor(Math.random() * m--);
[arr[m] , arr[index]] = [arr[index] , arr[m]]
}
return arr;
}
4. 有哪些排序算法,时间复杂度是多少?什么时候快排的效率最低?
5. 改变数组和不改变数组的方法分别是哪些?
6. 给定一个数组和一个正整数N,求一个和小于N的最长连续子数组
7. 给定两个有序数组,合并为一个有序数组。不许使用 js 的 concat 和 sort 方法
function arrSort(arr1, arr2) {
var [i,j] = [0,0];
let newArr = [];
while(i < arr1.length || j <arr2.length) {
if (arr1[i] < arr2[j]) {
newArr.push(arr1[i]);
i++
} else if (arr1[i] > arr2[j]) {
newArr.push(arr2[j])
j++
} else {
if(arr1[i]) newArr.push(arr1[i]);
if(arr2[j]) newArr.push(arr2[j]);
i++;
j++
}
}
return newArr
}
8. sort()的原理
**当数组长度小于等于10的时候,采用插入排序,大于10的时候,采用快排。
对于长度大于1000的数组,采用的是快排与插入排序混合的方式进行排序的,因为,当数据量很小的时候,插入排序效率优于快排。**
9. 不产生新数组,删除数组里的重复元素
10. 冒泡排序和快速排序的区别
冒泡排序是从最底层元素开始比较du,(与其上的元素比较)小于就往上再zhi比,大于就交换,再用较小dao的往上比较,直到最高层,第一次把最小的放到最上层,第二次把第二小的放到第二层,以次类推。
快速排序是先找到一个轴值,比较时把所有比轴值小的放到轴值的左边,比轴值大的放到右边,再在两边各自选取轴值再按前面排序,直到完成。
ES6
- Promise的原理
- ES6 module
- 谈一下generator函数
- 说一下对async和await的理解
- Map有哪些方法?
- 箭头函数,this指向
- 扩展符的作用
- 新增数据类型有哪些?
- 新增的数据结构有哪些?
- ES6 里 let 和 var 的区别
- 解释ES6的暂时性死区
http
1. 讲一下AJAX Request
2. 跨域
同源策略导致跨域,解决办法: JSONP、nignx反向代理、CORS配置(会有一次预检请求)
3. 页面加载的过程(输入URL后发生了什么?)
4. TCP三次握手和四次挥手,拥塞控制
5. TCP和UDP的区别
6. HTTP请求头和响应头都有些什么字段?
7. SSL加密原理
非对称加密,对称加密
8. HTTP/HTTPS/HTTP2有什么区别?
9. HTTP2的优势
10. HTTP 里的304状态码了解吗?100和307?
304-延伸到浏览器缓存问题
100:continue 延伸到post请求
307:临时重定向,请求体不会发生变化
11. 进程通信,有名和匿名管道
进程之间通信,就实现手段上来看,有以下几种:
- 匿名管道(有亲缘关系进程)
- 有名管道(无亲缘也可以)
- 消息队列
- 共享内存
- 信号量
- socket套接字
匿名管道:
PIPE匿名管道用于具有血缘关系之间的进程进行通信。
1.半双工通信,同一时间数据只能向一个方向传输,具有固定的读端和写端。
2.只能用于具有亲缘关系的进程间通信(父子或者公共祖先)。
3.本质是内核中的一块缓冲区。
4.自带“同步互斥机制”,同时只有一个进程能够读到数据。
5.生命周期随进程。
6.管道提供字节流服务。
命名管道
FIFO:命名管道(也是半双工),每个FIFO都有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO
特点:
- 可以在没有亲缘关系的进程间交换数据
- FIFO有路径名与之关联,它以一种特殊设备文件形式存在于文件系统中。
- 会出现抢读,写一次,就要去读,不管是谁读,里面的数据都会被清理
- 浏览器缓存
13. cookie、session、token的区别?
cookie由服务器生成,保存在客户端浏览器,容易被劫持,不安全
session保存在服务端,每个sessionID都是唯一的,当用户量太大时,占用服务器资源,较安全
token的工作原理:
1.客户端第一次请求时,发送用户信息到服务器。服务器对用户信息使用HSA256算法及密钥进行签名,再将这个签名和数据一起作为token返回给客户户端。
2.服务端不再保存token,客户端保存token。
3.当客户端再次发送请求时,在请求信息中将token一起发送给服务器。
4.服务器用同样的HSA256算法和密钥,对数据再计算一次签名,和token的签名做比较
5.如果相同,服务器就知道客户端登录过,则反之。
token的优势:
1.无状态、可扩展
2.支持移动设备
3.跨程序调用
4.安全
- 如果浏览器关闭了再打开, 请求还是from cache吗?
15. Service Worker 了解过么?
一个服务器与浏览器之间的中间人角色,如果网站中注册了service worker那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。从而大大提高浏览体验。
- 基于web worker(一个独立于JavaScript主线程的独立线程,在里面执行需要消耗大量资源的操作不会堵塞主线程)
- 在web worker的基础上增加了离线缓存的能力
- 本质上充当Web应用程序(服务器)与浏览器之间的代理服务器(可以拦截全站的请求,并作出相应的动作->由开发者指定的动作)
- 创建有效的离线体验(将一些不常更新的内容缓存在浏览器,提高访问体验)
- 由事件驱动的,具有生命周期
- 可以访问cache和indexDB
- 支持推送
- 并且可以让开发者自己控制管理缓存的内容以及版本
- Service worker运行在worker上下文 --> 不能访问DOM
- 它设计为完全异步,同步API(如XHR和localStorage)不能在service worker中使用
- 出于安全考量,Service workers只能由HTTPS承载
- 为什么用 token 就可以防止 csrf 攻击?
- token 的刷新机制是怎么样的, 为什么这么设置?
- 静态文件的浏览器缓存如何实现
19. method有哪些方法,分别是什么意思?post和put的区别?post与get的区别?
method | |
---|---|
get | 查询数据 |
post | 一般是对服务器的数据做改变,常用来数据的提交,新增操作 |
put | put请求与post一样都会改变服务器的数据,但是put的侧重点在于对于数据的修改操作,但是post侧重于对于数据的增加 |
patch | put的更新是全部更新,patch可以执行部分更新,只更新接收到的数据 |
delete | 用来删除服务器的资源 |
options | 属于浏览器的预检请求,查看服务器是否接受请求,预检通过后,浏览器才会去发get,post,put,delete等请求。非简单请求才会发送遇见请求 |
post和put的区别?
put请求与post一样都会改变服务器的数据,但是put的侧重点在于对于数据的修改操作,但是post侧重于对于数据的增加
post与get的区别?
- get是从服务器上获取数据,post是向服务器传送数据。
- 传参方式不同,一个参数挂在url上,一个参数在请求体里。
- get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
- GET产生一个TCP数据包,POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
20. Ajax 底层实现,readystate 有哪些
- 怎么实现标签页的通信
- OSI七层模型
- 反向代理知道么,Nginx
- 有没有了解过CDN
- 怎么实现标签页的通信
框架
- vue响应式原理
- vuex原理
- 组件间的通信方式及原理
- MVC和MVVM了解吗?有什么区别?
- Vue是怎么实现对数组变化的检测的
6. key的作用是什么?
key的作用主要是为了高效的更新虚拟DOM。另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它。
参考文章
- jsx的原理
- Virtual DOM 和 diff 算法
- 脚手架做了什么功能?
- vue-router原理
- Vue 里 v-if 和 v-show 的区别
- react生命周期
- react hook 相比较 class, 哪些不太容易实现?
- react 怎么做优化?
- 聊一下高阶组件 hoc
- 聊一聊组件设计, 领域模型
- redux原理
- setState 更新机制
- fiber架构
- react hook 原理
- 原生事件和 React事件的区别
- 高阶组件(HOC), Mixin, hook 对比和用处.
- webpack打包流程
- webpack如何处理图片、 CSS 文件?
- webpack做了什么优化?
- webpack热更新原理
- webpack 底层 Tapable 原理
28. webpack 如何实现异步加载?
就是把一些js模块给独立出一个个js文件,然后需要用到的时候,再创建一个script对象,加入到document.head对象中就可,浏览器会自动帮我们发起请求,去请求这个js文件,然后写个回调函数,让请求到的js文件做一些业务操作。
- babel原理
- transform-runtime有什么作用?
31. babel-runtime和babel-polyfill的作用和区别
Babel默认只转换新的JavaScript语法,而不转换新的API。 例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译。 如果想使用这些新的对象和方法,则需要为当前环境提供一个polyfill
babel-polyfill
目前最常用的配合Babel一起使用的polyfill是babel-polyfill,它会”加载整个polyfill库”,针对编译的代码中新的API进行处理,并且在代码中插入一些帮助函数。
babel-runtime
babel-polyfill解决了Babel不转换新API的问题,但是直接在代码中插入帮助函数,会导致污染了全局环境,并且不同的代码文件中包含重复的代码,导致编译后的代码体积变大。
Babel为了解决这个问题,提供了单独的包babel-runtime用以提供编译模块的工具函数, 启用插件babel-plugin-transform-runtime后,Babel就会使用babel-runtime下的工具函数。垫片也是即用即插。这样可以避免自行引入polyfill时导致的污染全局命名空间的问题。
babel-polyfill与babel-runtime相比虽然有各种缺点,但在某些情况下仍然不能被babel-runtime替代, 例如,代码:[1, 2, 3].includes(3),Object.assign({}, {key: 'value'}),Array,Object以及其他”实例”下es6的方法,babel-runtime是无法支持的, 因为babel-runtime只支持到static的方法。
- babel 如何将字符串解析成 AST ?
- 讲一下AST 语法树
- webpack 和 gulp的对比
- node事件循环
- node架构中的容灾
37. node 的多线程,高并发,安全
由于node 是单进程的,无法充分利用多核 CPU 的性能,一般会使用 cluster 模块,进行多进程的部署。在多进程的环境下,又会带来一些并发的问题。进程之间的数据是不共享的,但是依然会有很多共享的资源,比如文件系统,数据库等。对于这些资源的并发访问和修改依然会导致问题。
所以在 cluster 模式下,需要更可靠的锁机制,保证对多个 node 实例,一次只能执行一个异步函数。为了实现这一点,仅仅依赖单线程的 node 是不可行的,需要引入外部的状态管理。Redlock 算法提供了一种基于redis 的分布式锁的实现。关于分布式锁和redlock算法的详细介绍可以参考这个链接https://redis.io/topics/distlock
38. 谈一下stream
39. pm2原理
40. 说说排查内存泄露的方法
内存泄露通常有两种情况,一种是容易复现的,一种是不容易复现的。
对于容易复现的我们在测试环境中模拟排查即可。
对于不容易复现的我们就要借助内存快照,可以使用devtool查看内存快照,heapdump保存内存快照。heapdump保存的内存快照只会有Node环境中的对象,如果使用node-inspector快照中就会包含前端变量,容易造成干扰。
41. 有没有读过 egg 源码
42. 小程序跟 h5 的区别是什么? [小程序底层实现]
43. 讲一下 taro 小程序的底层原理,跟 mpvue 的区别 [AST, babel
44. SPA 项目如何监控 pv, uv 值
45. 聊一下 axios .有什么优点, 跟 fetch, ajax对比
46. axios 为什么既可以在浏览器发请求,又可以在node层发请求?
47. 客户端渲染和服务端渲染的区别
监控
- 如何处理项目的异常
- error 怎么捕获?
3. SPA 项目如何监控 pv, uv 值?
使用导航守卫的钩子上报数据
4. 如何在用户刷新、跳转、关闭浏览器时向服务端发送统计的数据?
监听onload或beforeUnload,或者定义图片链接作为上传接口,除此之外,还可以使用navigator.sendBeacon(url, data);
sendBeacon
方法具有如下特点:
- 发出的是异步请求,并且是
POST
请求,后端解析参数时,需要注意处理方式; - 发出的请求,是放到的浏览器任务队列执行的,脱离了当前页面,所以不会阻塞当前页面的卸载和后面页面的加载过程,用户体验较好;
- 只能判断出是否放入浏览器任务队列,不能判断是否发送成功;
-
Beacon API
不提供相应的回调,因此后端返回最好省略response body
。
5. 错误日志上报遇到的问题.
6. 负载均衡方式和容错机制
7. 怎么计算在一个页面上的停留时间
移动端
1. 移动端如何优化首页白屏时间过长 ?
2. 移动端优化方式? 离线包是如何实现的?
3. 有几种webview?
ios8之前是UIWebview
,ios8之后是WebkitWebview
4. webview和native的交互方式有哪些?
JavaScript 通知 Native
-
API注入,Native 直接在 JS 上下文中挂载数据或者方法
- 延迟较低,在安卓4.1以下具有安全性问题,风险较高
-
WebView URL Scheme 跳转拦截
- 兼容性好,但延迟较高,且有长度限制
- WebView 中的 prompt/console/alert拦截(通常使用 prompt)
Native 通知 Javascript:
-
IOS:
stringByEvaluatingJavaScriptFromString
-
Android:
loadUrl
(4.4-) -
Android:
evaluateJavascript
(4.4+)
5. 怎么排查交互中的错误?
- chrome真机调试
- weinre调试
- spy-debugger调试
- vconsole.js
- APP内部调试接口
6. 浏览器内核有哪些,移动端用的是哪个?
7. 移动端页面适配解决方案
rem,viewport
安全
1. 前端安全方面有没有了解?XSS和CSRF如何攻防?
2. sql 注入知道么?
3. 爬虫与反爬虫
其他
1. 文件指纹怎么生成?
2. npx是什么?
npx是执行Node软件包的工具,它从 npm5.2版本开始,就与npm捆绑在一起。
npx 想要解决的主要问题,就是调用项目内部安装的模块。
-
当在执行
npx <command>
的时候,npx会做什么事情?-
帮你在本地(可以是项目中的也可以是本机的)寻找这个 command
- 找到了: 就用本地的版本
- 没找到: 直接下载最新版本,完成命令要求
- 使用完之后不会在你的本机或者项目留下任何东西
-
因此优势总结:
- 不会污染本机
- 永远使用最新版本的dependency
3. 说一下对 package.json 的理解,它都有哪些作用?
package.json文件描述了一个NPM包的所有相关信息,包括作者、简介、包依赖、构建等信息。格式必须是严格的JSON格式。
package.json文件指定了项目的版本,依赖文件,可运行脚本,入口文件等等
4. webgl用过吗?
5. svg和canvas的区别
6. git push -u 是什么意思
7. git rebase解释下,git merge 和git rebase区别
8. 能写一个二叉树么,怎么去遍历
9. 伪类知道吗,有哪些?
10. Xhtml和html的区别?
11. 二维码怎么工作的,扫描pc端的二维码,怎么让pc端登录?
12. 怎么做一个实时的聊天系统
13. 当消息有延迟的时候,怎么保证消息的正确顺序?
14. Mysql的基本写法
15. mysql的索引用的什么
16. mySql和noSQL区别
17. meta标签
18. 了解盒模型吗?
盒模型有两种:1. W3c标准的盒子模型(标准盒模型) 2. IE标准的盒子模型(怪异盒模型)
标准盒模型下盒子的大小 = content(width)+ padding + border + margin
怪异盒模型下盒子的大小 = content(width+ padding + border) + margin
box-sizing
可以改变盒模型,border-box
将会出发怪异模式解析计算
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。