同步和异步的区别
js是单线程的,同步代码会阻塞运行,异步不会阻塞
手写Promise加载图片
loadImg(url) {
return new Promise((resolve,reject) => {
const img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
resolve('加载出错了')
}
img.src = url
})
}
讲讲event loop(事件循环/事件轮询)机制
JS如何执行
Promise的三种状态
Promise中的then方法和catch方法对状态的影向
async/await 和 Promise 的关系
一道笔试题,输出执行结果
async function test1() {
console.log('test1 start') //2
await test2()
console.log('test2 end') //5
await test3()
console.log('test3 end') //7
}
async function test2() {
console.log('test2 start') //3
}
async function test3() {
console.log('test3 start') //6
}
console.log('script start') //1
test1()
console.log('script end') //4
宏任务和微任务
二者的执行时间:
微任务 --》 DOM渲染 --》 宏任务
执行顺序是:
验证执行顺序
// 修改 DOM
const $p1 = $('一段文字
')
const $p2 = $('一段文字
')
const $p3 = $('一段文字
')
$('#container')
.append($p1)
.append($p2)
.append($p3)
// // 微任务:渲染之前执行(DOM 结构已更新)
Promise.resolve().then(() => {
const length = $('#container').children().length
alert(`micro task ${length}`)
})
// 宏任务:渲染之后执行(DOM 结构已更新)
setTimeout(() => {
const length = $('#container').children().length
alert(`macro task ${length}`)
})
async function async1 () {
console.log('async1 start') // 2
await async2()
console.log('async1 end') // 6
}
async function async2 () {
console.log('async2') // 3
}
console.log('script start') // 1
setTimeout(function () {
console.log('setTimeout') // 8
}, 0)
async1()
new Promise (function (resolve) {
console.log('promise1') // 4
resolve()
}).then (function () {
console.log('promise2') // 7
})
console.log('script end') // 5
class myPromise{
state = 'pending' //状态 'pending' 'fulfilled' 'rejected'
value = undefined //成功回调的值
reason = undefined //失败回调的值
resolveCallbacks = [] // pending状态下,存储成功的回调
rejectCallbacks = [] // pending状态下,存储失败的回调
constructor(fn) {
const resolveHandler = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled'
this.value = value
this.resolveCallbacks.forEach(fn => fn(this.value))
}
}
const rejectHandler = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected'
this.reason = reason
this.rejectCallbacks.forEach(fn => fn(this.reason))
}
}
try{
fn(resolveHandler,rejectHandler)
} catch (err) {
rejectHandler(err)
}
}
then(fn1, fn2) {
fn1 = typeof fn1 === 'function' ? fn1 : (v) => v
fn2 = typeof fn2 === 'function' ? fn2 : (e) => e
if (this.state === 'pending') {
const p1 = new myPromise((resolve,reject) => {
this.resolveCallbacks.push(() => {
try {
const newValue = fn1(this.value)
resolve(newValue)
} catch (e) {
reject(e)
}
})
this.rejectCallbacks.push(() => {
try{
const newReason = fn2(this.reason)
reject(newReason)
} catch (e) {
reject(e)
}
})
})
return p1
}
if (this.state === 'fulfilled') {
const p1 = new myPromise((resolve,reject) => {
try {
const newValue = fn1(this.value)
resolve(newValue)
} catch (e) {
reject(e)
}
})
return p1
}
if (this.state === 'rejected') {
const p1 = new myPromise((resolve,reject) => {
try{
const newReason = fn2(this.reason)
reject(newReason)
} catch (e) {
reject(e)
}
})
return p1
}
}
catch(fn) {
return this.then(null, fn)
}
}
myPromise.resolve = function(value) {
return new myPromise((resolve,reject) => resolve(value))
}
myPromise.reject = function(reason) {
return new myPromise((resolve,reject) => reject(reason))
}
myPromise.all = function(promiseList = []) {
if(!Array.isArray(promiseList)) throw new typeError('请输入一个数组')
const p1 = new myPromise((resolve,reject) => {
let resolvedCount = 0
let result = []
const length = promiseList.length
promiseList.forEach((p,index) => {
p.then(data => {
result[index] = data
resolvedCount ++
if (length === resolveCount) {
resolve(result)
}
}).catch(err => {
reject(err)
})
})
})
return p1
}
myPromise.race = function(promiseList = []) {
if(!Array.isArray(promiseList)) throw new typeError('请输入一个数组')
let resolved = false
const p1 = new myPromise((resolve,reject) => {
promiseList.forEach(p => {
p.then(data => {
if(!resolved) {
resolve(data)
resolved = true
}
}).catch(err => {
if(!resolved) {
reject(data)
resolved = true
}
})
})
})
return p1
}
补充:catch后无法再传递下去
document.getElementById() //单个
document.getElementsByTagName() //集合
document.getElementsByClassName() //集合
document.querySelector() // 单个
document.querySelectorAll() //集合
s
const b = document.querySelector('div')
b.a = 100 //不会体现在Html中
b.setAttribute('b', 100) //会体现的html中
DOM性能优化
location的一些相关知识
编写一个通用的事件监听函数
function bindEvent(ele, type, selector, fn) {
if (fn == null) {
fn = selector
selector = null
}
ele.addEventListener(type, event => {
const target = event.target
if(selector) {
if (target.matches(selector)) {
fn.call(target, event)
}
} else {
fn.call(ele, event)
}
})
}
补充: event.preventDefault(); 阻止默认事件发生
描述事件冒泡的流程
在嵌套的父子元素中,子元素的事件会一层一层向上传递,一直到顶层元素window对象
补充:event.stopPropagation(); 可以阻止捕获和冒泡
什么是事件代理
事件代理就是在父元素上绑定事件,然后通过父元素的事件来处理子元素的相关操作。
好处是代码简洁,减少浏览器内存
手写XMLHttpRequest
const xhr = new XMLHttpRequest()
xhr.open('GET', '/api', true) // true代表异步
let res
xhr.onreadystatechange = function () {
if(xhr.readState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText)
res = xhr.responseText
} else {
console.log('其他情况')
}
}
}
xhr.send(res)
readyState的几种状态
什么是同源策略
那些情况可以无视同源策略
如何理解jsonp
关于axios了解那些
如何封装axios
关于cookie,localStorage和sessionStorage的理解
HTTP常见的状态码有那些
HTTP常见的headers有那些
Request Headers
Response Headers
什么是Restful API
传统的api设计是把每个url当成一个功能,而Restful api是把每个url当成一个唯一的资源
通过get(获取数据),post(新建数据),patch/put(更新数据)以及delete(删除数据)来判断对资源的操作,尽量不使用url参数
描述一下HTTP的缓存机制
为什么要使用缓存? 可以减少网络请求的数量和体积,提高页面显示速度,使用户体验更好
那些资源可以被缓存? 静态资源(js,css,img)
什么是强制缓存?
设置Cache-control(Response Headers中):max-age(设置过期时间),no-cache(强制向服务器发送请求,由服务器判断该资源是否有更新,有则返回新内容,没有则使用缓存),no-store(不用本地缓存,也不使用服务端的缓存措施),private(针对个人用户),public(允许中间代理或路由进行缓存处理)
什么是协商缓存?
由服务器判断客户端资源是否和服务端资源一样,一致则返回304,否则返回200和最新的资源
第一次请求时,会返回资源和资源标识,通过判断这个资源标识来辨别资源是否有更新
资源标识(Response Headers中):
刷新状态对于缓存的影响
常用的git命令
移动端h5页面抓包工具
常见的Linux命令
webpack基本配置
基本的webpack项目配置中TreeShaking/热更新/Shimming/CodeSplitting
从输入url到渲染出页面的整个过程
console.log(a) // function a() {}
var a= 100
function a() {}
----------------------------------
console.log(a) // function a() {}
var a= 100
function a() {}
console.log(a) // 100
typeof能判断那些类型
手写深度比较isEqual
function isObject(obj) {
return typeof obj === 'object' && obj !== null
}
function isSameType(obj1,obj2) {
return Object.prototype.toString.call(obj1) === Object.prototype.toString.call(obj2)
}
function isEqual(obj1,obj2){
if (!isObject(obj1) || !isObject(obj2)) {
return obj1 === obj2
}
if (!isSameType(obj1,obj2)) {
return false
}
if (obj1 === obj2) {
return true
}
const key1 = Object.keys(obj1)
const key2 = Object.keys(obj2)
if(key1.length !== key2.length) {
return false
}
for (const key in obj1) {
if (!obj2.hasOwnProperty(key)) {
return false
}
const res = isEqual(obj1[key],obj2[key])
if(!res) {
return false
}
}
return true
}
介绍下数组中的pop,push,unshift,shift
讲讲纯函数,以及那些是纯函数
纯函数的特征:
纯函数有那些
数组slice和splice的区别
new Object() 和 Object.create()的区别
const obj1 = {a:1,b:2}
const obj2 = new Object(obj1)
// obj2 === obj1
const obj3 = new Object({a:1,b:2})
// obj3 !== obj1
常见的正则表达式语法
手写字符串trim方法,保证浏览器的兼容性
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+/, '').replace(/\s+$/, '')
}
}
讲讲json
手写函数用于解析url参数
function query(name) {
const search = location.search.substr(1)
const reg = new RegExp(`(^|&)&{name}=([^&]*)(&|$)`,'i')
const res = search.match(reg)
if (res === null){
return null
}
return res[2]
}
function query(name) {
const search = location.search
const p = new URLSearchParams(search)
return p.get(name)
}
const arr = [].concat.apply([],arr)
function flatern(arr) {
if(!Array.isArray(arr)) return false
const result = arr.some(item => item instanceof Array)
if(!result) {
return arr
}
const res = Array.prototype.concat.apply([],arr)
return flatern(res)
}
function unique(arr) {
let res = []
arr.forEach(item => {
if(!res.includes(item)){
res.push(item)
}
})
return res
}
function unique(arr) {
const res = new Set(arr)
return [...res]
}
function deepClone(obj) {
if (typeof obj !== 'object' || obj == null) {
return obj
}
let result
if(obj instanceof Array) {
result = []
} else {
result = {}
}
for(const i in obj) {
if(obj.hasOwnProperty(i)) {
result[i] = deepClone(obj[i])
}
}
return result
}
常见的假值
watch和computed的区别
原型,原型链和继承 这篇文章讲的很简单易懂
代码实践
class Person {
constructor(name) {
this.name = name
}
eat() {
console.log(`${this.name}在吃东西`)
}
}
class Student extends Person{
constructor(name,number) {
super(name)
this.number = number
}
study(){
console.log(`学号为${this.number}的学生${this.name}正在学习`)
}
}
class Teacher extends Person{
constructor(name,major) {
super(name)
this.major = major
}
teach() {
console.log(`${this.name}是教${this.major}的老师`)
}
}
// 实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name) // 夏洛
console.log(xialuo.number) // 100
xialuo.study() // 学号为100的学生夏洛正在学习
xialuo.eat() // 夏洛在吃东西
// 实例
const wanglaoshi = new Teacher('王老师', '语文')
console.log(wanglaoshi.name) // 王老师
console.log(wanglaoshi.major) // 语文
wanglaoshi.teach() // 王老师是教语文的老师
wanglaoshi.eat() // 王老师在吃东西
// 关系检测 ---> 都为true
console.log(xialuo.__proto__ === Student.prototype)
console.log(Student.prototype.constructor === Student)
console.log(Student.prototype.__proto__ === Person.prototype)
console.log(Person.prototype.constructor === Person)
console.log(Person.prototype.__proto__ === Object.prototype)
console.log(Object.prototype.constructor === Object)
function Person(name) {
this.name = name
this.eat = function() {
console.log(`${this.name}在吃东西`)
}
}
function Student(name,number) {
Person.call(this,name)
this.number = number
this.study = function() {
console.log(`学号为${this.number}的学生${this.name}正在学习`)
}
}
Student.prototype = new Person()
function Teacher(name,major) {
Person.call(this,name)
this.major = major
this.teach = function() {
console.log(`${this.name}是教${this.major}的老师`)
}
}
Student.prototype = new Person()
// 实例
const xialuo = new Student('夏洛', 100)
console.log(xialuo.name) // 夏洛
console.log(xialuo.number) // 100
xialuo.study() // 学号为100的学生夏洛正在学习
xialuo.eat() // 夏洛在吃东西
// 实例
const wanglaoshi = new Teacher('王老师', '语文')
console.log(wanglaoshi.name) // 王老师
console.log(wanglaoshi.major) // 语文
wanglaoshi.teach() // 王老师是教语文的老师
wanglaoshi.eat() // 王老师在吃东西
// 关系检测 --->除了第二个都为true
console.log(xialuo.__proto__ === Student.prototype)
console.log(Student.prototype.hasOwnProperty('constructor')) // false 表明Student.prototype没有'constructor'。它调用的是Person.prototype的'constructor'
console.log(Student.prototype.constructor === Person)
console.log(Student.prototype.__proto__ === Person.prototype)
console.log(Person.prototype.constructor === Person)
console.log(Person.prototype.__proto__ === Object.prototype)
console.log(Object.prototype.constructor === Object)
function foo(){}
var a=new foo();
console.log(a instanceof foo);//true
function foo(){}
var a=new foo();
console.log(foo.prototype.isPrototypeOf(a));//true
class jQuery {
constructor(selector) {
const result = document.querySelectorAll(selector)
const length = result.length
for (let i = 0; i < length; i++) {
this[i] = result[i]
}
this.length = length
this.selector = selector
}
get(index) {
return this[index]
}
each(fn) {
for (let i = 0; i < this.length; i++) {
const elem = this[i]
fn(elem)
}
}
on(type, fn) {
return this.each(elem => {
elem.addEventListener(type, fn, false)
})
}
// 扩展很多 DOM API
}
// 插件
jQuery.prototype.dialog = function (info) {
alert(info)
}
// “造轮子”
class myJQuery extends jQuery {
constructor(selector) {
super(selector)
}
// 扩展自己的方法
addClass(className) {
}
style(data) {
}
}
// const $p = new jQuery('p')
// $p.get(1)
// $p.each((elem) => console.log(elem.nodeName))
// $p.on('click', () => alert('clicked'))
function fn1(){
fn1.a = 10
console.log(this.a)
}
const a = 1oo
fn1() // 100
function fn2(){
console.log(this.a)
}
const obj = {
a: 10,
fn2
}
obj.fn2() // 10
注意: 当有多个嵌套时,只会绑定最初的那个。如:
function fn2(){
console.log(this.a)
}
const obj = {
a: 10,
fn2
}
const obj2 = {
obj,
a: 20
}
obj2.obj.fn2() // 10
function fn3(){
console.log(this.a)
}
const obj = { a: 10}
function fn4() {
return fn3.call(obj)
}
const a = 20
fn4() // 10
window.fn4() // 10
function fn5(a){
this.a=a;
}
const bar=new fn5(2);
console.log(bar.a);//输出 2
function fn6(){
setTimeout(function(){
console.log(this.a);
},1000);//输出1 --> 指向window
setTimeout(()=>{
console.log("====");
console.log(this.a);
},2000);//输出10 --> 指向obj
}
const a=1;
const obj={a:10}
fn6.call(obj);
if(!Function.prototype.softBind){
Function.prototype.softBind=function(obj){
var fn=this;
var args=Array.prototype.slice.call(arguments,1);
var bound=function(){
return fn.apply(
(!this||this===(window||global))?obj:this,
args.concat.apply(args,arguments)
);
};
bound.prototype=Object.create(fn.prototype);
return bound;
};
}
function createCache() {
const data = {}
return {
set: function(key,val) {
data[key] = val
},
get: function(key) {
return data[key]
}
}
}
function fn1() {
const a = 100
return function() {
console.log(a)
}
}
const a = 200
const fn2 = fn1()
fn2() // a = 100
function fn1(fn) {
const a = 100
fn()
}
const a=200
function fn2() {
console.log(a)
}
fn1(fn2) // 200
// 所有的自由变量的查找,是在函数定义的地方,向上级作用域查找
// 不是在执行的地方!!!
有序和无序区别
Map和Object区别
const obj = {
a:1
}
const map = new Map([
['c' , '3'],
[obj, true]
])
map.set({a:1},1)
map.set(function aa(){},'2')
map.delete('c')
console.log(map.has(obj))
console.log(map.get(obj))
console.log(map)
const set = new Set([1,2,3,4,3])
console.log(set)
set.add(5)
set.has(3)
set.delete(2)
set.clear()
WeakMap和WeakSet(了解)
reduce的简单了解和使用
const arr = [1,2,3,4,5,6]
const sum = arr.reduce((sum,value) => sum + value)
console.log(sum)
const arr = [1,2,3,4,5,1,2,1,3,3,5]
const countNames = arr.reduce((counts,value) => {
if (value in counts) {
counts[value] += 1
} else {
counts[value] = 1
}
return counts
},{})
console.log(countNames)
const arr = [
{ name: '张三', age: '20' },
{ name: '李四', age: '21' },
{ name: '小明', age: '22' }
]
const str = arr.reduce((str,val) => {
return `${str}${val.name} - ${val.age}\n`
},'')
console.log(str)
const arr = [1,2,3,[1,2,3],[1,2,3]]
const newArr = arr.reduce((newarr,val) => newarr.concat(val),[])
console.log(newArr)
const arr = [1,2,3,1,3,2,4,5]
const newArr = arr.reduce((newarr,val) => {
if (!newarr.includes(val)) {
newarr.push(val)
}
return newarr
},[])
console.log(newArr)
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// 取消请求
controller.abort()
防抖:n秒钟内执行事件,如果在n秒内重复触发,则重新计时
场景:搜索框搜索输入。只需要用户最后一次输入完,再发送请求;窗口resize事件,只有窗口调整完后,才计算窗口大小。防止重复渲染
function debounce(fn,delay=300) {
let timer = null
return function(...args) {
const ctx = this
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(ctx, args)
}, delay)
}
}
节流:在n秒内只执行一次,如果多次触发也是只执行一次
场景:滚动加载,加载更多或滚动到底部监听;搜索框,搜索联想
//使用时间戳写法,事件会立即执行,停止触发后没有办法再次执行
function throttled1(fn,delay=300) {
let start = Date.now()
return function(...args) {
const end = Date.now()
const ctx = this
if (delay <= (end - start)) {
fn.apply(ctx, args)
start = Date.now()
}
}
}
//delay毫秒后第一次执行,第二次事件停止触发后依然会再一次执行
function throttled2(fn,delay=300) {
let timer = null
return function(...args) {
const ctx = this
if(!timer) {
timer = setTimeout(()=>{
fn.apply(this, args)
timer = null
},delay)
}
}
}
px % rpx em rem vw/vh的区别
箭头函数有什么缺点?什么时候不能使用箭头函数?
请描述TCP三次握手和四次挥手(网络连接TCP协议,传输内容是HTTP协议)
for-in和for-of的区别
function createPromise(val) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(val)
}, 2000);
})
}
!(async function() {
// const p1 = createPromise(10)
// const p2 = createPromise(20)
// const p3 = createPromise(30)
// 同时出现
// const r1 = await p1
// console.log(r1)
// const r2 = await p2
// console.log(r2)
// const r3 = await p3
// console.log(r3)
// 同时出现
// const list = [p1,p2,p3]
// Promise.all(list).then(res => console.log(res))
// 同时出现
// const list = [p1,p2,p3]
// for await (let res of list) {
// console.log(res)
// }
//---------分割------------
// 实现依次出现,而不是同时出现
// const p1 = await createPromise(10)
// console.log(p1)
// const p2 = await createPromise(20)
// console.log(p2)
// const p3 = await createPromise(30)
// console.log(p3)
// 实现依次出现,而不是同时出现
const list = [10,20,30]
for (let num of list) {
const res = await createPromise(num)
console.log(res)
}
})()
offsetHeight,scrollHeight和clientHeight的区别
HTMLConllection和NodeList区别
computed和watch的区别
Vue组件的通讯方式
vuex中action和mutation的区别
js中严格模式下有什么特点 ‘use strict’
HTTP跨域请求时为何发送options请求
JS垃圾回收
JS内存泄漏如何检测呢?
内存泄漏的场景有那些?(vue为例)
浏览器和nodejs的事件循环有什么区别?
vdom真的很快吗?
遍历一个数组,for和forEach那个更快?
nodejs中进程和线程
为什么需要多进程?
nodejs开启进程的方式
const fork = require('child_process').fork
// 开启子进程
const computeProcess = fork(js文件地址)
computeProcess.send('xxx') // 传递消息给子进程
computeProcess.on('message', data => {
console.log(data)
}) //接收子进程返回的数据
computeProcess.on('close', () => {
console.log('子进程意外关闭')
}) //处理当子进程意外关闭的情况
process.on('message', data => {
console.log(process.pid) //进程id
console.log(data) //从主线程传递的消息
const sum = 'xxx'
process.send(sum) //传递消息给主线程
})
const http = require('http')
const cpuCoreLength = require('os').cpus().length
const cluster = require('cluster')
if(cluster.isMaster) {
for(let i = 0;i < cpuCoreLength; i++) {
cluster.fork()
}
cluster.on('exit', worker => {
console.log('子进程退出')
cluster.fork() //进程守护
})
} else {
// 多个子进程会共享一个tcp连接,提供一份网络服务
const server = http.createServer((req,res) => {
res.writeHead(200)
res.end('done')
})
server.listen(3000)
}
什么是JS Bridge
requestIdleCallback和requestAnimationFrame的区别
Vue的生命周期
vue2、vue3以及React的diff算法不同点
Vue-router的三种模式
Retina 屏 1px 像素问题,如何实现
#box {
padding: 10px 0;
position: relative;
}
#box::before {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background: #d9d9d9;
transform: scaleY(0.5);
transform-origin: 0 0;
}
X 偏移量 0
Y 偏移量 0
阴影模糊半径 0
阴影扩散半径 0.5px
阴影颜色
#box2 {
margin-top: 20px;
padding: 10px;
border-radius: 5px;
/* border: 1px solid #d9d9d9; */
box-shadow: 0 0 0 0.5px #d9d9d9;
}
网络请求中,token和cookie有什么区别?
HTTP协议和UDP协议的区别?
什么是HTTPS中间人攻击?如何预防?
JS中defer和async的区别
常见的前端攻击有那些?如何预防?
WebSocket和HTTP的区别
描述从输入url到页面展示的完整过程