阶段二面试

222 阅读11分钟

■ 符号说明

💘 课题

🌟 常见重要

🌛 需要有印象的

💘 Week1

🌟 说出JS数据类型有哪些

原始类型/基本类型:null、undefined、boolean、number、string、symbol、bigint 对象类型/引用类型/复杂/复合类型:

标准普通对象:object
标准特殊对象:ArrayDateMathRegExpError……
非标准特殊对象:NumberStringBoolean……
可调用/执行对象「函数」:function

🌟 JS引用类型object、array还有哪些

可调用/执行对象「函数」:function
标准特殊对象:ArrayDateMathRegExpError……
非标准特殊对象:NumberStringBoolean……

🌟 如何把数据强制转换为数值型

说明:面试概率低、工作概率高

语法:parseInt、parseFloat、Number

🌛 说出数据转换为布尔型的结果

null、undefined、0、NaN、空字符串结果是false、其他都是true

🌛 说出数据转换为字符串型的结果

都字符串

String(内容)、toString(内容)

🌛 说出数据转换为数值型的结果

Number(数据)

数据结果
null0
undefinedNaN
true/false1/0
123/NaN123/NaN
''/'123'/'123a'0/123/NaN

🌛 说出下述隐式转换的结果

var result = 100 + true + 21.2 + null + undefined + "Tencent" + [] + null + 9 + false;

[] + [] // ""

[] + {} // "[object object]"

{} + [] // 0 对于编译器而言,代码块不会返回任何的值, 接着+[]就变成了一个强制转number的过程

true+true+true===3 // true

true-true // 0

true==1 // true

true===1 // false

9+"1" // 91

91-"1" // 90

[]==0 // true Number([]) 0

🌛 null 和 undefined 有什么区别?

undefined 表示未定义,新定义的变量没有赋值就是undefined

null表示清空,当一个变量不用的时候,除了等待网页关闭销毁,也可以把它赋值为null。此时游览器会进行一个回收也就是清空内存。

了解1

typeof(null)会是一个object。最初这么设计的原因为:通常用作一个空引用一个空对象的预期,就像一个占位符。typeof的这种行为已经被确认为一个错误,虽然提出了修正,出于后兼容的目的,这一点已经保持不变。

了解2

null == undefined    true
null === undefined   false

了解3

null + 1       1
undefined + 1  NaN

🌛 为什么 0.1 + 0.2 !== 0.3? 你如何解决这个问题?

在计算机中,由于存储空间有限,十进制转二进制会存在精度丢失问题。

因为 0.1 这样的数值用二进制表示你就会发现无法整除,

最后算下来会是0.0001100110011001...由于存储空间有限,位数是无限的,只能取近似。

代码:0.1 + 0.2 == 0.3 // false

代码:0.625 + 0.625 == 1.25 // true

进制转换规则:www.runoob.com/w3cnote/dec…

十进制整数 -> 转二进制

转换:除2反向取余

举例:(5).toString(2) // 结果 101
转换:除2反向取余
2| 5    --- 余1
------
2| 2    --- 余0
------
1    

举例:(13).toString(2) // 结果 1101
转换:除2反向取余
2| 13    --- 余1
------
2| 6     --- 余0
------
2| 3     --- 余1
------
1    

核对:https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fwww.pianshen.com%2Fimages%2F732%2Faf802c5be33b4934e6a6fec52a208734.png&refer=http%3A%2F%2Fwww.pianshen.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1642634303&t=293eccde3cc561a3ed2d38f8cf57eea9

十进制小数 -> 转二进制

转换:乘2正向取整

举例:(0.625).toString(2) // 结果 0.101
转换:乘2正向取整
0.625*2=1.25======取出整数部分1 
0.25*2=0.5========取出整数部分0 
0.5*2=1==========取出整数部分1 

举例:(0.7).toString(2)  // 结果 0.101100110011001100110011 0011 ...
0.7*2=1.4========取出整数部分1 
0.4*2=0.8========取出整数部分0 
0.8*2=1.6========取出整数部分1 
0.6*2=1.2========取出整数部分1 
0.2*2=0.4========取出整数部分0  
0.4*2=0.8========取出整数部分0 
0.8*2=1.6========取出整数部分1 
0.6*2=1.2========取出整数部分1 
0.2*2=0.4========取出整数部分0
...

🌟 实战购物车小数计算精度丢失如何解决?

推荐解决: 浮点数转化成整数

实战使用:购物车结算时,商品价格(33.01)-优惠券价格(5),本应该是28.01,但是实际的结果是28.009999999999998

33.01 - 5 = 28.009999999999998

(33.01*100 - 5*100) / 100 = 28.01

es6 解决办法

es6 提供了 Number.EPSILON,这个值等于 2^-52 ,这个值非常非常小,在底层计算机已经帮我们运算好,并且无限接近 0,但不等于 0,这个时候我们只要判断误差值在这个范围内就可以说明是相等的。

function numbersequal(a,b){
    return Math.abs(a-b)<Number.EPSILON
}
var a = 0.1 + 0.2;
var b = 0.3;
console.log(numbersequal(a, b))
// true

四舍五入

采用四舍五入方法,取了一个 10 位小数

function numTofixed(num) {
    if (typeof num == 'number') {
        num = parseFloat(num.toFixed(10))
    }
    return num;
}

numTofixed(0.1 + 0.2);
// 0.3

使用第三方库

math.js
bignumber.js
....

💘 Week2

- day1

🌟 说出变量在内存中的分配

栈:存 原始类型【名字】&【数据】、对象类型的【名字】&【堆地址】

堆:对象类型数据

🌟 说出变量赋值内存分配

原始类型:变量赋值,栈开辟内存直接存数据 -> 后期修改数据,数据互不影响 对象类型:变量赋值,栈开辟内存,存放堆地址 -> 后期修改数据,数据相互影响

切记切记切记:上课讲的形参也会出现传值、传地址问题

var num = 100      
function fn(n) {  
  // var 形参 = 实参
  // var n = num也就是100   原始类型 传值 不影响
  n = 200		 
  console.log(n)   // 打印: 200
}
fn(num)             
console.log(num)   // 打印: 100

// --------------------------

var obj = { name: 'Jack' }    

function fn(o) {	 	  
  // var 形参 = 实参
  // var o  = obj    对象类型赋值 传地址  相互影响
  o.name = 'Rose'	  	//    
  console.log(o.name) //  打印:Rose
}
fn(obj)				  
console.log(obj.name) //  打印:Rose

栈   		   						  								  	 堆
obj:地址1	  																  地址1:{ name: 'Jack改成Rose' }  
fn函数里面的o变量:地址1 函数调用完毕o变量就销毁了 

- day2

🌟 判断是否是数组

方法1:通过语法 Array.isArray()

方法2:instanceof

方法3:原型链(constructor)

方法4:判断是否是数组:Object.prototype.toString.call(Arr) === '[object Array]'

Array.isArray([1, 2 ,3])     	 // true
[1, 2 ,3] instanceof Array 	   // true
[1, 2 ,3].constructor === Array // true

Array.isArray('aa')     	 // false
'aa' instanceof Array 	   // false
'aa'.constructor === Array // false
Object.prototype.toString.call(Arr) === '[object Array]'

🌛 如何交换两个变量的值

  • 临时变量法
var a = 1
var b = 2;
var temp = a
a = b
b = temp
  • 加减法
var a = 1
var b = 2;
a = a+b  // a = 1+2 = 3
b = a-b  // b = 3-2 = 1
a = a-b  // a = 3-1 = 2
  • 解构赋值法
var a = 1
var b = 2;

[a, b] = [b, a]
  • 数组法
var a = 1
var b = 2
a = [a, b]
b = a[0]
a = a[1]
  • 对象法
var a = 1
var b = 2
a = { a: b, b: a }
b = a.b
a = a.a
  • 等等太多了

🌟 说出数组有哪些方法

数据操作:shift/unshift/pop/push

遍历数据: forEach/map/filter/find/findIndex

工作常用:concat/join/indexOf/includes

学习常用:reverse/splice

了解:sort

数组转字符串用 join

删除 splice

🌟 说出数组哪些方法会改变原数据

数据操作:shift/unshift/pop/push

学习常用:reverse/splice

了解:sort

🌟 如何实现数组去重

方法1:通过es6新增的Set数据结构、配合展开运算符和解构赋值去重 [...new Set(重复数组)]

方法2:通过filter配合indexOf实现数组去重

方法3:定义空数组,通过forEach遍历重复的数组,通过indexOf判断当前值是否在数组中,不在就push

方法4:定义空数组,通过forEach遍历重复的数组,通过includes判断当前值是否在数组中,不在就push

方法5:利用对象的属性去重

方法6:....

// 方法1:通过es6新增的Set数据结构结果     [...new Set(重复数组)]
var arr = [1,2,3,2,3]
console.log([...new Set(arr)])


// 方法2:通过filter配合indexOf实现数组去重
var arr = [1,2,3,2,3]
var newarr = arr.filter(function(item, i) {
    // i=0  item值是1  arr.indexOf(item)  返回0  真
    // i=1  item值是2  arr.indexOf(item)  返回1  真 
    // i=2  item值是3  arr.indexOf(item)  返回2  真
    // i=3  item值是2  arr.indexOf(item)  返回1  假
    // i=4  item值是3  arr.indexOf(item)  返回2  假
    return i === arr.indexOf(item);
}) 
console.log(newarr)


// 方法3:定义空数组,通过forEach遍历重复的数组,通过indexOf判断当前值是否在数组中,不在就push
var newarr = []
var arr = [1,2,3,2,3]
arr.forEach(function(item, i) {
    if (newarr.indexOf(item) === -1)
    {
        newarr.push(item)
    }
})
console.log(newarr)


// 方法4:定义空数组,通过forEach遍历重复的数组,通过includes判断当前值是否在数组中,不在就push
var newarr = []
var arr = [1,2,3,2,3]
arr.forEach(function(item, i) {
    if (!newarr.includes(item))
    {
        newarr.push(item)
    }
})
console.log(newarr)

// 方法5:利用对象的属性去重 对象中键和值相当可以简写只写一个。
var arr = [1,2,3,2,3]
var newarr = []
var obj = {}
arr.forEach(function(item) {
	if (!obj[item]) {
		obj[item] = item // 出现记录
    newarr.push(item)
	}
})

- day3

🌟 说出字符串常用方法

工作常用:数组、查找、替换、截取、大小、空格

split、find、replace、substr、toUpperCase/toLowerCase、trim

学习:.length、lastIndexOf、repeat

字符串转数组使用split

截取 substr

repalce response request resolve reject remove relative return

🌛 JS如何去空格

说明: 通过 trim 去左右空格

留心:中间不能去

追问:JS如何去所有空格

解决:通过2021年 ES12新增语法replaceAll来解决

' a b c '.replaceAll(' ', '')

解决:通过正则

' a b c '.replace(/\s/g, '')

- day4&5

🌛 伪数组

长得像数组,能用少部分的属性 例如length 但是所有方法都不可以使用,在js中常见的伪数组有

document.qsa、arguments等等

🌟 谈谈你对this指向的理解

面试: 调用当前方法的对象 也就是谁调用的打印的就是谁

举例:

function fn1() {} 
fn1() // 最大全局变量window调用的

标签对象.事件类型 = 处理函数  // 标签对象触发后调用的

学习

普通函数   window
对象函数   对象本身
事件函数   事件源
定时器函数  window
箭头函数   父function中的this  父没有function就是window
自定义     call/apply/bind
构造函数   this === 实例化对象 === 公共空间/原型方法中的this 


其他:事件、构造函数、公共空间/原型对象上方法-优先使用普通函数写法,当需要明确改变this指向的时候再换箭头函数,
其他全部优先使用箭头函数。

🌟 图片懒加载原理

好处:减少HTTP请求

原理:

1、监控滚动条滚动
2、获取总可视内容高度(可见视口高度+滚动条滚动高度)
3、获取所有图片
4、遍历步骤3(或者说:遍历伪数组)
5、在步骤4中判断,图片.offsetTop <= 步骤2    成立-修改src属性为data-src、失败-不管
6、节流防抖优化
留心1:有时图片获取不到高度与宽度我们可以使用imgObj.onload
留心2:不一定所有的图片都需要懒加载,我们可以给需要懒加载的图片添加特定的类名以此来判断是否需要懒加载。

留心:有时候图片高度获取怎么不到?

<!-- 195x265 -->
<br />
<img src="./imgs/1.jpg" alt="">

<script>
var imgObj = document.querySelector('img')
console.log(imgObj.offsetWidth); // 0  部分属性必须再图片加载完毕后才能获取到其值
console.log(imgObj.offsetLeft);  // 8

imgObj.onload = function() {
    console.log(imgObj.offsetWidth); // 195
    console.log(imgObj.offsetLeft);  // 8
}
</script>

🌟 性能优化:网站首屏加载过慢如何解决

function lazyload() 
{
    // 1 每次获取最新的 可见视口 + 滚动的高度
    // 2 获取所有图片
    // 3 遍历
    // 4 判断:当前图片的.offsetTop < 步骤1(可见视口 + 滚动的高度)
    // 不成立-不管
    // 成立-修改图片的src地址 改成真是的  


    // 1 每次获取最新的 可见视口 + 滚动的高度
    var temp1 = window.innerHeight || document.documentElement.clientHeight  // 兼容ie
    var temp2 = document.body.scrollTop || document.documentElement.scrollTop // 兼容doctype
    var maxShowHeight = temp1 + temp2
    // 2 获取所有图片
    var imgs = document.querySelectorAll('img')
    // 3 遍历
    imgs.forEach(function(item, index) { // item就是每一个图片标签对象
        // 4 判断:当前图片的.offsetTop < 步骤1(可见视口 + 滚动的高度)
        // 不成立-不管
        // 成立-修改图片的src地址 改成真是的  
        // console.log(item, item.offsetTop , maxShowHeight)
        if (item.offsetTop < maxShowHeight)
        {
            // item.src = item.src-real  切记非标签默认属性 不能直接点
            item.src = item.getAttribute('src-real')
        }
    })
}

// TODO: 待后续进一步优化
// 首次
lazyload() 
// 后续
// window.onscroll = lazyload()  错误 undefined
window.onscroll = lazyload

💘 Week3

🌛 谈谈你对事件流机制的理解

术语:事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流

学习:多个标签嵌套,事件触发-先检查当前标签,然后继续向上挨个检查祖先标签有没有事件 有就触发的现象就是事件流

DOM事件流分为三个阶段,分别为:

捕获阶段:事件从Document节点自上而下向目标节点传播的阶段;

目标阶段:真正的目标节点正在处理事件的阶段;

冒泡阶段:事件从目标节点自下而上向Document节点传播的阶段。

🌛 谈谈你对事件委托的理解

概念:利用事件冒泡机制,指定一个事件处理程序,管理某一类型的所有事件。

事件委托的好处:

  • 利用冒泡的原理,将事件注册父级身上,这样只在内存中开辟一块空间,既节省资源又减少DOM操作,提高性能

  • 可以为动态添加的元素绑定事件

🌛 正则

匹配手机号:/^1\d{10}$/.test(数据)

匹配邮箱:/^\w{2,20}@\w{2, 20}.(com|org|cn|edu)$/.test(数据)

匹配中文:/^[\u4e00-\u9fa5]+$/.test(数据)

去所有空格:str.replaceAll(' ', '') 或 str.replace(/\s/g, '')

购物车去非数字:str.replace(/\D/g, '')

🌟 es6新增语法

## 2015ES6

    工作:修饰符、解构赋值、模板字符串、箭头函数、对象简写、展开运算符、新增数据结构、循环forof、module模块化、class类等等
    工作:链判断操作符 ?.
    面试:
    - 修饰符(1-letconst区别,2-暂时性死区)
    - 解构赋值(交换两个变量的值)
    - 箭头函数(1-哪里用 vue/微信小程序/react等项目大量使用,2-有什么用 改变this指向)
    - 谈谈你对this关键词的理解
    - 说一下es6新增了哪些语句

    了解:函数形参默认值、arguments关键词、rest参数、find/findIndex、新增数据类型、空判断操作符??
    留心:
    - 解构赋值没有面试题,但是后续工作大量使用(注:容易忘)
    - 对象简写没有面试题,但是后续工作大量使用(注:容易忘)

## 2016ES7Array.prototype.includes
## 2017ES8Async functions
## 2018ES9Promise.prototype.finally
## 2019ES10
## 2020ES11String.prototype.matchAllimport()、BigIntPromise.allSettled、
## 2021ES12String.prototype.replaceAllPromise.any、
## 2022ES13

🌟 let、const

面试题1:区别

相同点-都是块级作用域/不能重复定义
不同点-let修改/const不能修改

面试题2:暂时性死区

let前面的区域就是暂时性死区,必须先定义再使用

面试题3:如何选

普遍用let、明确后期不改用const 例如Date、xhr、promise对象等

面试题4:const能更改吗

原始类型:不可以
对象类型:可以

原理:const不可以修改栈里面的地址,但是可以修改堆里面的数据

🌟 bind 和 call/apply 的区别

fun.call(thisArg, param1, param2, ...)
fun.apply(thisArg, [param1,param2,...])

fun.bind(thisArg, param1, param2, ...)

是否立刻执行

  • call/apply 改变了函数的 this 上下文后 马上 执行该函数。
  • bind 则是返回改变了上下文后的函数, 不执行该函数

返回值的区别

  • call/apply 返回 fun 的执行结果。
  • bind 返回 fun 的拷贝,并指定了 funthis 指向,保存了 fun 的参数。

🌛 手写bind/call/apply 原理

  • call

apply

bind

💘 Week4

🌟 原型、原型链概念

原型:js给每个构造函数分配的公共空间,减少内存占用

原型链:多个原型的集合,当调用对象的属性或方法时,先在自身找,找不到去原型链上找,一直找到Object构造函数的原型

属性找不到会提示undefined

方法找不到就会报错

🌟 构造函数new干了啥

1 定义obj空对象

2 给obj增加__proto__属性 指向 obj.prototype

3 将 构造函数this中的数据 全部放到obj上

4 返回obj对象

function _new(constructor)
{
 let obj = Object.create(constructor.prototype)
 constructor.call(this)
 return obj
}

<script>
function Stu() {
    this.a = 11
    this.b = 2
}

// 分析1
let obj1 = new Stu   // {a:1,b:2,__proto__:地址1}
console.log(obj1);
/*
栈                     堆
Stu.prototype:地址1    地址1:{constructor,}
obj1:地址2             地址2:{a:1,b:2,__proto__:地址1}
*/

// 分析2
let obj2 = Stu()  // undefined
console.log(obj2);


// 分析3:定义一个_new函数来仿写new关键词
// let obj3 = new Stu
function _new(constructor) {
    // console.log('hello,this is data', constructor);
    // 1 搞obj空对象
    let obj = {}
    // 2 给obj增加__proto__属性 并且指向 obj.prototype
    obj.__proto__ = constructor.prototype
    // 3 将 构造函数this中的数据 全不放到obj上
    // obj = constructor 不行的🚫  将obj改成 Stu构造函数
    // obj.a = 
    // obj.b = 
    // constructor.apply(obj)
    constructor.call(obj)  // 将constructor当函数调用,里面的this改成obj 
    // console.log('搁着呢小哥哥来抓我:',obj);
    // 4 返回
    return obj
}
let obj3 = _new(Stu)
console.log(obj3);
</script>

🇨🇳 箭头函数与普通函数区别?

箭头函数不能作为构造函数

  1. 语法更加简洁、清晰
  2. 箭头函数不会创建自己的this
  3. 箭头函数继承而来的this指向永远不变
  4. .call()/.apply()/.bind()无法改变箭头函数中this的指向
  5. 箭头函数不能作为构造函数使用
  6. 箭头函数没有自己的 arguments( 收集函数的参数 是一个伪数组)
  7. 箭头函数没有原型prototype
  8. 箭头函数不能用作Generator函数,不能使用yeild关键字

juejin.cn/post/684490…

💘 Week5 高频周

- day1

🌟 输入网址浏览器干哈了(简单版)

打开浏览器输入网址回车 ->  浏览器会去DNS服务器找网址相对应的IP地址 -> 找不到网页会显示【无法访问此网站】 找到了【根据ip地址去请求服务器】 -> 服务器返回数据 -> 【浏览器解析】

脚下留心:浏览器其实返回的是index.html的数据,然后在解析的过程中,遇到link、script、img等 再次发送请求拿数据然后解析

🌟 谈谈你对HTTP理解

面试:HTTP是超文本传输协议,规定了客户端和服务端如何通信,然后由请求和相应两个部分组成

学习:就是一个规则,你必须按照这个规则才可以和后端交互拿数据
概念:超文本传输协议
作用:规定客户端和服务端通信技术
场景:网页、APP等
请求组成:请求行(地址/状态吗/请求方式)、请求头(ua、content-type、token、cookie)、请求体(接口参数)
响应组成:响应行(同上)、响应头(暂略)、响应体(接口数据调试错误)

🌟 HTTP周边:HTTP动词

🌟 HTTP周边:状态码

🌟 HTTP周边:请求头参数

  • HTTP动词(请求方式 method)
明确:form标签也就是w3c就是遵循http规则设计的  但是它的请求方式仅仅只有两种get、post 其他不支持
但是:我们目前也用get、post 等到三阶段就用n多
然后:面试就问你有哪些
种类:常用的get查询、post增加数据、put修改数据、delete删除数据
实际:java攻城狮就用get查询 增删改都用post
  • 状态码
2xx  200 成功  201 成功并且服务器创建了新数据
3xx  301 站内跳转   302 站外跳转   304  浏览器缓存
4xx  400 你传递给后端的参数有误  401 密码错误  403 没有权限  404文件不存在  405 请求方式有误
5xx  500 服务器有误
  • 请求头参数
ua、content-type、token、cookie

🌟 HTTP周边:强制缓存、协商缓存(待搞)

🌟 get、和post有什么区别

安全角度:post相对比get安全            因为get会在地址栏 在地址栏就有访问历史记录  post请求体不会存在来留痕
数据角度:post相对传输的数据比get多     get会受到不同浏览器地址栏长度限制,post是服务器配置


上传图片:2M 一般只能上传png、jpg  gif不允许

🌟 谈谈你对http、https的理解,有什么区别

http超文本通讯协议	80
https也是超文本通讯协议  相对http更加安全  443

-day2

🌟 谈谈你对骨架屏的理解

骨架屏就是在页面数据尚未加载前先给用户展示出页面的大致结构,直到请求数据返回后再渲染页面,补充进需要显示的数据内容。常用于文章列表、动态列表页等相对比较规则的列表页面。 很多项目中都有应用:ex:饿了么h5版本,知乎,facebook等网站中都有应用。

参考

p1-jj.byteimg.com/tos-cn-i-t2…

🌟 谈谈你对节流防抖的理解

回答:节流防抖都是用来进行项目优化,减少代码触发的频率,同时又不影响实际效果 节流:一段时间内只执行一次 防抖:一段时间内可重复执行,但是要把之前的取消掉

项目中应用:防止用户过量点击,当一个用户在页面提交登录或者注册需求时,如果因为网速或其他问题,请求无法快速响应,用户可能会大量多次点击发送请求,会造成请求冗余,这时我们就可以使用节流防抖来解决这一现象。(比如再点击过后给登录按钮设置短暂的enabled)

思路:事件中代码用定时器包起来,然后再前面加判断

问题:输入教主会执行 jiaozhu教主 这么多次
标签对象.oninput = function() {
    console.log(1)
}

节流:一段时间内 1let t
标签对象.oninput = function() {
    if (t) return
    t = setTimeout(() => {
        console.log(1)
        t = null
    }, 3000)
} 

防抖:一段时间内可重复执行,但是要把之前的取消掉
let t
标签对象.oninput = function() {
    if (t) clearTimeout(t)
    t = setTimeout(() => {
        console.log(1)
        t = null
    }, 3000)
}

🌟 谈谈你对高阶函数的理解

将其他函数作为参数或返回其他函数的函数称为高阶函数。

高阶函数中作为参数传递的函数被称为回调函数。

高阶函数的优势:

  • 它们可以帮助我们写出简洁的代码,调试工作会更加容易。

现在 JavaScript 有一些内置的高阶函数,你可能已经在不知不觉中就使用它们了,例如 filter()reduce()sort()forEach()

🌟 function+ajax+callback

/**
 * 发送GET请求
 * 技术栈:function + ajax + callback
 * @param {String} url 请求地址 
 * @param {String} params 请求参数   格式:参数名=值&....&参数名=值
 * @param {Function} callback  回调函数
 * @param {Function} headersFn 自定义请求头
 * @return undefined
 */
function get(url, params, callback, headersFn = null)
{
    // 一、 创建xhr对象
    const xhr = new XMLHttpRequest
    // 二、 监听请求状态
    xhr.onreadystatechange = function() 
    {
        // 判断后端返回数据后再处理
        if (xhr.readyState === 4)
        {
            // 判断返回的状态200成功在处理
            if (xhr.status === 200)
            {
                // 获取数据
                let res = JSON.parse(xhr.responseText)
                // 不同逻辑处理
                callback(res)
            } else {
                console.log('请求失败:', xhr.status);
            }
        }
    }
    // 三、 设置请求方式,请求地址
    // xhr.open('get', `请求地址?请求参数`)
    xhr.open('get', `${url}?${params}`)

    if (headersFn) headersFn(xhr)
    
    // 四、 发送
    xhr.send(null)
}

-day3

🌟 谈谈你对promise的理解

概念:ES6中新增的一种异步编程解决方案,主要用于解决ajax回调地狱问题。

作用:常用于封装ajax异步请求

🌟 说一下promise原理

底层为我们创建了Promise构造函数,并且给promise绑定了then、catch、finally等原型方法,和reject、resolve、all(全部成功)、allsettled(没有失败),race(跑得最快的),any(有一个成功就走then,全部失败才走catch)等静态方法。

🌟 说一下promise几个状态

进行中(pendding)(new promise一个函数实参中的代码默认触发)、成功了(resolve)、失败了(reject)

🌟 Promise.all、Promise.allSettled区别

Promise.all( 数组里面是一个个Promise对象 ) 有一个失败就走失败

Promise.allSettled( 数组里面是一个个Promise对象 ) 没有失败

🌟 Promise.race、Promise.any区别

Promise.race( 数组里面是一个个Promise对象 ) 根据第一个最快返回的决定状态

Promise.any( 数组里面是一个个Promise对象 ) 有一个成功就是then 都失败 才是catch

- day4

🌟 跨域相关

  • 跨域导致原因
  • Access-control-allow-origin
概念:当请求一个url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
原因:浏览器安全策略/同源策略
后果:不能跨网站操作发送ajax请求、WEB存储等
  • 跨域解决方案
一般来说跨域这个问题我们前端不负责解决的,主要由后端 运维负责。如果确实有需要的话,一般会用jsonp去解决但是需要和后端配合比较麻烦,而在自己终端时,大多使用前端代理,谷歌插件,谷歌命令。


前端  
前端代理  http-proxy-middleware 
谷歌插件 
谷歌命令
jsonp
websocket
postMessage 


留心:上述不管哪种方式仅自己可以使用
最终:开发就选上面其中一种,最终后端:cors法  原理响应头告诉浏览器任何人都可以使用;nginx 反向代理 

🌛 谈谈你对同步异步的理解

会被加入到浏览器队列的代码称之为异步代码,例如 ajax、setTimeout/setInterval、Promise.then 等等,

他们不按书写✍🏻顺序执行

按照书写顺序执行打印的代码称之为同步代码

🌟谈谈你对async,await的理解

async、await可以将异步执行代码转变为同步!但是其实底层还是异步代码。

async、await是generator的语法糖,(因为async相当于替换了generator中的*键,await相当于generator的yeild)

通过async修饰function、await修饰promise,

底层会将await后面的表达式先执行一遍,再将await下一行及以后的代码加入到微任务中。

🌛 谈谈你generator的理解

es6新增的语法,通过*号修饰函数,当调用函数的时候返回一个generator对象,通过next函数迭代获取函数内部的数据,当遇到yield就会暂停,再次写next才会继续

🇨🇳 说出浏览器运行机制

浏览器主进程,负责创建和销毁tab进程、负责交互前进后退、负责网页文件下载等 渲染进程:每个tab对应一个渲染进程,下面有GUI渲染线程、JS引擎线程、事件线程、定时器线程、异步请求线程 GPU进程:负责3D图绘制 第三方插件进程:负责第三方插件处理,例如跨域、广告拦截插件等

🇨🇳 说出浏览器输入网址干了啥

浏览器输入网址回车
去DNS服务器找网址对应的IP地址 
根据IP地址加端口访问服务器软件 
服务器返回数据
浏览器通过render渲染进程处理,
其中GUI线程负责页面渲染、JS引擎线程负责解析JS代码

🌛 说出JS为什么是单线程

先看一个比喻

进程就是一个公司,每个公司都有自己的资源可以调度;公司之间是相互独立的;而线程就是公司中的每个员工(你,我,他),多个员工一起合作,完成任务,公司可以有一名员工或多个,员工之间共享公司的空间

什么是进程?

进程:是cpu分配资源的最小单位;(是能拥有资源和独立运行的最小单位)

什么是线程?

线程:是cpu调度的最小单位;(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)

浏览器是多进程的

放在浏览器中,每打开一个tab页面,其实就是新开了一个进程,在这个进程中,还有gui渲染线程,js引擎线程,http请求线程等。 所以,浏览器是一个多进程的。

大家都在说js是单线程的,但是为什么要设计成单线程?

这主要和js的用途有关,js是作为浏览器的脚本语言,主要是实现用户与浏览器的交互,以及操作dom;这决定了它只能是单线程,否则会带来很复杂的同步问题。 举个例子:如果js被设计了多线程,如果有一个线程要修改一个dom元素,另一个线程要删除这个dom元素,此时浏览器就会一脸茫然,不知所措。所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变.

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

🌛 说出JS是单线程 为什么不存在执行效率问题

JS是单线程执行程序代码,形成一个执行栈,挨个处理;

但是遇到特别耗费时间的代码 ,例如异步请求,事件等,

不会堵塞等待执行,而是交给浏览器其他线程处理后,再丢到执行栈中处理,从而保证还行效率

🌟 谈谈你对Event Loop的理解

Event Loop即事件循环,

是指浏览器或Node的一种确保javaScript单线程运行时不会阻塞的一种机制,

也就是我们经常使用的异步原理。

种类:浏览器的Event Loop、Node.js中的Event Loop

🇨🇳 谈谈你对浏览器的Event Loop理解

浏览器输入网址服务器响应数据后,

浏览器会通过render进程开始解析工作

GUI线程负责页面渲染

JS引擎线程负责执行JS代码

遇到异步代码会交给其他线程处理,然后放到队列中,

事件循环主要是从队列中取出代码放到执行栈中交给js引擎线程处理

🌟 说出宏任务、微任务各有哪些

单词含义:I input 输入、 O output 输出

用户角度IO操作:鼠标键盘-是计算机输入信息,显示器-是输出设备

电脑角度IO操作:CPU、内存与其他设备之间数据转移过程就是IO操作,例如数据从磁盘读到内存,或者内存写到磁盘

编程角度IO操作:进程读取数据操作

  • 宏任务:
整体代码(script)
定时器(setTimeoutsetInterval)
I/O操作(DOM事件、AJAX异步请求)

setImmediate(node环境)
requestAnimationFrame(浏览器环境)
  • 微任务
Promise.then catch finally
async/await(底层还是promise)
process.nextTick(node环境) 
MutationObserver(浏览器环境)

🌟 说出先执行宏任务还是微任务

算整体代码script:处理一个宏任务将所有微任务清除

不算整体代码script:先n微,再1宏 -> n微,再1宏 -> n微

- day5

🌛 前端存储有几种

回答1:常用的2种,主要是cookie、h5存储;浏览器其实还有web sql、indexdb

H5
cookie
web sql
indexdb

🌟cookie和h5的区别

回答2:答案在下面

H5性能更优:H5存储性能比COOKIE高(因为H5请求头不会携带cookie)

H5存储空间更大:H5单条数据5M左右、COOKIE单条数据4KB

生命周期:

cookie 			       自己设置,如果不设置浏览器关闭销毁
h5 localStorage    永久
h5 sessionStorage  窗口

🌟如何实现localStorage7天过期

回答3:

cookie.  1*1000*60*60*24*7


步骤1:存数据的时候 也额外存一个键叫 expires  记录时间
	localStorage.setItem('uname', 'value')
	localStorage.setItem('expires', (new Date).getTime() + 1*1000*60*60*24*7 )
步骤2:使用的时候增加判断
	// 公式:当前使用时间 > 存储时间 (过期了)
	// 举例:比如你是2月1号存 
	// 然后:你加了7天  2月8号过期
	// 最后: 2月5号 > 2月8号   不成立 没过期
	// 最后: 2月11号 > 2月8号  成立  过期

🌟如何实现七天免登录

登陆成功的基础上在H5中存储一个expires键,值为时间;(new Date().getTime()+????)

取当前时间与H5设置时间相对比,超过即失效,没超过则存储数据继续有效。

🌟登录是如何实现的

首先获取登录按钮绑定点击事件,

事件处理函数中:

第一步阻止浏览器默认动作 e.preventDefault

第二步获取input框的数据,发送请求,判断响应状态,

成功:1:弹框提示 2:H5存储数据 3:跳转页面

失败:1:弹框提示

💘 Week6 较为低频

🌛 xhr、.get、\.ajax区别

问题1XMLHttprequestJQ代码的区别
回答1:
相同点:都可以发送异步请求
不同点:JQ是基于XMLHttprequest封装了 
1 更简单、 2 解决兼容问题等

问题2:$.get、$.ajax,$.post区别
回答2:一样,唯一区别$.ajax比$.get、$.post语法上更强

🌛 jq你会吗,比如操作样式用什么属性...

获取标签:CSS选择器、筛选选择器、过滤选择器等
操作标签:样式css方法、类addClass、属性attr/prop、内容val/html/text、节点append/remove
事件:$().事件类型()     $().on(事件类型,子,处理函数)
异步请求:$.get、$.post、$.ajax
动画:$.animate() 
网页加载完毕:$(function(){})    (留心:和window.onload区别 
对象相互转换:$()[索引]   或者  $(JS标签对象)

💘 Week7 高频

🌟 说出几个常用的git命令

配置SSH  ssh-keygen
用户配置  git config --global user.name/email ''
获取代码 
git clone 仓库地址
git init 
git remote add origin 仓库地址
git remote remove origin 仓库地址
增删改:git add . / commit -m / push

周边(忽略文件) .gitignore
周边(代码冲突) git pull git status 
周边(日志回滚)  git log 、 git reset 
周边(分支)    git branch / git checkout ...
周边(其他)    git stash、git tag、git pull、git fetc、git merge

🌟 说出git工作流,说一下你们日常开发工作流

按照git flow
老大给需求 就由dev创建功能分支进行开发
dev完毕提交到测试分支release分支
测试完毕提交到master
功能分支-dev分支-测试分支-master分支,如果出现bug则在master分支下新建hotfix分支,解决bug后,再次push

🌟 说下git如何解决代码冲突

push时发现代码冲突则pull最新数据至本地,然后Git status 检查冲突来源,手动解决后,重新add ./commit/push

🌟 说一下你在开发登陆时,线上出现bug了如何维护

基于master创建hotfix分支,
解决了再 push 并 merge 到 master、dev 上

🌟 克隆获取的是默认分支代码,如何获取其他分支代码

git checkout 分支名
git checkout -b 分支名 						 基于当前分支创建新分支并切换至新分支
git checkout -b 新分支名 旧分支名 		基于旧分支创建新分支并切换至新分支

🌟 用过哪些git图形化可视软件

编辑器
sourcetree  
等等

🌟 说下git和svn区别

git是分布式、svn是集中式
svn相对容易冲突
等