1、为什么要用 setTimeout 模拟 setInterval,以及代码实现 ?
每个 setTimeout 产生的任务会直接 push 到任务队列中;而 setInterval 在每次把任务 push 到任务队列前,
都要进行一下判断(看上次的任务是否仍在队列中,如果有则不添加,没有则添加
let timer = null
interval(func, wait){
let interv = function(){
func.call(null)
timer=setTimeout(interv, wait)
}
timer= setTimeout(interv, wait)
}
2、如何实现一个深拷贝(考虑循环引用对象、和symbol类型)?
方法一:
function cloneDeep4(source, hash = new WeakMap()) {
if (!isObject(source)) return source
if (hash.has(source)) return hash.get(source)
let target = Array.isArray(source) ? [] : {}
hash.set(source, target)
let symKeys = Object.getOwnPropertySymbols(source)
if (symKeys.length) { // 查找成功
symKeys.forEach(symKey => {
if (isObject(source[symKey])) {
target[symKey] = cloneDeep4(source[symKey], hash)
} else {
target[symKey] = source[symKey]
}
})
}
for(let key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (isObject(source[key])) {
target[key] = cloneDeep4(source[key], hash)
} else {
target[key] = source[key]
}
}
}
return target
}
方法二:
function cloneDeep4(source, hash = new WeakMap()) {
if (!isObject(source)) return source
if (hash.has(source)) return hash.get(source)
let target = Array.isArray(source) ? [] : {}
hash.set(source, target)
Reflect.ownKeys(source).forEach(key => {
if (isObject(source[key])) {
target[key] = cloneDeep4(source[key], hash)
} else {
target[key] = source[key]
}
})
return target
}
3、修改this指向的方法以及区别,箭头函数可以修改this指向吗,为什么?
call,apply,bind;
call,apply的区别是,第二个参数,apply接受一个数组,call是有多少个参数,就依次的写多少个就行。
bind和call,apply的区别是:bind的返回函数不会立即执行,而call,apply的会。
bind的第二个参数和call的第二个参数一样。
4、 浏览器的垃圾回收,以及v8引擎的垃圾回收?
浏览器的垃圾回收:1、标记清楚;2、引用计数;
v8引擎的:
栈内存:调用栈上下文切换后被回收;
堆内存:
·新生代内存回收机制:
新生代内存容量小,64位系统下仅有32M。新生代内存分为From、To两部分,
进行垃圾回收时,先扫描From,将非存活对象回收,将存活对象顺序复制到To中,
之后调换From/To,等待下一次回收
·老生代内存回收机制
晋升:如果新生代的变量经过多次回收依然存在,
那么就会被放入老生代内存中
标记清除:老生代内存会先遍历所有对象并打上标记,
然后对正在使用或被强引用的对象取消标记,回收被标记的对象
整理内存碎片:把对象挪到内存的一端
5、js的数据类型有哪些?基础数据类型和复杂数据类型的区别?判断数据类型的方法以及原理?
·基本数据类型:Boolean,Undefined,Null,Number,BigInt,String, Symbol
·引用(复杂)数据类型:object
区别:
基本数据类型存储在:栈内存
引用数据类型存储在:引用地址存储在栈内存,实际值存储在堆内存。
方法以原理:
instanceof;
只要右边变量的 prototype 在左边变量的原型链上即可
constructor;
当一个函数F被定义时,JS引擎会为F添加prototype原型,
然后再在prototype上添加一个constructor属性,并让其指向F的引用
typeof(这个可以不说原理);
typeof 目前能返回string,number,boolean,symbol,
bigint,undefined,object,function这八种判断类型
Object.prototype.toString(这个是判断类型最准的方法)
toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,
更严格的讲,是 toString运行时this指向的对象类型,
返回的类型格式为[object,xxx],xxx是具体的数据类型
6、浏览器的事件机制
大致:一串js代码,从上而下执行,同步任务直接执行,异步任务会有宏任务和微任务。
在这过程中,宏任务会放到宏任务队列,微任务会放到微任务队列。
当同步代码执行完毕后,会先将微任务队列里面的微任务执行清空,
然后再去宏任务队列拿一个宏任务执行,
在这执行过程中,有微任务,就将它放到微任务队列,
等该宏任务执行完毕,再执行微任务队列里面的任务,直到清空,
再去宏任务队列,拿去一个宏任务,依次循环。
看代码说出打印结果:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start');
setTimeout(function () {
console.log('setTimeout')
new Promise(function (resolve) {
console.log('setTimeout - promise1');
resolve()
}).then(function () {
console.log('setTimeout - promise2')
new Promise(function (resolve) {
console.log('setTimeout - setTimeout - promise1');
resolve()
}).then(function () {
console.log('setTimeout - setTimeout - promise2')
});
});
}, 0);
setTimeout(function () {
console.log('setTimeout2')
}, 0);
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve()
}).then(function () {
console.log('promise2')
});
console.log('script end')
结果:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
setTimeout - promise1
setTimeout - promise2
setTimeout - setTimeout - promise1
setTimeout - setTimeout - promise2
setTimeout2
7、css 的伪类和伪元素有哪些?有什么区别?
8、webpack 优化的手段
9、webpack的热更新原理
10、react diff算法
11、react常用的hooks
12、react的性能优化
13、useMemo,useCallback,react.memo的区别
14、调用setState之后发生了什么
15、前端常用的性能优化
16、http2和http1.1的比较
17、webpack动态加载的原理
18、promise.all是什么?如果有一个失败了,其他没有失败,还要想得到结果怎么办?
21、为什么会出现跨域问题,怎么解决?
22、说一下浏览器的缓存
23、怎么做响应式开发
24、输入url之后发生了什么
25、ts中interface和type的区别
[https://blog.csdn.net/glorydx/article/details/112625953](https://blog.csdn.net/glorydx/article/details/112625953)