初级前端面试题

持续更新中

  1. 同步和异步的区别

    js是单线程的,同步代码会阻塞运行,异步不会阻塞

  2. 手写Promise加载图片

loadImg(url) {
	return new Promise((resolve,reject) => {
		const img = document.createElement('img')
		img.onload = () => {
			resolve(img)
		}
		img.onerror = () => {
			resolve('加载出错了')
		}
		img.src = url
	})
}
  1. 讲讲event loop(事件循环/事件轮询)机制

    1. 同步代码,一行一行放在Call Stack执行
    2. 遇到异步代码,会先记录下,等待时机(定时,网络请求等)
    3. 时机到了,就移动到Callback Queue
    4. 当Call Stack 为空(即同步代码执行完),event loop开始工作
    5. 轮询查找Callback Queue,如有任务就将其移动到Call Stack 执行
    6. 然后继续轮询查找
  2. JS如何执行

    1. 从前到后,一行一行执行
    2. 如果某一行执行报错,则停止下面代码的执行
    3. 先把同步代码执行完,再执行异步代码
  3. Promise的三种状态

    1. pending 状态下,不会触发then和catch
    2. resolved 状态下,会触发后续的then回调函数
    3. rejected 状态下,会触发后续的catch回调函数
  4. Promise中的then方法和catch方法对状态的影向

    1. then正常返回resolved,里面有报错则返回rejected
    2. catch正常返回resolved,里面有报错则返回rejected
  5. async/await 和 Promise 的关系

    1. 执行async函数,返回的是Promise对象
    2. await 相当于Promise的then方法
    3. try…catch 用来捕获异常,替代了Promise的catch
  6. 一道笔试题,输出执行结果

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
  1. 宏任务和微任务

    1. 宏任务: setTimeout,setInterval, Ajax, DOM事件,setImmediate,I/O(Node.js)
    2. 微任务: Promise.then,MutaionObserve, process.nextTick
      注意: 定时器的时间是指从执行定时器开始计时的,当时间到了之后就会把这个任务放到Callback Queue中

    二者的执行时间:
    微任务 --》 DOM渲染 --》 宏任务

    执行顺序是:

    1. Call Stack清空
    2. 执行当前的微任务
    3. 尝试渲染DOM
    4. 触发event loop机制
验证执行顺序
// 修改 DOM
const $p1 = $('<p>一段文字</p>')
const $p2 = $('<p>一段文字</p>')
const $p3 = $('<p>一段文字</p>')
$('#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}`)
})

  1. 一道题目,输出执行顺序
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 
  1. 手写Promise
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后无法再传递下去
  1. 获取DOM节点的几种方式
document.getElementById() //单个
document.getElementsByTagName() //集合
document.getElementsByClassName() //集合
document.querySelector() // 单个
document.querySelectorAll() //集合
  1. property和attribute的区别
    1. property: 修改对象属性,但不会体现到html结构中
    2. attribute: 修改html属性,会改变html结构
<div>s</div>
const b = document.querySelector('div')
b.a = 100  //不会体现在Html中
b.setAttribute('b', 100) //会体现的html中
  1. DOM性能优化

    1. DOM查询缓存
    2. 对于DOM的多次操作,使用文档片段(createDocumentFragment),操作完成后再将该片段插入到DOM中
    3. 对于要操作的DOM,先克隆(cloneNode),对克隆的节点进行操作,然后再使用replaceChild方法将克隆节点替换原先的
    4. 对于DOM的多种样式操作,可以拼接在一起一次操作
  2. location的一些相关知识

    1. href: 完整的url
    2. protocol: url协议
    3. pathname: url路径名
    4. host: 主机名和端口
    5. hostname: 主机名
    6. hash: url的hash
    7. search: url的查询部分
  3. 编写一个通用的事件监听函数

<!-- 普通绑定和代理绑定 -->
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(); 阻止默认事件发生
  1. 描述事件冒泡的流程
    在嵌套的父子元素中,子元素的事件会一层一层向上传递,一直到顶层元素window对象
    补充:event.stopPropagation(); 可以阻止捕获和冒泡

  2. 什么是事件代理
    事件代理就是在父元素上绑定事件,然后通过父元素的事件来处理子元素的相关操作。
    好处是代码简洁,减少浏览器内存

  3. 手写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)
  1. readyState的几种状态

    1. 状态0表示尚未调用open方法
    2. 状态1表示open方法已经被调用
    3. 状态2表示send方法已经被调用,header已经被接收
    4. 状态3表示下载中,responseText中有部分内容
    5. 状态4表示下载完成
  2. 什么是同源策略

    1. 同源是指:协议,端口,域名三者必须保持一致
    2. 当Ajax发送请求时,浏览器要求当前网页和server必须同源
  3. 那些情况可以无视同源策略

    1. 加载图片
    2. 加载css
    3. 加载js
  4. 如何理解jsonp

    1. js是可以跨域的,服务器可以任意动态拼接数据返回,只要符合js文件格式。
    2. 使用js发送跨域请求,服务器根据发送的请求返回数据到callback函数中,然后前端定义好相应的函数来执行返回的函数即可
  5. 关于axios了解那些

    1. 关于axios,是一个基于promise的请求库,可以用在Node和浏览器中
    2. 在浏览器中创建XMLHttpRequest,在node中创建http请求
    3. 拦截请求和响应
  6. 如何封装axios

    1. 获取axios实例,实例里面可以设置baseurl和timeout超时时间还有headers
    2. 设置拦截器,在请求拦截中可以设置url,method,baseurl,transformRequest(在发送前转换数据格式),headers,params以及data等
    3. 在响应拦截中可以设置data,status,statusText,headers,confing和request(生成此次响应的请求)
  7. 关于cookie,localStorage和sessionStorage的理解

    1. localStorage和sessionStorage最大可存储5M。API可直接使用setItem,getItem和removeItem。并且都不会随着http请求发送出去
    2. localStorage数据会永久有效,除非手动或代码删除
    3. sessionStorage数据只存在于当前会话中,关闭页面或浏览器都会被清除
    4. cookie一般由服务器生成,可以设置失效时间。若没有设置时间,则关闭浏览器就会失效。存放数据大小为4k左右。会随着HTTP请求发送出去。通过document.cookie来修改
  8. HTTP常见的状态码有那些

    1. 1xx 服务器收到请求
    2. 2xx 请求成功
    3. 3xx 重定向
    4. 4xx 客户端错误
    5. 5xx 服务端错误
      常见的状态码:
    6. 200 请求成功
    7. 301 永久重定向(配合location,浏览器自动处理)
    8. 302 临时重定向(配合location,浏览器自动处理)
    9. 304 资源未被修改
    10. 404 资源未找到
    11. 403 没有权限
    12. 500 服务器错误
    13. 504 网关超时
  9. HTTP常见的headers有那些
    Request Headers

    1. Accept 浏览器可接收的数据格式
    2. Accept-Encoding 浏览器可接收的压缩算法,如gZIP
    3. Accept-Languange 浏览器可接收的语言,如zh-CN
    4. Connection: keep-alive 一次TCP连接重复使用
    5. cookie
    6. Host
    7. User-Agent 浏览器信息
    8. Content-type 发送数据的格式,如application/json

    Response Headers

    1. Content-type 返回数据的格式,如application/json
    2. Content-length 返回数据的大小,比如多少字节
    3. Content-Encoding 返回数据的压缩算法,如gzip
    4. Set-Cookie 设置cookie
  10. 什么是Restful API
    传统的api设计是把每个url当成一个功能,而Restful api是把每个url当成一个唯一的资源
    通过get(获取数据),post(新建数据),patch/put(更新数据)以及delete(删除数据)来判断对资源的操作,尽量不使用url参数

  11. 描述一下HTTP的缓存机制
    为什么要使用缓存? 可以减少网络请求的数量和体积,提高页面显示速度,使用户体验更好

    那些资源可以被缓存? 静态资源(js,css,img)

    什么是强制缓存?
    设置Cache-control(Response Headers中):max-age(设置过期时间),no-cache(强制向服务器发送请求,由服务器判断该资源是否有更新,有则返回新内容,没有则使用缓存),no-store(不用本地缓存,也不使用服务端的缓存措施),private(针对个人用户),public(允许中间代理或路由进行缓存处理)

    什么是协商缓存?
    由服务器判断客户端资源是否和服务端资源一样,一致则返回304,否则返回200和最新的资源
    第一次请求时,会返回资源和资源标识,通过判断这个资源标识来辨别资源是否有更新
    资源标识(Response Headers中):

    1. Last-Modified 资源的最后修改时间
    2. Etag 资源的唯一标识
    3. 优先使用Etag
    4. Last-Modified只能精确到秒级
    5. 如果资源被重复生成,而内容不变,使用Etag更精准
  12. 刷新状态对于缓存的影响

    1. 正常操作: 地址栏输入url,跳转链接,前进后退等 --> 强制和协商都有效
    2. 手动刷新: F5,点击刷新按钮,右击菜单刷新 --> 强制失效,协商有效
    3. 强制刷新: ctrl+f5 --> 强制和协商都失效
  13. 常用的git命令

    1. git clone 克隆文件到本地
    2. git status 查看改动的文件
    3. git diff 查看具体的改动地方,后可接文件名
    4. git add + 文件名 提交文件到缓存区
    5. git log 查看提交记录
    6. git show + commit的编号 可以查看修改的内容
    7. git checkout + 文件名 可以回溯到文件提交前的状态
    8. git push -u origin master 提交到主线上
    9. git pull origin master 拉取主线上最新的代码
    1. git checkout -b + 分支名 使用该分支
    2. git add . 添加文件
    3. git commit -m ‘注释’
    4. git push origin + 分支名 提交该分支的代码到线上
    5. git checkout master 切换到master主线
    6. git merge + 分支名 合并该分支内容到主线上
    7. git push origin master 提交主线上的内容到线上
  14. 移动端h5页面抓包工具

    1. window 一般用fiddler
    2. Mac OS 一般用charles
  15. 常见的Linux命令

    1. ssh work@ + 地址 可以远程登录到服务器上
    2. ls ll 可以查看文件
    3. ls -a 可以查看隐藏文件
    4. mkdir 创建文件夹
    5. rm -rf 删除文件夹和里面的所有文件
    6. mv 可以修改文件名或移动文件
    7. cp 可以拷贝文件
    8. touch + 文件名 可以新建文件
    9. cat + 文件名 可以查看文件内容,但不能修改
    10. vim + 文件名 可以查看文件内容,可以修改
  16. webpack基本配置

    1. entry对象 入口文件
    2. output对象 输出文件
    3. plugins数组 放入插件
    4. module对象中有rules数组,可以指定检测规则。每个规则是一个对象,有test正则、use数组、include(包含文件)和exclude(不包含文件)。use数组中也是对象,里面有loader和options对象,而options对象中又有name,outputpath和limit等
    5. 在开发模式下,有mode模式为development,devserve启动服务,devtool错误定位为’eval-cheap-module-source-map’
    6. 在生产模式下,有mode模式为production,devtool错误定位为 ‘source-map’
  17. 基本的webpack项目配置中TreeShaking/热更新/Shimming/CodeSplitting

    1. TreeShaking
      1. 描述:用来移除没有引用的代码
      2. 配置:在package.json中配置"sideEffects",当为false时表示删除任何未引用的代码。也可以变为数组,在里面添加不能删除的代码,比如’.css,.scss’
    2. 热更新
      1. 描述:只能用在开发环境中
      2. 配置:devServer中配置hot,host和static
    3. Shimming
      1. 描述:提供预置依赖,即在某个地方会用到这个库,但是没有引入进去
      2. 配置: 在plugins中添加new webpack.ProvidePlugin({
        _:“loadsh”
        })
    4. CodeSplitting
      1. 描述:把引入的库单独放一个文件,把业务代码单独放一个文件。这样引入的库的代码不用重新加载,只需要重新加载业务代码部分即可
      2. 配置:在optimization中配置runtimeChunk,usedExports,providedExports以及splitChunks即可
  18. 从输入url到渲染出页面的整个过程

    1. 加载资源的形式 HTML,媒体文件,js和css
    2. 加载资源的过程
      1. DNS解析: 把域名解析为IP地址
      2. 浏览器根据IP地址向服务器发送HTTP请求
      3. 服务器处理HTTP请求,并返回数据给浏览器
    3. 渲染页面的过程
      1. 根据HTML代码生成DOM tree (om指对象模型)
      2. 根据css代码生成CSSOM
      3. 将DOM tree和 CSSOM整合成 Render Tree(渲染树)
      4. 根据Render tree 渲染页面
      5. 遇到
<!-- 验证 -->
console.log(a) // function a() {}
var a= 100
function a() {}
----------------------------------
console.log(a) // function a() {}
var a= 100
function a() {}
console.log(a) // 100
  1. typeof能判断那些类型

    1. undefined,string,number,boolean,symbol
    2. object (null == ‘object’)
    3. function
  2. 手写深度比较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
}
  1. 介绍下数组中的pop,push,unshift,shift

    1. pop
      功能:去除数组中最后一个值
      返回值:去除的值
      影响:会改变源数组
    2. push
      功能:在数组最后添加一个值
      返回值:数组的长度
      影响:会改变源数组
    3. unshift
      功能:在数组的最前面添加一个值
      返回值:数组的长度
      影响:会改变源数组
    4. shift
      功能:去除数组最前的一个值
      返回值:去除的值
      影响:会改变源数组
  2. 讲讲纯函数,以及那些是纯函数
    纯函数的特征:

    1. 不会改变源数组(没有副作用)
    2. 返回一个数组

    纯函数有那些

    1. concat
    2. map
    3. filter
-----更新
  1. 数组slice和splice的区别

    1. 功能区别:slice – 切片, splice – 剪切
    2. 参数
      1. slice(start,end) 包括开始,但不包括结束
      2. splice(index,count,items…) 从index位置开始剪切,第二个参数表示要删除的个数,第三个参数表示要添加的值
    3. 返回值
      1. slice 返回新的数组
      2. splice 返回被删除的值组成的数组
    4. 是否为纯函数
      1. slice 纯函数
      2. spice 非纯函数
  2. new Object() 和 Object.create()的区别

    1. new Object() 表示创建一个新对象
    2. Object.create() 是指将传入的对象作为新对象的prototype
const obj1 = {a:1,b:2}
const obj2 = new Object(obj1)
// obj2 === obj1
const obj3 = new Object({a:1,b:2})
// obj3 !== obj1
  1. 常见的正则表达式语法

    1. *表示0到多次
    2. ?表示0到1次
    3. +表示1到多次
    4. \d 匹配一个数字
    5. \D 匹配一个非数字
    6. \s 匹配一个空白符
    7. \S 匹配一个非空白符
    8. \w 匹配一个单字字符(字母、数字或者下划线) 等价于 [A-Za-z0-9_]
    9. \W 匹配一个非单字字符 等价于 [^A-Za-z0-9_]
  2. 手写字符串trim方法,保证浏览器的兼容性

if (!String.prototype.trim) {
	String.prototype.trim = function () {
		return this.replace(/^\s+/, '').replace(/\s+$/, '')
	}
}
  1. 讲讲json

    1. json是一种数据结构,本质上是一段字符串
    2. json格式和js对象结构一致,对js语言更加友好
    3. window.JSON是全局对象。JSON.stringify将对象JSON化,JSON.parse将JSON对象化
  2. 手写函数用于解析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]
}

<!-- URLSearchParams -->
function query(name) {
	const search = location.search
	const p = new URLSearchParams(search)
	return p.get(name)
}
  1. 手写flatern
<!-- 嵌套一层数组 -->
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)
}
  1. 手写数组去重
<!-- 传统方式 -->
function unique(arr) {
	let res = []
	arr.forEach(item => {
		if(!res.includes(item)){
			res.push(item)
		}
	})
	return res
}

<!-- set -->
function unique(arr) {
	const res = new Set(arr)
	return [...res]
}
  1. 手写深拷贝
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
}
  1. 值和引用类型
    1. 值存储在栈中
    2. 引用类型存储在堆中
-----更新
  1. 常见的假值

    1. 0
    2. NaN
    3. ‘’
    4. null
    5. undefined
    6. false
  2. watch和computed的区别

    1. computed是计算属性,计算属性的返回值,可以直接使用;watch是监听器,监听某个值发生变化然后执行对应的回调函数
    2. computed中所依赖的属性值没有变化的时候,就不会再计算,而是直接使用之前的值。
    3. computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听
    4. computed必须有return,而watch没有
  3. 原型,原型链和继承 这篇文章讲的很简单易懂

    1. 简单版本:
      1. 原型是什么???就是函数的prototype指向的对象
      2. 原型继承又是什么??? 就是通过new调用函数创造的对象的内部[[prototype]]链接函数的prototype指向的对象/通过extends
      3. 原型链又是什么??? 对象之间通过prototype链接而形成的关系
    2. 详细了解:
      1. prototype:每个函数/class都有一个prototype属性,这个属性指向其原型对象
      2. proto:每个实例(除null外)都会有的属性,叫做__proto__,这个属性会指向该实例的原型
      3. constructor: 每个原型对象都有一个constructor属性,指向其关联的构造函数/class
      4. 函数/class、原型和实例的关系:每个函数/class都有一个原型对象,原型对象都包含一个指向函数/class的指针,而实例都包含一个指向原型对象的内部指针。
      5. 最顶层的对象原型是null:Object.prototype.proto === null
代码实践
<!-- 用class方式 -->
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)

<!-- 用 new 方式 -->
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)
  1. js中的反射是什么?
    1. 在传统的面向类环境中,检查一个实例(JavaScript中的对象)的继承祖先(JavaScript中的委托关联)通常被称为内省或反射
    2. 方式:
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
  1. 手写一个简易的jQuery,考虑插件和拓展性
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'))
-----更新
  1. this的不同应用场景,如何取值?
    1. this的运行机制:this是在运行时进行绑定的,不是在编写时绑定的,它的上下文关系取决于函数调用时的各种关系。this的绑定和函数声明的位置没有关系,只取决于函数的调用方式。就是说this指向是动态绑定的,但是总是指向调用函数的那个对象,而不管函数是声明在哪里
    2. 默认绑定:就是独立函数调用时,默认绑定到window
    3. 隐式绑定:当一个对象拥有或者包含这个函数的引用时,this就会指向这个对象
    4. 显示绑定:使用apply,call或bind方法,第一个参数传入this的指向,后面就是参数
    5. new绑定:
      1. 创建一个新的对象
      2. 对这个新对象进行prototype链接
      3. 函数的this会指向这个新对象
      4. 如果没有返回内容,则会自动返回这个新对象
    6. 优先级:new > 显示绑定 > 隐式绑定 > 默认绑定
    7. 箭头函数:this指向定义时所在的对象,不是运行时所在的对象
<!-- 默认绑定 -->
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

<!-- new绑定 -->
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);
  1. 手写bind函数
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;
	   };
   }
  1. 实际开发中闭包的应用场景,举例说明
function createCache() {
	const data = {}
	return {
		set: function(key,val) {
			data[key] = val
		},
		get: function(key) {
			return data[key]
		}
	}
}
  1. 闭包代码问题
<!-- 函数作为返回值 -->
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

// 所有的自由变量的查找,是在函数定义的地方,向上级作用域查找
// 不是在执行的地方!!!
-----更新
  1. 有序和无序区别

    1. 有序:操作慢
    2. 无序:操作快
  2. Map和Object区别

    1. API不同,Map可以用任意类型为key
    2. Map是有序结构(对象)
    3. Map操作同样很快
    4. Map可以用来关联两个不相干的数据
<!-- Map -->
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)
  1. Set和数组区别
    1. API不同
    2. Set元素不能重复
    3. Set是无序结构,操作很快
const set = new Set([1,2,3,4,3])
console.log(set)
set.add(5)
set.has(3)
set.delete(2)
set.clear()
  1. WeakMap和WeakSet(了解)

    1. 弱引用,防止内存泄漏
    2. WeakMap只能用对象作为Key,WeakSet只能用对象作为Value
    3. 没有forEach和size,只能add delete has
  2. reduce的简单了解和使用

    1. 参数有四个
      1. previousValue: 上一次调用callbackFn时的返回值。在第一次调用时,若指定了初始值InitialValue,则其值为initalValue,否则为数组索引为0的元素
      2. currentValue: 数组中正在处理的元素
      3. currentIndex: 数组中正在处理的元素的索引
      4. array: 用于遍历的数组
    2. 初始值initialValue,在参数后面,可有可无
    3. 简单使用
      1. 数组累加
      2. 计数
      3. 输出字符串
      4. 二维数组变一维数组
      5. 数组去重
<!-- 数组累加 -->
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)
-----更新(进阶学习)
  1. ajax,fetch和axios的区别
    1. ajax 是一种技术统称,用于实现客户端和服务端异步通信,实现局部刷新页面的效果
    2. fetch
      1. 浏览器原生API,用于网络请求
      2. 和XMLHttpRequest是一个级别的
      3. 更加简洁好用,支持promise
      4. 服务器返回 400,500 等错误码时并不会 reject。仅当网络故障时或请求被阻止时,才会标记为 reject
    3. axios
      1. 是一个第三方库
      2. 支持node和浏览器(从浏览器中创建 XMLHttpRequest,从 node.js 创建 http 请求)
      3. 支持 Promise API
      4. 可以拦截和响应请求
      5. 自动转换JSON数据
      6. 可以取消请求(CancelToken 已经被弃用了)
<!-- axios取消请求 -->
const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// 取消请求
controller.abort()
  1. 节流和防抖
<!-- 防抖 -->
防抖: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)
		}
    }
}
  1. px % rpx em rem vw/vh的区别

    1. px:绝对单位值,一个像素
    2. rpx:微信小程序解决自适应屏幕尺寸的尺寸单位,可以根据屏幕宽度进行自适应
    3. em:在font-size中是相对于父元素的字体大小,在其他属性中是相对于自身的字体大小,如width
    4. rem: 是相对于根元素html,只需要设置根元素大小,即可将整个页面的比例设置好。浏览器默认字体大小,一般是16px
    5. %: 相对于父元素大小的百分比值
    6. vh和vw:相对于可视窗口的高度和宽度,1vh等于1/100的视口高度,1vw等于1/100的视口宽度。比如:浏览器高度900px,宽度为750px,1vh=900px/100=9px,1vw = 750px/100=7.5px
    7. vmin和vmax:vmin是vh和vw中的较小值,vmax是vh和vw中的较大值
  2. 箭头函数有什么缺点?什么时候不能使用箭头函数?

    1. 箭头函数的缺点
      1. 没有arguments
      2. 无法通过apply,call和bind改变this指向
      3. 某些嵌套箭头函数代码难以阅读
    2. 不能使用箭头函数的场景
      1. 对象方法中。当要通过this来获取对象自身的属性或方法时,不能使用箭头函数
      2. 对象的原型方法中。
      3. 构造函数
      4. 动态上下文的回调函数中。如监听事件,想要获取this的对象,就不能使用箭头函数
      5. vue2中的生命周期函数和method方法。vue的组件本质上是一个对象。(补充:React组件本质上是class,这里面可以使用箭头函数,是可以获取自身的属性和方法的)
-----更新
  1. 请描述TCP三次握手和四次挥手(网络连接TCP协议,传输内容是HTTP协议)

    1. 三次握手
      1. client发包,server接收。server知道有client要找它
      2. server发包,client接收。client知道server已经接收到消息了
      3. client发包,server接收。server知道client要准备发送请求了
    2. 四次挥手
      1. client发包,server接收。server知道client已经发完请求了
      2. server发包,client接收。client知道server已经收到消息,等待server关闭。此时,server可能还在返回数据,还没停止
      3. server发包,client接收。client知道server已经发送完数据了,此时可以关闭连接了
      4. client发包,server接收。server关闭连接
  2. for-in和for-of的区别

    1. for-in 用于遍历可枚举数据,即Object.getOwnProperDescritors()。 如对象,数组,字符串
    2. for-of 用于遍历可迭代数据,即有Symbol.iterator属性的。 如对象,数组,字符串,Map,Set
    3. 注意
      1. 遍历对象:for-in可以,for-of不可以
      2. 遍历Map,Set:for-of可以,for-in不可以
      3. 遍历generator:for-of可以,for-in不可以
      4. for-in会遍历原型链上的可枚举属性
      5. for-of可以与break、continue和return配合使用
    4. (连环问题)for-await-of的作用:用于遍历多个Promise。类似于Promise.all
            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)
            }
        })()
    
  3. offsetHeight,scrollHeight和clientHeight的区别

    1. offsetHeight/offsetWidth: border + padding + content
    2. clientHeight/clientWidth: padding + content
    3. scrollHeight/scrollWidth: padding + 实际内容尺寸(可能会被子元素撑开)
  4. HTMLConllection和NodeList区别

    1. HTMLConllection 是Element的集合
    2. NodeList是Node集合
    3. Node和Element区别
      1. DOM是一棵树,所有节点都是Node
      2. Node是Element的基类
      3. Element是其他HTML元素的基类,如HTMLDivElement
    4. 获取Node和Element的返回结果可能不一样:如ele.childNodes和ele.children不一样。前者包含Text和Comment节点,后者不会
    5. HTMLConllection和NodeList都是类数组,转成数组方式有:
      1. Array.from(list)
      2. Array.property.slice.call(list)
      3. […list]
  5. computed和watch的区别

    1. computed是计算属性,计算属性的返回值,可以直接使用;watch是监听器,监听某个值发生变化然后执行对应的回调函数
    2. computed中所依赖的属性值没有变化的时候,就不会再计算,而是直接使用之前的值。
    3. computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听
    4. computed必须有return,而watch没有
    5. 使用场景: computed可以用在当一个值受多个属性影响的时候–>购物车商品结算, watch----当一条数据影响多条数据的时候–>搜索框。
  6. Vue组件的通讯方式

    1. props和$emit 父子组件传值
    2. 自定义事件 兄弟组件或不相干的组件传值,vue3中需要引入event-emitter插件
    3. a t t r s 作为 p r o p s 和 attrs 作为props和 attrs作为propsemit的替换方案。会获取除了props外的传入的值和事件。可以通过 v-bind=“$attrs” 传入内部组件
    4. $parent 可以获取父组件的方法和属性
    5. $refs 可以获取子组件的方法和属性
    6. provide和inject 可以跨多级传值。当需要传入响应值是要把provide写成方法的形式
    7. Vuex
  7. vuex中action和mutation的区别

    1. mutation是原子操作,必须是同步代码
    2. action是可以包含多个mutation,也可以包含异步代码
  8. js中严格模式下有什么特点 ‘use strict’

    1. 全局变量必须先声明再使用
    2. 禁止使用with
    3. 不使用eval创建作用域
    4. 禁止this指向window
    5. 函数参数不能重名
  9. HTTP跨域请求时为何发送options请求

    1. options请求是跨域请求之前的预检查,可以知道服务端支持那些方法
    2. 浏览器自动发起的,不需要我们干预
    3. 不会影响开发的实际功能
  10. JS垃圾回收

    1. 什么是垃圾回收?就是函数执行完成,回收再也用不到的对象或变量
    2. 之前是用引用计数来清除,循环引用无法清除
    3. 现在是用标记清除,即从window开始逐步向下遍历,能获取到的对象或变量就不清除,找不到的就清除
    4. 连环问题:js闭包是内存泄漏吗?内存泄漏是指那些非预期的情况,即本应该被回收,但不能被回收的情况。而闭包是数据不可以被回收,是预期之内的。
  11. JS内存泄漏如何检测呢?

    1. 使用chrome devTools的Performance和Memory工具来检测,使用前得先点击小垃圾箱清除垃圾。点击小黑原点,然后执行页面操作,然后点击stop即完成检测
    2. 一个合理的内存使用情况应该是一个锯齿状的,即有创建也有回收。如果没有回收,则是一个一直递增的形状。
  12. 内存泄漏的场景有那些?(vue为例)

    1. 被全局变量、函数引用,组件销毁时未清除
    2. 被全局事件、定时器引用,组件销毁时未清除
    3. 被自定义事件引用,组件销毁时未清除
    4. 连环问题:WeakMap和WeakSet的垃圾回收?它们都是弱引用,即不能影响正常的垃圾回收,该回收的还是会被回收。
  13. 浏览器和nodejs的事件循环有什么区别?

    1. 浏览器
      1. 同步任务 + 微任务 + DOM渲染 + 宏任务
      2. 微任务:Promise.then,async/await
      3. 宏任务:setTimeout,setInterval,网络请求,DOM事件
    2. Node
      1. 同步任务 + 微任务 + 按顺序执行6个类型的宏任务(每个开始时都会先执行当前的微任务)
      2. 微任务:Promise.then,async/await,process.nextTick(优先级最高,优先执行)
      3. 宏任务:
        1. Timer: setTimeout setInterval
        2. I/O callbacks 处理网络请求
        3. Idle,prepare 闲置状态(nodejs内部使用)
        4. Poll轮询 执行poll中的i/o队列
        5. Check查询 存储setImmediate回调
        6. Close callbacks 关闭回调,如socket.on(‘close’)
  14. vdom真的很快吗?

    1. vdom是指用js对象模拟DOM节点数据
    2. Vue和React等框架的核心价值
      1. 组件化
      2. 数据视图分离,数据驱动视图—>核心价值点
      3. 只关注业务数据,不用再关心DOM变化
    3. 总结
      1. vdom并不快,直接用js操作DOM才是最快的
      2. 但是“数据驱动视图”必须要有合适的技术方案,不能全部DOM重建
      3. vdom就是目前来说最合适的技术方案,不是因为它很快,而是合适
  15. 遍历一个数组,for和forEach那个更快?

    1. for更快
    2. forEach每次都要创建一个函数来调用,而for不会创建函数
    3. 函数需要独立的作用域,会有额外的开销
  16. nodejs中进程和线程

    1. 进程:os进行资源分配和调度的最小单位,有独立内存空间
    2. 线程:os进行运算调度的最小单位,共享进程内存空间
    3. js是单线程的,但是可以开启多进程执行,如WebWorker
  17. 为什么需要多进程?

    1. 多核CPU,更适合处理多进程
    2. 内存较大,多个进程才能更好的利用(单进程有内存上限)
    3. 压榨机器资源,更快更节省
  18. nodejs开启进程的方式

    1. child_process:主要用于开启单个进程进行计算
    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('子进程意外关闭')
    }) //处理当子进程意外关闭的情况
    
    <!--  子进程-js文件地址 -->
    process.on('message', data => {
        console.log(process.pid) //进程id
        console.log(data) //从主线程传递的消息
        const sum = 'xxx'
        process.send(sum) //传递消息给主线程
    })
    
    1. cluster:主要用于开启多个服务多个进程
    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)
    }
    
  19. 什么是JS Bridge

    1. APP内的网页中的js无法直接调用API,只能通过APP自己封装一些特定API到网页中,然后网页中的JS调用这些特定API执行某些功能
    2. js无法直接调用native API
    3. 需要通过一些封装后的特定格式来调用
    4. 这些特定格式就是JS Bridge,比如微笑JSSDK
    5. JS Bridge目前有两种方式
      1. 注册全局API: 只能处理一些简单的同步的情况
      2. URL Scheme: 可以处理比较复杂的情况
  20. requestIdleCallback和requestAnimationFrame的区别

    1. requestAnimationFrame是每次渲染完都会执行,高优
    2. requestIdleCallback是空闲时才会执行,低优
    3. 连环问题:它们是宏任务还是微任务? 都是宏任务。都要等到DOM渲染后才执行。
  21. Vue的生命周期

    1. beforeCreate:创建一个空白的Vue实例。data和method尚未被初始化,不可使用
    2. created:初始化完成,完成响应式绑定。data和method已经初始化完成,可以调用。尚未开始渲染模板。$el还不可用
    3. beforeMount:编译模板,调用render生成vdom,此时还没开始渲染DOM
    4. mounted:完成DOM渲染,组件创建完成。开始由“创建阶段”进入“运行阶段”
    5. beforeUpdate:data发生变化之后,准备更新DOM(尚未更新DOM)
    6. Updated:data发生变化,且DOM更新完成。(不要在updated中修改data,可能会导致死循环)
    7. beforeUnmount:组件进行销毁(尚未销毁,可正常使用)。可移除、解绑一些全局事件和自定义事件
    8. unmounted:组件被销毁了,所有指令、事件侦听器都被移除。所有子组件实例都被卸载。
    9. activated:被keep-alive缓存的组件激活时调用
    10. deactivated: 被keep-alive缓存的组件失活时调用
    11. 连环问题:什么时候操作DOM比较合适?mounted和updated都不能保证子组件全部加载完成,可以使用$nextTick渲染DOM
    12. 连环问题:Vue3有什么区别?使用setup替代了beforeCreate和Created。使用hooks函数形式。
  22. vue2、vue3以及React的diff算法不同点

    1. vue2 - 双端比较:用四个指针来同时比较,然后指针移动再比较
    2. vue3 - 最长递增子序列
    3. React - 仅右移
    4. Tree diff的优化
      1. 只比较同一层,不跨级比较
      2. tag不同层则删掉重建(不再去比较内部的细节)
      3. 子节点通过key区分
    5. 连环问题: Vue和React为何循环时必须要用key?
      1. vdom diff算法会根据key判断元素是否要删除
      2. 匹配了key,则只需要移动元素 – 性能较好
      3. 未匹配key,则需要删掉重建 – 性能较差
  23. Vue-router的三种模式

    1. Hash
    2. WebHistory
    3. MemoryHistory
  24. Retina 屏 1px 像素问题,如何实现

    1. 有些手机屏幕的 DPR = 2 ,即 1px 它会用两个物理像素来显示,就粗了
    2. 使用 css 伪类 + transform 来优化这一问题。即把默认的 1px 宽度给压缩 0.5 倍
    #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;
    }
    
    1. 连环问题:如果有 border-radius 怎么办? 设置box-shadow
    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;
    }
    
  25. 网络请求中,token和cookie有什么区别?

    1. cookie
      1. 介绍
        1. HTTP无状态,每次请求都要带上cookie,以帮助识别身份
        2. 服务端也可以向客户端set-cookie,cookie大小限制4kb
        3. 默认有跨域限制:不可跨域共享、传递cookie(可以通过设置withCredentials来实现跨域共享和传递;或者设置domain主域名)
      2. 现代浏览器开始禁止第三方cookie
        1. 和跨域限制不同。这里是:禁止网页引入的第三方js设置cookie
        2. 打击第三方广告,保护用户隐私
        3. 新增属性SameSite:Strict/Lax/None;可自行选择值
      3. cookie和session
        1. cookie用于登录验证,存储用户标识(如userID)
        2. session在服务器端(内存中),存储用户详细信息,和cookie信息一一对应
        3. cookie+session是常见的登录验证解决方案
    2. token
      1. 介绍
        1. cookie是HTTP规范,而token是自定义传递
        2. cookie会默认被浏览器存储,而token需要自己存储
        3. token默认没有跨域限制
      2. JWT(json web token)
        1. 前端发起登录,后端验证成功之后,返回一个加密的token
        2. 前端自行存储这个token(包含了用户信息,加密了)
        3. 以后访问服务器端口,都要带着这个token,作为用户信息
    3. 连环问题:Session和JWT那个更好?
      1. Session
        1. 优点
          1. 原理简单,易于学习
          2. 用户信息存储在服务端,可快速封禁某个用户
        2. 缺点
          1. 占用服务器内存,硬件成本高
          2. 多进程,多服务器时,不好同步 — 需要第三方缓存,如redis
          3. 默认有跨域限制
      2. JWT
        1. 优点
          1. 不占用服务器内存
          2. 多进程,多服务器不受影响
          3. 没有跨域限制
        2. 缺点
          1. 用户信息存储在客户端,无法快速封禁某个用户(可用黑名单)
          2. 万一服务器端密钥被泄漏,则用户信息全部丢失
          3. token体积一般大于cookie,会增加请求的数据量
      3. 比较
        1. 如果有严格管理用户信息的需求(保密、快速封禁)推荐Session
        2. 如果没有特殊要求,则使用JWT
    4. 如何实现sso单点登录
      1. 有A网站,B网站和第三方独立网站(专门用来验证)
      2. 第一次访问A网站,A网站会将登录重定向到第三方网站
      3. 验证信息等都交给第三方网站来验证,验证通过后会回传一个token和sso凭证。这个token值会保存在A网站下和第三方网站下
      4. 下次登录A网站时会带上token值,继续到第三方验证,通过后即可登录
      5. 当登录B网站时(不用输入账号密码),会带上第三方网站下存储的token,然后去验证,通过后又会返回一个token到B网站下
      6. 总结一点就是:都要通过第三方独立网站去验证,只要在第三方验证后都可以登录
  26. HTTP协议和UDP协议的区别?

    1. HTTP协议在应用层
    2. TCP和UDP在传输层
    3. TCP协议
      1. 有连接(三次握手)
      2. 有断开(四次挥手)
      3. 稳定传输
    4. UDP协议
      1. 无连接,无断开
      2. 不稳定传输,但效率高
      3. 如视频会议,语音通话
    5. 连环问题:HTTP协议1.0,1.1和2.0有什么区别
      1. 1.0版本
        1. 最基础的HTTP协议
        2. 支持最基本的get,post协议
      2. 1.1版本
        1. 缓存策略Cache-control E-tag等
        2. 支持长连接Connection:keep-alive,一次TCP连接多次请求
        3. 断点续传,状态码206
        4. 支持新的方法PUT DELETE等,可用于Restful API
      3. 2.0版本
        1. 可压缩header,减少体积
        2. 多路复用,一次TCP连接可以多个HTTP并行请求
        3. 服务端推送
  27. 什么是HTTPS中间人攻击?如何预防?

    1. HTTPS加密过程 —> 先非对称加密后对称加密
      1. 使用非对称加密,生成公钥和私钥
      2. 将公钥传输给客户端
      3. 客户端使用公钥生成随机码,并将这个随机码加密后传输给服务端
      4. 服务端使用私钥解密随机码,获取解密后的随机码,并使用这个随机码对数据进行加密
      5. 客户端使用之前生成的随机码来解密数据
    2. 中间人攻击
      1. 劫持通信,伪造CA证书,生成公钥和私钥
      2. 利用伪造生成的公钥和私钥进行数据加密
    3. 如何预防:使用安全的,可靠的SSL证书,不要使用免费的,不知名的证书
  28. JS中defer和async的区别

    1. 无:HTML暂停解析,下载JS,执行JS,再继续解析HTML
    2. defer:HTML继续解析,并行下载JS,HTML解析完后再执行JS
    3. async:HTML继续解析,并行下载JS,执行JS,再解析HTML
    4. 连环问题:prefetch和dns-prefetch有什么区别?
      1. prefetch和preload
        1. prefetch表示资源在未来页面可能会使用,空闲时加载
        2. preload表示资源在当前页面使用,要优先加载
      2. dns-prefetch和preconnet
        1. dns-prefetch即DNS预查询
        2. preconnet即DNS预连接
      3. 答案:无关,前者是资源预获取,后者是DNS预查询
  29. 常见的前端攻击有那些?如何预防?

    1. XSS
      1. Cross Site Script 跨站脚本攻击
      2. 手段:将JS代码插入到网页内容中,渲染时执行JS代码
      3. 预防:特殊字符替换(前端和后端都可以)
    2. CSRF
      1. Cross Site Request Forgery 跨站请求伪造
      2. 诱导用户去访问另一个网站的接口,伪造请求
      3. 严格的跨域限制 + 验证码机制 + 为Cookie设置SameSite
    3. 点击劫持
      1. Click Jacking
      2. 诱导页面上蒙一个透明的iframe,诱导用户进行点击
      3. 让iframe不能跨域加载:设置X-Frame-Options:sameorigin
    4. DDoS
      1. 分布式拒绝服务
      2. 手段:分布式的、大规模的流量访问,使服务器瘫痪
      3. 做硬件预防(阿里云WAF)
    5. SQL注入
      1. 提交内容时写入SQL语句,破坏数据库
      2. 处理输入的内容,替换特殊字符
  30. WebSocket和HTTP的区别

    1. WebSocket
      1. 支持端对端通讯
      2. 可以由client发起,也可以由server发起
      3. 用于:消息通知,直播间讨论区,聊天室,协同编辑等
      4. 实际项目推荐使用scoket.io,API更见简洁
    2. WebSocket连接过程
      1. 先发起一个HTTP请求
      2. 成功之后再升级到WebSocket协议,再通讯
    3. 二者区别
      1. WebSocket协议名是ws://,可双端发起请求
      2. WebSocket没有跨域限制
      3. 通过send和onmessage通讯(HTTP通过req和res)
    4. 连环问题:WebSocket和HTTP长轮询的区别?
      1. HTTP长轮询:客户端发起请求,服务端阻塞,不会立即返回。当返回后,客户端会再立即发送请求。再重复这个过程。
      2. WebSocket:客户端可发起请求,服务端也可以发起请求
  31. 描述从输入url到页面展示的完整过程

    1. 网络请求
      1. DNS查询(得到IP),建立TCP连接(三次握手)
      2. 浏览器发起HTTP请求
      3. 收到请求响应,得到数据(HTML,css,js,媒体资源)
    2. 解析
      1. HTML构建DOM树
      2. css构建CSSOM树
      3. 两者结合,形成render Tree
    3. 渲染
      1. 计算各个DOM的尺寸,定位等,最后绘制到页面上
      2. 遇到JS会先执行JS
      3. 异步加载CSS、图片,可能会触发重新渲染
    4. 连环问题:重绘repaint和重排reflow的区别?
      1. 重绘:元素外观改变,如颜色,背景色等。但元素的尺寸和定位不会变,不会影响到其他元素的位置
      2. 重排:重新计算尺寸和布局,可能会影响到其他元素的位置。比如元素增高,可能会使得相邻元素位置下移
      3. 减少重排方案:
        1. 集中修改样式,或者直接切换css class
        2. 修改之前先设置display:none,脱离文档流。修改后再改回来
        3. 使用BFC特性,不会影响到其他元素的位置
        4. 频繁触发(resize scroll)使用节流和防抖
        5. 使用createDocumentFragment 批量操作DOM
        6. 优化动画,使用CSS3和requestAnimationFrame
    5. 拓展BFC
      1. 解释:块级格式化上下文。内部元素无论如何改动,都不会影响其他元素的位置
      2. 触发条件:
        1. 根节点<html>
        2. float:left/right
        3. overflow:auto/scroll/hidden
        4. display:inline-block/table/table-row/table-cell
        5. display:flex/grid;的直接子元素
        6. position:absolute/fixed
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值