啊啊啊啊啊啊 promise好难啊!救救我!
今天接了一个电话,感觉自己信息被卖了,因为没有投过他们公司的简历,像是骗人进去培训交钱然后去外包的意思…
Promise
是异步编程的一个解决方案,主要用来解决回调地狱,一般是有异步请求操作时,会对Promise进行封装。
Promise是一个容器,里面保存这某个未来才会结束的事件的结果(一般是一个异步操作)。
三种状态
pending(进行中) 等待状态,比如正在进行网络请求,或者定时器没有到时间的时候。
fulfilled(已成功) 满足状态,当我们主动回调了resolve()时,接口调用成功,就处于该状态,并且会回调.then()
rejected(已失败)拒绝状态,当我们主动回调了reject()时,接口调用失败,就处于该状态,并且会回调.catch()
一旦把异步代码放到Promis后,异步状态就会马上变成pending,异步代码执行后只会是fulfilled或者rejected的其中一个状态。(会出现一次,互斥)
Promise基本使用
- 是一个构造函数
- 接受一个函数作为参数(异步代码或者同步代码都可以)
- 一旦promise被实例化,容器里的代码就会执行,状态就会马上变成pending
new Promise(resolve,reject)默认resolve,reject传入两个参数,这两个是Promise自带的函数对象。
resolve——成功:接口请求成功
reject ——拒绝:接口请求失败
一旦异步操作有了结果,如果的 fullfiled 状态,则我们需要通过 resolve 来调用 then 方法传递的第一个函数
一旦异步操作有了结果,如果的 rejected 状态,则我们需要通过 reject 来调用 then 方法传递的第二个函数
这是一个then().catch()就是简单的链式调用
new Promise((resolve,reject)=>{
// 第一次网络请求
setTimeout(()=>{
resolve() //调用成功函数
reject('error message') //调用失败
},1000)
}).then(()=>{
//调用成功后
console.log('第一次调用成功')
})
}).catch((err)=>{
//调用失败后抓住错误信息
console.log(err) //error message
})
还有省略.catch()写法 ,then()传入两个函数对象,第一个函数对象内写请求成功函数,第二个函数对象内写请求失败函数。
new Promise((resolve,reject)=>{
// 第一次网络请求
setTimeout(()=>{
resolve('调用成功')
reject('error message')
},1000)
}).then(data=>{
//请求成功
console.log(data) //调用成功
},err=>{
//请求失败
console.log(err) //error message
})
拿到所有请求接口数据再做操作:Promise.all()
// 在all里写入多个请求
Promise.all([
// 第一个请求
$ajax({
url:'url1',
success:function(data){
resolve(data)
}
}),
// 第一个请求
$ajax({
url:'url2',
success:function(data){
resolve(data)
}
}),
]).then(results=>{
// 在.then里同时处理两个请求接口返回的数据
// results是一个数组对象,将接口1接口2的数据合并了
// results[0]:接口1返回数据,results[1]:接口2返回数据
})
JS判断数据类型的几种方法
typeof(typeof null的值为Object无法分辨是null还是Object)
instanceof(只能判断对象是否存在于目标对象的原型链上)
constructor
Object.prototypr.toString.call()
可以区分null、string、boolean、number、undefined、array、function、object、date、math数据类型,不能细分谁是是的实例
为什么typeof null是Object?
因为在JS中,不是所有对象都是用二进制存储的,如果二进制前三位都是0的话,系统会判断为Object类型,而null的二进制全部都是0,就被判断为Object了。
000 对象
1 整型
010 双精度字符
100 字符串
110 布尔类型
==
和 ===
的区别?
===
是严格意义上的相等,会比较两边的数据类型和值的大小。
数据类型不同、数据类型相同但是值不同都会返回false
==
是非严格意义上的相等
两边类型相同比较大小
两边类型不同会转换类型再比较大小。
- Null == Undefined->true
- Boolean/String == Number->先转为number再比较
- Object == String,Number,Symbol -> Object 转化为原始类型
JS中常用的继承方式?
-
原型继承
- 把父类的实例作为子类的原型
- 子类实例共享了父类构造函数的引用属性 不能传参
-
组合继承
- 在子函数中运行复函数,需要利用call把this改变
- 在子函数的prototype里面new Father()
- 可传参 不共享父类的引用属性,使Father的原型中的方法也得到继承
- 调用了两次父类的构造函数,造成了不必要的消耗,父方法可复用
-
寄生组合继承
-
ES6 中的extend
- 子类只要继承父类,可以不写constructor,如果写了constructor中的第一句话必须是super
深拷贝和浅拷贝
深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。
浅拷贝只复制一层对象的属性,而深拷贝则递归复制了所有层级。
举例:
假设B复制了A,修改A的时候,看B是否发生变化:
如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值)
如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中的不同的值)
浅拷贝只会将对象的各个属性进行依次复制,并不会进行递归复制,而 JavaScript 存储对象都是存地址的,仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。
深拷贝则不同,它不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。在计算机中开辟一块新的内存地址用于存放复制的对象。(一般用hash递归)
为什么要使用模块化?
防止命名冲突
更好的分离,按需加载
更好的复用性
更高的维护性
exports和module.exports有什么区别?
导出方式不一样:
exports.xxx=‘xxx’
module.export={}
exports是module.exports的引用,两个指向的是用一个地址,而require能看到的只有module.exports
JS模块包装格式有哪些?
commonjs:同步运行,不适合前端
AMD:异步运行,require.js,主要采用异步的方式加载模块,模块的加载不影响后面代码的执行,所有依赖这个模块的语句都写在一个回调函数中,模块加载完毕,在执行回调函数。
CMD:异步运行,seajs
ES6和Commonjs的区别
commonjs模块输出的是值的拷贝,ES6输出的是值的引用
commonjs是在运行时加载,是一个对象,ES6是在编译时加载,是一个代码块
commonjs的this指向当前模块,ES6的this指向undefined
tcp和udp的区别?
连接方面:tcp面向连接 udp不需要连接,tcp需要三次握手四次挥手请求连接
可靠性:tcp是可靠传输,一旦传输过程中丢包的话会进行重传,udp是不可靠传输,但会最大努力交付
工作效率:tcp工作效率高,udp实时性高,因为不需要建立连接,更不需要复杂的握手以及挥手复杂的算法,也没有传重机制。
是否支持多对多:tcp是点对点,udp支持一对一,一对多,多对多。
首部大小:tcp首部占20字节,udp首部占8字节
什么是防抖?什么是节流?
防抖
- n秒在执行该事件,如果在n秒内被重复触发,则重新计时
// 防抖函数
function debounce(func, delay) {
let timeout
return function () {
let arg = arguments
if (timeout) clearTimeout(timeout)
timeout = setTimeout(() => {
func(arg)
}, delay);
}
}
// 立即执行防抖函数
function debounce2(fn, delay) {
let timer
return function () {
let args = arguments
if (timer) clearTimeout(timer)
let callNow = !timer
timer = setTimeout(() => {
timer = null
}, delay);
if (callNow) { fn(args) }
}
}
节流
- n秒内只允许一次,如果n秒内重复触发,只有一次生效
// 节流 ,时间戳版
function throttle(fn, wait) {
let previous = 0
return function () {
let now = Date.now()
let _this = this
let args = arguments
if (now - previous > wait) {
fn.apply(_this, arguments)
previous = now
}
}
}
// 节流 ,定时器版
function throttle2(fn, wait) {
let timer
return function () {
let _this = this
let args = arguments
if (!timer) {
timer = setTimeout(() => {
timer = null
fn.apply(_this, arguments)
}, wait);
}
}
}
JS中常见的设计模式
单例模式:不管创建多少个对象都只有一个实例
工厂模式:代替new创建一个对象,且这个对象像工厂制作一样,批量制作属性相同的实例对象(指向不同)
构造函数模式
发布订阅者模式:更新后会通知每一个观察者做出更新
代理模式
迭代器模式
Vue中父子组件的生命周期
父子组件的生命周期是一个嵌套的过程
渲染的过程
- 父beforeCreate -> 父Created -> 父beforeMount -> 子beforeCreate -> 子created ->子beforeMount -> 子mounted -> 父mounted
子组件更新过程
- 父boforeUpdate -> 子beforeUpdate -> 子Updated -> 父Updated
父组件更新过程
- 父beforeUpdate -> 父Updated
销毁过程
- 父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
Vue中的nextTick
nextTick:在下次DOM更新循环结束之后执行延迟回调,在修改数据之后立即使用这个方法,获取更新之后的DOM。
vue改变dom元素结构后使用vue.$nextTick()方法来实现dom数据更新后延迟执行后续代码
computed和watch的区别
-
computed:计算机属性,依赖其他属性,当其他属性改变的时候下一次获取computed值时也会改变,computed的值会有缓存。
-
watch:监听到值的变化就会执行回调,支持异步,无缓存。如果想要深度监听,在后面加deep:true。如果想要监听完立马运行的话,后面加一个immediate:true。
Vue-router的模式
- hash模式:
- 监听hashchange事件实现前端路由,利用url中的hash来模拟一个hash,来保证url改变的时候页面不会重新加载。
- history模式:
- 利用pushstate和replacestate来将url替换但不刷新,但是有一个致命点,一旦刷新就可能会404,因为没有当前真正的路径,想要解决这一问题就需要后端的配合,将不存在的路径重定向到入口文件中。
Vue-router有哪几种钩子函数?
-
beforeEach[全局前置守卫]
- 参数有:
- to(Router路由对象)即将要进入的目标路由对象
- from(Router对象)当前导航正要离开的对象
- next(Function函数)一定要调用该方法来resolve这个钩子才能进行下一步。
- 可进行一些页面跳转前处理,例如判断需要登录的页面进行拦截,做登录跳转!!
- 应用场景,进入页面登录判断、管理员权限判断、浏览器判断
-
afterEach(全局后置守卫)
-
beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave(组件内钩子-组件内的守卫)