简介
以下是本人一年多整理的前端学习笔记,现汇总分享给大家,很多问题都是面试必问的
更多学习资源,可以点击我获取更多
1 js数据类型
原始类型: null undefined number string boolean Symbol BigInt
引用类型: 对象
2 判断一个对象属性是否存在
本身属性判断: obj.hasOwnProperty(key)
包括了上级属性判断: key in obj
Reflect.get(obj, ‘key’) 没有返回undefined
Reflect.has(obj, ‘key’)
3 把 10000000 格式化成 10,000,000
// 1 把 10000000 格式化成 10,000,000
let str = '10000000'
str = str.replace(/(?=\B(\d{3})+$)/g, ',')
console.log(str)
4 平滑滚动
div{scroll-behavior: smooth
}
5 禁止函数new方式调用
function fn(){if (new.target){throw new Error("can't invoke with 'new'")}console.log('fn call')
}
new fn() // error
6 零宽字符以及应用场景
var account1 = '我\u200d是\u200d中\u200d国\u200d人',account2 = '我是中国人';
console.log(account1.length, account2.length)
// 打印 9 5
console.log(account1, account2)
// 打印 我是中国人 我是中国人
/u200d 是零宽度字符,不会具体显示出来,作用如下
1 起到水印,防止文章盗版,别人拷贝文字,也会把零宽字符也会一起盗版
2 实现隐藏信息加密传递和解密处理
7 js可迭代器理解
可迭代协议:就是任何一个对象,只要包含Symbol.iterator, 且返回一个迭代器,就表示可以迭代
{[Symbol.iterator]: function () {return '迭代器'}
}
比如如下数组,就是可迭代对象
var arr = [1,2,3]
undefined
var iter = arr[Symbol.iterator]()
undefined
iter.next()
{value: 1, done: false}
iter.next()
{value: 2, done: false}
iter.next()
{value: 3, done: false}
iter.next()
{value: undefined, done: true}
面试题: 比如实现如下
const [a, b] = {a: 1, b: 2}
运行上面代码会报出如下错误
caught TypeError: {(intermediate value)(intermediate value)} is not iterableat <anonymous>:1:16
原因是{a: 1, b: 2} 不是一个可迭代的对象,那么如何实现迭代呢,是要让对象实现Symbol.iterator就可以
Object.prototype[Symbol.iterator] = function(){return Object.values(this)[Symbol.iterator]()
}
8 null 和 undefined区别
null 表示是一个对象,但是此时没有对象就是null
undefined 表示是任何一个值,但是此时没有值就是undefined
以下这些都是undefined
- 变量被声明,但没有赋值
- 调用函数,参数没有提供
- 对象属性没有赋值
- 函数没有返回值
另外解释
- null 是一个表示"无"的对象(空对象指针),转为数值时为0;
- undefined 是一个表示"无"的原始值,转为数值时为NaN。
9 标记模板字符串
const fontSize = 20
const color = '#00ff00'
const hi = style`font-size: ${fontSize}px,color: ${color}
``aaaaaa`
function style () {console.log(arguments)return style
}
style 是一个函数,会把模板值作为参数传给函数
10 promise终极面试
Promise.resolve().then(() => {console.log(0)return Promise.resolve(4)
}).then((res) => {console.log(res)
});Promise.resolve().then(() => {console.log(1)}).then(() => {console.log(2)}).then(() => {console.log(3)}).then(() => {console.log(5)}).then(() => {console.log(6)})print 0 1 2 3 4 5 6
11 微软经典算法面试,9宫格输入法排列组合
function keyboardMap(digits) {var map = ['', '', 'abc', 'def', 'ghi', 'jkl','mno', 'pqrs', 'tuv', 'wxyz'];var result = []for (var i = 0 ; i < digits.length; i++) {// result = result 组合map[digits[i]]result = _compose(result, map[digits[i]])}function _compose(arr1, arr2) {if (arr1.length === 0) return arr2if (arr2.length === 0) return arr1var r = []for (var i = 0 ; i < arr1.length; i++) {for (var j = 0 ; j < arr2.length; j++) {r.push(arr1[i] + arr2[j])}}return r}return result
}console.log(keyboardMap('2345678'))
12 并发请求
/*** 并发请求* @param {*} urls 待请求的url数组* @param {*} maxNum 最大并发数* @returns */
function concurRequest(urls, maxNum) {return new Promise((resolve) => {if (urls.length === 0){resolve([])return}const results = []let count = 0 // 当前请求完成数量let index = 0 // 下一个请求下标async function request() {if(index === urls.length) returnconst i = indexconst url = urls[index]index++console.log(url)try{const resp = await fetch(url)results[i] = resp}catch(err) {results[i] = err} finally{count++if (count === urls.length) {console.log('over')resolve(results)}request()}// console.log(results)}const times = Math.min(maxNum, urls.length)for (let i = 0 ; i < times; i++) {request()}})
}const urls = []
for (let i = 1 ; i <= 10; i++) {urls.push('https://www.baidu.com/?a=' + i)
}
concurRequest(urls, 1)
13 如何将class转换为function
class Example{constructor(name){this.name = name}func(){console.log(this.name)}
}
// 开始转化为function
'use strict';
function Example(name) {// 只能用new的方式执行if (!(this instanceof Example)) {throw new TypeError('Class consturctor cannot be invoked without new');}this.name = name
}
// 设置func不可枚举
Object.defineProperty(Example.prototype, 'func', {value: function () {// func 不能通过newif (!(this instanceof Example)) {throw new TypeError(' new func is error');}console.log(this.name)},enumerable: false
})
Example.prototype.func = function (){console.log(this.name)
}
14 可以重试的请求方法
function request(url, maxCount = 5) {return fetch(url).catch(err => maxCount <= 0 ? Promise.reject(err) : request(url, maxCount - 1))
}request('https://www.baidu.com', 6)
.then((resp) => {console(resp)
}).catch((err) => {console.log(err)
})
15 let 和 var区别
1 污染全局
2 块级作用
3 重复生命
4 暂时性死区 TDZ
16keyof基础进阶
const cat = {name: 'jiang',age: 3
}const user = {loginId: '123'
}function <T extends object, K extends keyof T> getValue(obj: T, name: K): T[K] {return obj[name]
}getValue(cat, 'name')
getValue(user, 'loginId')
17 大整数相加
function sum(a, b){let result = ''const len = Math.max(a.length, b.length)//a和b位数补齐a = a.padStart(len, '0')b = b.padStart(len, '0')let carry = 0 for (let i = len - 1; i >= 0; i--) {const n = +a[i] + +b[i] + carrycarry = Math.floor(n / 10)result = (n % 10) + result}if (carry){result = '1' + result}return result
}console.log(sum('1112222222222222222222222222222222222222222233333333333333333333333', '2'))
18 粘性定位
.box{position: sticky;top: 0;}
19 文本溢出处理
1 单行文本溢出
.txt{width: 200px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}
2 多行文本溢出
.txt{width: 200px;height: 150px;line-height: 30px;overflow: hidden;display: -webkit-box;-webkit-line-clamp: 5;-webkit-box-orient: vertical;}
20 获取鼠标位置的各种方式
1 e.x
鼠标到视口窗口左侧距离,忽略滚动条
2 e.clientX
同e.x
3 e.pageX
鼠标到页面左侧距离,包含滚动条的距离
4 e.screenX
鼠标到电脑整个屏幕左侧距离
5 e.movementX
鼠标到上一次位置的横向距离
6 e.offsetX
鼠标到目标元素(e.target)左侧距离
21 flip 动画思想
https://www.imgeek.net/article/825358760
22 让事件只触发一次
XXX.addEventListener('XXXX', () => {},
{once: true
})
23 模拟new操作
// 模仿new操作
function newFun(Fun, ...args) {// 1 创建一个空对象let newObj = {}// 2 把空对象和构造函数通过原型链进行链接newObj.__proto__ = Fun.prototype// 3 把构造函数的this绑定到新的对象上const result = Fun.apply(newObj, args)return result instanceof Object ? result : newObj
}function Person (name) {this.name = name
}Person.prototype.say = function (){console.log(this.name + 'say')
}var p1 = newFun(Person, 'jiang')
p1.say()
24 js关于this指向的问题
1.全局对象中的this指向指向的是window
2.全局作用域或者普通函数中的this指向全局window 3.this永远指向最后调用它的那个对象在不是箭头函数的情况下
4.new 关键词改变了this的指向
5.apply,call,bind可以改变this指向,不是箭头函数
6.箭头函数中的this它的指向在定义的时候就已经确定了箭头函数它没有this,看外层是否有函数,有就是外层函数的this,没有就是window
7.匿名函数中的this永远指向了window匿名函数的执行环境具有全局性,因此this指向window
25 js何如实现继承
1.原型链继承
缺点:无法调用父类构造函数
2.借用构造函数维承
缺点:无法电泳父类原型函数
3.组合式继承
缺点:会调用两次构造方法
4.ES6的class类继承
26 scripte标签里的async和defer有什么区别
当没有async和defer这两个属性的时候:
浏览器会立刻加载并执行指定的脚本
normal情况
parse dom -> fetch script -> exec -> parse dom
有async:
加载和渲染后面元素的过程将和script的加载和执行并行进行(异步)
parse dom -> parse dom -> exec -> parse dom -> fetch script
有defer:
加载和渲染后面元素的过程将和script的加载并行进行(异步),但是它的执行事件要等所有元素解析完成之后才会执行
parse dom -> 全部解析完 parse dom -> exec -> fetch script
扩展link 的preload 和 prefetch区别
preload 先加载,后面请求不会再加载,只要碰到link就是立即加载
prefetch 先获取,只是现在用不用,碰到link不会立即获取,会等到空闲时候才会获取
27 函数箭头特点
不能作为构造函数使用,不能new
箭头函数就没有原型
箭头函数没有arguments
箭头函数不能使用call, apply,bind去噶便this的执行
this指向外层第一个函数的this
28 js判断类型方式
// 1 typeof() 对于基本数据类型没问题,遇到引用数据类型无效
console.log(typeof(666)) // number
console.log(typeof([1,2,3]))// object
// 2 instanceof() 只能判断引用数据类,不能判断基本数据类型
console.log([] instanceof Array) // true
console.log('abc' instanceof String) // false
// 3 constructor 几乎可以判断基本数据类型和引用类型
// 但是如果改变的原型,就不能使用了
console.log(('abc').constructor === String) // true
// 4 Object.prototype.toString.call()
// 可以判断任何数据类型
var opt = Object.prototype.toString
console.log(opt.call(2)) //[object Number]
console.log(opt.call(true)) //[object Boolean]
console.log(opt.call('abc')) //[object String]
console.log(opt.call([])) // [object Array]
29 Proxy比defineProperty好在哪里
defineProperty实现, 无法监听新增的属性
const obj = {a: 1,b: 2,c: {a: 1,b: 2}
}
let v = obj.a
Object.defineProperty(obj, 'a', {get(){console.log('a', '读取')return v},set(val) {console.log('a', '赋值')v = val}
})obj.a = 10
console.log(obj.a)
// 不生效 definePropert 无法深度遍历
obj.c.a = 20
defineProperty递归实现多层级监听
const obj = {a: 1,b: 2,c: {a: 1,b: 2}
}// 手写递归遍历,达到多层级监听的问题function _isObject(v) {return typeof v === 'object' && v != null}function observe(obj) {for (const k in obj) {let v = obj[k]if (_isObject(v)) {observe(v)}Object.defineProperty(obj, k, {get(){console.log(k, '读取')return v},set(val) {console.log(k, '赋值')v = val}})}}observe(obj)obj.a = '江'obj.c.a = '华'console.log(obj.c.a)
proxy简单实现
const proxy = new Proxy(obj, {get(target, k) {let v = target[k]console.log(k, '读取')return v},set(target, k , val) {if (target[k] !== val) {target[k] = valconsole.log(k, '更改')}}
})
proxy递过实现多层级监听
const obj = {a: 1,b: 2,c: {a: 1,b: 2}
}function _isObject(v) {return typeof v === 'object' && v != null}function observe(obj) {// proxy实现const proxy = new Proxy(obj, {get(target, k) {let v = target[k]if (_isObject(v)) {v = observe(v)}console.log(k, '读取')return v},set(target, k , val) {if (target[k] !== val) {target[k] = valconsole.log(k, '更改')}}})return proxy
}const proxy = observe(obj)
proxy.a = 'aaa'
proxy.c.a = 'bbbbb'
proxy.ccccconsole.log(proxy.c.a)
20 宽度适应内容
width: fit-content
21 对象数组去重
const arr = [{a: 1, b: 2},{b: 2, a: 1},{a: 1, b: 2, c: {a: 1, b: 2}},{b: 2, a: 1, c: {b: 2, a: 1}}
]
//错误做法
// const newArr = [...new Set(arr)]
// console.log(newArr)const isObject = (val) => typeof val === 'object' && val != null
const newArr = [...arr]
for (let i = 0; i < newArr.length; i++) {for (let j = i + 1; j < newArr.length; j++) {// if (newArr[j] === newArr[i]) {// newArr.splice(j, 1)// j--// }if (equals(newArr[i], newArr[j])) {newArr.splice(j, 1)j--}}
}function equals(val1, val2) {if (isObject(val1) && isObject(val2)) {const keys1 = Object.keys(val1),keys2 = Object.keys(val2)if (keys1.length !== keys2.length) {return false}for (const k of keys1) {if (!keys2.includes(k)) {return false}if (!equals(val1[k], val2[k])) {return false}}return true} else {return val1 === val2 }
}console.log(newArr)
22 文字颜色和背景混合
.title{font-size: 3em;transition: 0.5s,mix-blend-mode: difference}
23 并发任务控制
function timeout(time) {return new Promise((resolve) => {setTimeout(() => {resolve();}, time)});
}class SuperTask{constructor(paralleCount){// 并发数量this.paralleCount = paralleCountthis.runningCount = 0 ; // 正在运行任务数this.tasks = []}add(task){return new Promise((resolve, reject) => {this.tasks.push({task, resolve, reject})this._run()})}_run(){while(this.runningCount < this.paralleCount && this.tasks.length){const {task, resolve, reject} = this.tasks.shift()this.runningCount++task().then(resolve, reject).finally(() => {this.runningCount--this._run()})}}
}
const superTask = new SuperTask(2);function addTask(time, name){superTask.add(() => timeout(time)).then(() => {console.log(`任务${name}完成`)})
}
// 同一时段,最多只能执行两个
addTask(10000, 1) // 10000 输入 1
addTask(5000, 2) // 5000 输入 1
addTask(3000, 3) // 8000 输入 1
addTask(4000, 4) // 12000 输入 1
addTask(5000, 5) // 15000 输入 1
24 消除异步传染性
因为getUser是异步,导致以下全部函数都是异步
async function getUser(){return await fetch('').then((resp) => resp)
}async function m1(){return await getUser()
}async function m2(){return await m1()
}async function m3(){return await m2()
}async function main(){const user = await m3()console.log(user)
}main()
function getUser(){return fetch('https://www/baidu.com')
}function m1(){return getUser()
}function m2(){return m1()
}function m3(){return m2()
}function main(){const user = m3()console.log(user)
}function run(func){const cache = []let i = 0;const _originalFetch = window.fetchwindow.fetch = (...args) => {if(cache[i]){if (cache[i].status === 'fulfilled') {return cache[i].data} else if (cache[i].status === 'rejected') {throw cache[i].err}}const result = {status: 'pending',data: null,err: null}cache[i++] = resultconst prom = _originalFetch(...args).then(resp=>resp).then(resp=>{result.status = 'fulfilled'result.data = resp}, err=> {result.status = 'rejected'result.data = err})throw prom}try{func()}catch(err) {if (err instanceof Promise){const reRun = () => {i = 0func()}err.then(reRun, reRun)}}
}run(main)
25 文字环绕设置
img{width: 50%;height: 50%;border-radius: 50%;shape-outside: circle(50%)}
26 Promise like 判断
function isPromiseLike(value) {return (value !== null && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function')
}
27 认识Symbol.toStringTag
function A(){}
const aa = new A()
Object.prototype.toString.call(aa)
'[object Object]'
A.prototype[Symbol.toStringTag] = 'A'
'A'
Object.prototype.toString.call(aa)
'[object A]'
38 具名导入和结构
// import导入只是标记一下变量,ESM符号绑定
import {n, increase} from './a'
// 结构是具体赋值
const {n, increase} = a
39 模拟一个微队列
function asyncRun(func){if (typeof Promise !== 'undefined') {Promise.resolve().then(func)} else if (typeof MutationObserver !== 'undefined') {const ob = new MutationObserver(func)const textNode = document.createTextNode('0')ob.observe(textNode, {characterData: true})textNode.data = '1'} else {setTimeout(func)}
}
40 js实现单例
方案一:缺点,原型链上追加属性无效
function singleton(className){let ins;return class {constructor(...args){if (!ins) {ins = new className(...args)}return ins}}
}class Video {constructor(){console.log('video created')this.name = 'jiang'}}const SingleVideo = singleton(Video)const video1 = new SingleVideo()
const video2 = new SingleVideo()
console.log(video1 === video2)
console.log(video1)
方案二:proxy代理方式,完美方案
function singleton(className){let ins;// 使用代理,并挟持构造器return new Proxy(className, {construct(target, args) {if (!ins) {ins = new target(...args)}return ins}})
}class Video {constructor(){console.log('video created')this.name = 'jiang'}}const SingleVideo = singleton(Video)const video1 = new SingleVideo()
const video2 = new SingleVideo()
console.log(video1 === video2)
console.log(video1)
41 冻结对象提升效率
有时候vue2在频繁给数组对象赋值,会很消耗性能,因为要执行Object.defineProterty
冻结对象
this.datas = Object.freeze(this.getDatas())
42 js函数重载
const searcher= {}function addMethod(object, name, fn) {const old = object[name]object[name] = function (...args) {if (args.length === fn.length) {return fn.apply(this, args)} else {return old.apply(this, args)}}
}
addMethod(searcher, 'find', () => {console.log('查询所有用户')
})addMethod(searcher, 'find', (name) => {console.log('按照用户名查询')
})addMethod(searcher, 'find', (firstname, lastname) => {console.log('按照姓和名查询')
})searcher.find()
searcher.find('jiang')
searcher.find('hua', 'jiang')
42 vue一次性渲染列表太大,出现白屏,依次延迟加载方案
import {ref} from 'vue'
export function useDefer(maxFrameCount = 1000) {const frameCount = ref(0)const refreshFrameCount = () => {requestAnimationFrame(() => {frameCount.value++;if (frameCount.value < maxFrameCount) {refreshFrameCount()}})}refreshFrameCount();return function (showInFrameCount) {return frameCount.value >= showInFrameCount}
}
使用
const defer = useDefer()
<div v-for="n in 100"><mycomp v-if="defer(n)"></mycomp>
</div>
43 对象属性筛选
const pick = (obj, ...props) => {return Object.fromEntries(Object.entries(obj).filter(([k]) => props.includes(k)))
}
let obj = {name: 'jiang',age: 35
}
console.log(pick(obj, 'name'))
// { name: 'jiang' }
44 随机颜色
const randomColor = () => '#' + Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, '0')
console.log(randomColor())
45 随机字符串
const randomString = () => Math.random().toString(36).slice(2)
console.log(randomString())
46 从html获取纯文字
const removeTag = (fragment) => new DOMParser().parseFromString(fragment, 'text/html').body.textContent | ''
console.log(removeTag('<div>你哈</div>'))
47 什么数据响应
数据变化后,会自动重新运行依赖该数据的函数
48 let和var区别
- 是否污染全局
- 是否是块级作用域
- 是否能重复声明
- 是否有暂时性死区
48 无限递归是否栈溢出
function foo(){// setTimeout(foo, 0) // 不会栈溢出setTimeout(foo(), 0) // 会栈溢出
}
foo()
49 可以重试的请求方式
function request(url, maxCount = 5) {return fetch(url).catch(err => maxCount <= 0? Promise.reject(err) : request(url, maxCount--))
}
request('http://wwww', 5)
50 apply作用
修改this指向,等同call,但不一样的是call参数不是数组
函数.apply(参数1, …参数2)
等价于
参数1.函数(…参数2)
console.log.call.call.call.call.apply((a) => a, [1,2])
// 不输入任何值
51 js真正私有字段
class A{#name; // 定义私有字段constructor(){this.#name = 'jiang'}#method(){console.log('私有方式,外面不能访问')console.log(this.#name)}
}const a = new A()
A.#name // 无法访问
52 Math.floor 和 parseInt区别
Math.floor 是向左取整
parseInt 是向靠近0的一侧取整
console.log(Math.floor(2.5)) // 2
console.log(parseInt(2.5)) // 2
console.log(Math.floor(-2.5)) // -3
console.log(parseInt(-2.5)) // -2
53 控制动画停止与运行
.container{animation: rotate 20s linear paused infinite;
}.container:hover{animation-play-state: running;
}
54 磨砂玻璃特效
元素背后的div模糊
.mark{backdrop-filter: blur(5px)}
55 元素缩放方式
- zoom
- scale
span{font-size: 12px;transform: scale(0.5);display:inline-block;transform-origin: left center}
56 智能弹幕不遮挡人脸方案
.danmu{-webkit-mask-image: url(./mark.svg);-webkit-mask-position: center;-webkit-mask-repeat: no-repeat;}
57 js滚动元素到可视区域
elm.scrollIntoView({behavior: "smooth",block: "center"})
58 块级函数
var a;
console.log(b)
if (true){console.log(a)console.log(b)a = 5function a() {}function b() {}a = 0console.log(a)
}console.log(a)
console.log(b)// 函数不管在哪里声明,都会提升到顶层,
// 如果是判断里面,提升到顶部会赋值undefine
// 当代码hi行到判断里面,函数a和b会生成块级作用域,值是本身
59 transform
.item{width: 100px;height: 100px;transform: rotate(120deg) translate(100px, 100px)
}
transform 执行顺序是从右往左,先平移,在旋转,且旋转都是按照平移钱为中心旋转
60 保持宽高比
.container{width: 90%;margin: 0 auto;aspect-ratio: 16/9.0}
61 元素的尺寸
参考:https://www.douyin.com/user/MS4wLjABAAAAi2oukRVcHpgD-HbVdzsxE7tYykr91YuIKukR_X_Yy08EFWRQhRrECDF6FvbvT8Xa?is_search=1&list_name=follow&modal_id=7124153340170112268&nt=0
- clientWidth clientHeight
边框以内的尺寸,四舍五入的整数, 不包含滚动条 - offsetWidth offsetHeight
布局尺寸,包含滚动条 - scrollWidth scrollHeight
内容尺寸,包含边距和溢出部分,不需滚动且没有溢出等同于clientXXX - getBoundingClientRect(),包含小数
const rect = dom.getBoundingClientRect()
如果盒子没有旋转变形等情况下
rect.width 和 rect.height 等同于scrollXXXX
如果盒子旋转或变形
rect 是最小矩形去框住这个盒子
62 this指向问题
- 通过new调用,指向新对象
- 直接函数调用, 指向全局对象
- 对象调用 obj.func 指向obj
- call,apply,bind 指向第一个参数
63 经典面试题[‘1’, ‘2’, ‘3’].map(parseInt)
console.log(['1', '2', '3'].map(parseInt))
// 得到结果 [ 1, NaN, NaN ]
/*** [* 函数('1', 0),* 函数('2', 1),* 函数('3', 2)* ]*/
64 设置阴影方式
- box-shadow
根据外面盒子来做阴影 - filter: drop-shadow
根据盒子像素点来做阴影,透明的地方不管
65 flex + margin 简化布局
.parent{display: flex}.item{margin: auto; // 整体居中margin-left: auto; // 居右margin-right: auto; // 居左边}
66 数据分组
const people = [{name: 'jiang', age: 20},{name: 'mao', age: 22},{name: 'han', age: 30},
]function groupBy(arr, generateKey){// 参数归一化if (typeof generateKey === 'string') {const propName = generateKeygenerateKey = (item) => item[propName]}const result = []for (const item of arr) {const key = generateKey(item)if (!result[key]) {result[key] = [item]} else {result[key].push(item)}}return result
}console.log(groupBy(people, (item) => item.age))console.log(groupBy(people, (item) => item.name))console.log(groupBy(people, 'name'))const arr = [1,2,3,4,5,6,7,8]
console.log(groupBy(arr, (item) => item % 2 === 0 ? '偶' : '奇'))
67 css 属性默认值设置
initial:主动将某一css属性设为默认值
unset:清除浏览器默认样式(全部清除all:unset)
revert:恢复浏览器默认样式 (全部恢复all:revert)
68 原型链prototype作用
概念:每个对象都有一个原型(prototype),并从愿原型上继承属性和方法,原型本身也是一个对象,它也有自己的原型,形成一个链式结构
对象寻找属性过程:
假设 p 是对象,p.name
- 先从p对象找有没有name属性
如果没有 - p.proto(指向Parent.protorype)上有没有name属性
如果没有 - Object.prototype上有没有name属性
如果没有 - 返回null
69 块级格式化上下文
全称:Block Formatting Context
简称: BFC
概念: 它是一块独立的渲染区域, 它规定了在该区域钟,常规流块盒的布局
- 常规流块盒在水平方向,必须撑满包含块
- 常规流块盒在包含块的垂直方向一次摆放
- 常规流盒若外边距无缝相邻,则进行外边距合并
- 常规流块盒的自动高度和摆放位置,无视浮动元素
BFC渲染区域: 这个区域是由某个HTML元素创建,以下元素会在其内部创建BFC区域: - 根元素,意味着html元素创建的BFC区域,覆盖网页所有的元素
- 浮动和绝对定位元素
- overflow不等于visable的块盒
1. float: left
2. position: absolute
3. overflow: hidden
BFC具体规则:
- 创建BFC元素,它的自动高度需要计算浮动元素
- 创建BFC元素,它的边框盒不会与浮动元素重叠
- 创建BFC元素,不会和它的子元素进行外边距合并
70 高度塌陷,清除浮动办法
.clearfix::after{content: '';display: block;clear: both}或则position: absolute或则float: left或则overflow: auto , scroll, hidden或则display: flow-root
71 vue3效率提升主要表现在哪些方面
参考https://www.douyin.com/user/MS4wLjABAAAAeIIkCgELXG6XdUxuE9nQ6W4AfS-aoPFbtmnBL8ytcYtBSyurgePBYZXJpB0LJBCT?modal_id=7214297604476112188
- 静态元素或则静态属性会被提升
- 预字符串化
- 缓存事件处理函数
- Block Tree
- PatchFlag
72 随机数
const randomNum = (min, max) => Math.random()*(max - min) + min
73 事件循环
-
主线程
主线程是执行代码的地方,遇到函数,会把该函数放入执行栈 -
执行栈
执行结构,有函数就会从栈取出,直接执行 -
任务队列
在执行栈中,遇到比如setTimeout,会放入任务队列,等到计时结束,会任务队列放入执行栈判断是否要执行
任务分为宏任务和微任务,优先检查微任务是否有执行,如果没有,在检查宏任务是否有执行
微任务是在当前事件循环的尾部去执行;宏任务是在下一次事件循环的开始去执行 -
宏任务
有明确的异步任务需要执行和回调;需要其他异步线程支持。
setTimeout
setInterval
setImmediate -
微任务
没有明确的异步任务需要执行,只有回调;不需要其他异步线程支持。
promise.then promise.catch
new MutaionObserver()
promise.nextTick()74 浏览器缓存策略
- 强缓存,本地缓存
不发起请求,直接从缓存里面内容,浏览器会把js,css,image存入内存,下次直接从内存取 - 协商缓存,弱缓存
需要向后台发请求,通过判断来决定是否协商缓存如果内容没有变化,返回304,历览器就用缓存里面的内同 - 触发条件
强缓存:
http1.0: 时间戳响应标头
http1.1: Cache-control 响应标头
弱缓存:
http1.0: 请求头:if-modified-since 响应头: last-modified
http1.1:请求头:if-none-match 响应头: Etag
- 强缓存,本地缓存
75 为什么需要虚拟dom
- 框架设置
- 跨平台
76 ES6 结构基本使用
// 对象结构
const user = {name: 'jiang',sex: '男'
}
const {name = '默认值', sex: gender = '男'} = user
console.log(name, gender)// obj存放了不包含name的剩下字段
const {name: name1, ...obj} = user// 数组结构
const numbers = ['0', '1', '2']
const [a, , b] = numbers
const {0: a1, 2: b2} = numbers
const[a3, b3, ...num] = numbers
console.log(a, b)//参数结构
function ajax({method = 'get', url = '/'} = {})
ajax({method: 'aaa',url:'http'
})
77 vue3 自定义指令
自定义指令
const map = new WeakMap()
const ob = new ResizeObserver((entries) => {for (const entry of entries) {const handler = map.get(entry.target)if (handler) {const box = entry.borderBoxSize[0]handler({width: box.inlineSize,height: box.blockSize})}}
})export default {mounted(el: HTMLElement, binding: any) {ob.observe(el)map.set(el, binding.value)},unmounted(el: HTMLElement) {ob.unobserve(el)},
}
注册指令main.js
import sizeDirect from './directive/sizeDirect'
...
app.directive('size-ob', sizeDirect)
使用
<div class="desktop-main" v-size-ob="handlerSize"></div>
...
const handlerSize = ((size: any) =>{console.log('handlerSize', size.width, size.height)
})
78 实现memoize缓存函数
function memoize(func, resolver) {const memoized = function (...args) {const key = resolver ? resolver.apply(this, args) : args[0];if (memoized.cache.has(key)) {return memoized.cache.get(key)}const result = func.apply(this, args)memoized.cache.set(key, result)return result;}memoized.cache = new WeakMap()return memoized
}var object = {a: 1, b: 2}var values = memoize((obj) => Object.values(obj))
console.log(values(object))
79 模拟实现lodash的get函数
function get(object, path, defaultValue) {let obj = objectif (typeof path === 'string') {const reg = /[^\[\].]+/gpath = path.match(reg)}for (const key of path) {if (!obj) {return defaultValue}obj = obj[key]}return obj === undefined ? defaultValue : obj
}const object = {a: [{b: {c: 1}}]}
console.log(get(object, ['a', '0', 'b'], null))
console.log(get(object, ('a[0].b.c'), null))
80 给fetch添加超时功能
function createFetchWithTimeout(timeout = 1000) {return function (url, options) {return new Promise((resolve, reject) => {const signalController = new AbortController();fetch(url, {...options,signal: signalController.signal}).then(resolve, reject);setTimeout(() => {reject(new Error('fetch timeout'))// 取消请求signalController.abort()}, timeout)})}
}const request = createFetchWithTimeout(3000)
request("http:****")
81. 主流浏览器有哪些
- Navigator(Gecko)
- Opera(Preston/Blink)
- IE(Trident) / Edge(Chromium)
- Firfox(Gecko)
- Safari(Webkit)
- Chrome(Webkit, chromium, Blink)
82. 浏览器有哪些进程和线程
- 进程
- 浏览器主进程(只有一个)
- GPU进程(3D渲染)
- 第三方插件进程
- 浏览器渲染进程(内核)
- 线程
- GUI渲染线程(html + css)
- js解析引擎线程
- 事件触发线程(事件队列)
- 定时器触发线程(setTimeout)
- 异步网络请求线程(Ajax)
83. canvas转成file
canvas.toBlob((blob) => {const file = new File([blob], 'avatar.jpg', {type: 'image/jpeg'})
}, 'image/jpeg')
84. dataURL转blob
dataURLToBlob: function (dataurl) {let arr = dataurl.split(',')let mime = arr[0].match(/:(.*?);/)[1]let bstr = atob(arr[1])let n = bstr.lengthlet u8arr = new Uint8Array(n)while (n--) {u8arr[n] = bstr.charCodeAt(n)}return new Blob([u8arr], { type: mime })
}
85. 盒子水平居中方式
方式1:
.parent{position: relative;width: 800px;height: 800px;background: red; }.children{width: 100px;height: 100px;background: blue;position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);}
方式2:
.parent{position: relative;width: 800px;height: 800px;background: red; }.children{width: 100px;height: 100px;background: blue;position: absolute;left: 0px;right: 0px;top: 0px;bottom: 0px;margin: auto;}
方式3:flex方式
方式4:
文本居中: line-height : height
图片居中 vertical-align: middle
flex定位: display: flex; margin: auto
86. 隐藏元素方式
1 visibility: hiddn, 占用空间
2 opacity: 0 不会改变布局,也会触发事件
3 display: none 不占用空间, 改变布局
4 transform: scale(0) 缩放无限小,元素所在的位置保留
87. Symbol
作用: 保证每个属性都是独一无二的,防止属性冲突
参考: ![https://blog.csdn.net/wsz123456wsz/article/details/127598604]
88. js 安全性
- XSS 跨站脚本攻击
攻击者通过注入恶意代码来利用网络漏洞,从而在受害者浏览器与逆行代码,分为三种,存储型,反射型,DOM型- 存储型XSS:攻击者向目标网站的数据库注入恶意脚本,当用户访问,这些脚本会被执行
解决办法:过滤输入数据,并编码输出数据 - 反射型XSS:攻击者向目标网站发生恶意链接,当用户点击,脚本被执行
解决办法:校验数据数据,编码输出数据 - DOM型XSS:攻击者将恶意脚本注入到网页中,用户交互时脚本被执行
解决办法:使用安全的js库,避免使用eval函数
- 存储型XSS:攻击者向目标网站的数据库注入恶意脚本,当用户访问,这些脚本会被执行
- CSRF 跨站请求伪造
攻击者通过欺骗用户到目标网站上执行某些操作来发起伪造请求,比如一个攻击者可能会发送一个包含修改用户密码的请求,而这个请求看起来是来自用户自己的浏览器,因此网站可能会对请求进行处理并修改用户密码。由于这个请求来自攻击者而不是用户,因此这种攻击被称为跨站请求伪造。
解决办法:- 使用令牌,CSRF令牌是一种随机生成的值,用于验证用户的请求是否来自合法来源。服务器会将CSRF令牌包含在生成的表单中,然后在处理表单提交时验证该令牌。如果请求中没有正确的CSRF令牌,服务器将拒绝请求。
- 另一种防止CSRF攻击的方法是使用SameSite属性。SameSite属性是一种cookie属性,可以控制cookie是否可以被跨站点请求访问。当设置SameSite属性为“strict”时,浏览器将禁止跨站点请求访问该cookie,从而防止CSRF攻击。
- CORS 跨源资源共享
跨域请求是指来自不同域的资源请求,例如从一个网站向另一个网站发出AJAX请求。由于浏览器的同源策略,一般情况下,跨域请求是不允许的。CORS是一种通过HTTP头来授权浏览器允许特定的跨域请求的机制。
解决办法:配置CORS:为了配置CORS,服务器需要向响应添加一些特定的HTTP头。这些头告诉浏览器哪些跨域请求是允许的,哪些不允许。常见的CORS头包括Access-Control-Allow-Origin、Access-Control-Allow-Methods和Access-Control-Allow-Headers等。
89. js安全最佳实战
- 禁用不必要的js功能
- 使用https
- 使用密码哈希,密码不能明文存储
- 定期更新js库
90. vue 通讯方式
- props和$emit
- 父组件provide,子组件inject来注入变量
- $refs获取组件实例
- eventBus
- vuex状态管理
91. vue生命周期
- beforCreate 无法访问data,methods,computed
- created 可以访问无法访问data,methods,computed,不能访问dom
- beforeMounte render首次调用
- mounted 真实dom挂在完成
- beforeUpdate 数据修改,但是页面未修改
- updated 页面修改完成
- beforeDestory 可以做销毁前的一些工作
- destoryed
92. vue事件指令
- v-once - 定义它的元素或组件只渲染一次,包括元素或组件的所有节点,首次渲染后,不再随数据的变化重新渲染,将被视为静态内容。
- v-cloak - 这个指令保持在元素上直到关联实例结束编译 – 解决初始化慢到页面闪动的最佳实践。
- v-bind - 绑定属性,动态更新HTML元素上的属性。例如 v-bind:class。
- v-on - 用于监听DOM事件。例如 v-on:click v-on:keyup
- v-html - 赋值就是变量的innerHTML – 注意防止xss攻击
- v-text - 更新元素的textContent
- v-model - 1、在普通标签。变成value和input的语法糖,并且会处理拼音输入法的问题。2、再组件上。也是处理value和input语法糖。
- v-if / v-else / v-else-if。可以配合template使用;在render函数里面就是三元表达式。
- v-show - 使用指令来实现 – 最终会通过display来进行显示隐藏
- v-for - 循环指令编译出来的结果是 -L 代表渲染列表。优先级比v-if高最好不要一起使用,尽量使用计算属性去解决。注意增加唯一key值,不要使用index作为key。
- v-pre - 跳过这个元素以及子元素的编译过程,以此来加快整个项目的编译速度。
93. vue computed 和watch区别
- computed 有缓存值,可以设置setter 和 getter,一般用在模板渲染中
- watch 监听值变化,适合用户监听值变化然后去做一些复杂的业务逻辑
94. vue v-if 和v-for为什么不能同时使用
- 会先解析v-for,再解析v-if, 会带来性能消耗
- 可以使用template先判断if
<template v-if="isShow"><p v-for="item in items">
</template>
- 可以使用计算属性, 过滤掉不需要显示的
computed: {items: function() {return this.list.filter(function (item) {return item.isShow})}
}
95. vue2 响应数据原理
思路: 数据劫持 + 观察者模式
- 数据劫持, 利用Object.defineProperty劫持对象属性的set和get,数据变化时候,触发消息给订阅者。
- vue 双向绑定整合了Observer,Complile和Watcher
- Observer 监听器,对数据对象遍历,包括子鼠星,利用defineProperty监听setter和getter,来监听数据变化,
- Compile 解析器 解析编译Vue模板数据, 渲染视图,同时将每个执行对应的绑定更新数据,监听数据变化,添加到监听数据订阅者,同步更新函数
- Watcher 订阅者, 订阅observer属性变化的消息,当发生变化,来通知complie来更新函数 来搭建Observer 和Compile通信桥廊
- Dep 订阅器,采用发布,订阅模式,用来手监听阅者watcher,对监听observice和watcher进行统一管理
https://img-blog.csdnimg.cn/20190920152632212.png
96 vue 检测数组变化
vue将data中的数组进行了原型链重写,指向了自己的原型方法,如果数组包含了引用类型,会再次遍历依赖,实现了数组变化监听,有以下两种情况无法监听到变化
- 利用索引设置项 vm.items[index] = newValue
- 修改数组长度 vm.items.length = newLength
以上解决办法
vm.$set(vm.items, indexOfItem, newValue)
97. vue 父子组件生命周期函数执行顺序
- 渲染过程
父beforeCreate -> 父created -> 父beforeMount
-> 子beforeCreate -> 子created ->子beforeMount ->子Mounted
-> 父 mounted - 更新过程
父 beforeUpdate ->
子 beforeUpdate -> 子 updated ->
父 updated - 销毁过程
父 beforeDestroy ->
子 beforeDestroy -> 子 destroyed ->
父 destroyed
总结:父组件先开始,子组件先结束
98. vue v-model双向绑定的原理
- model 更新视图,使用数据劫持+观察者模式 参考 95
- 视图更新model,v-model本质是value+input的语法糖,用户输入,监听input变化,从而更新value
<input v-model='something'>
相当于
<input v-bind:value="something" v-on:input="something = $event.target.value">
如果在自定义组件中,会利用名为value的prop和input事件
父组件:
<ModelChild v-model="message"></ModelChild>子组件:
<div>{{value}}</div>props:{value: String
},
methods: {test1(){this.$emit('input', '小红')},
},
99. vue3响应和vue2响应式区别
- vue2 使用object.defineProperty, vue3 使用proxy
- Object.defineProperty 无法检测数组下标变化,且智能代理对象的第一层属性,如果对象属性还是对象,需要递归遍历,proxy代理也是第一层,子属性如果是对象,则通过reactive继续做代理,实现深度代理
- Object.defineProperty无法监控新增加的属性,需要借助$set, proxy没问题
- proxy再ie上有兼容问题
100. vue2 和 vue3 diff算法分别说一下
diff 算法就是比较新旧两颗树,找出差异化,利用打补丁的方式进行局部渲染,触发时机是 _update(_rende())
- 同层比较,只比较统一层级,不会跨级
- tag不相同,直接删除重建,不在深度遍历
- tag和key相同,认为是相同节点,继续深度遍历
101. hash 和 history 模式实现原理
- hash 是带 # , 地址变化,不会重新请求,通过hasChange事件可以知道hash是否发生变化
- history实现,主要具有html5 发布的两个标准,pushState 和 replaceState,这两个api可以再改变url,但是不会重新发送请求,
- 区别
- hash兼容低版本,但是history不行
- hash有#, history没有
- 刷新页面,hash模式可以正常加载到hash对应页面,而history没有处理的话,会返回404,一般需要后端将所有页面都配置重定向路由
102. vue-router路由钩子函数是什么,执行顺序是什么
- 钩子函数总类有全局守卫,路由守卫,组件守卫
- 全局路由守卫
- beforeEach,全局前置守卫,路由跳转前触发,比如拦截登录验证
- beforeResolve,全局解析守卫,和beforeEach类似,路由跳转前触发,再beforeEach和组件内beforeRouteEnter后,afterEach前触发
- afterEach, 全局后置钩子,发生在beforeEach和beforeResolve 之后,没有next
- 路由守护 beforeEnter
- 再beforeEach后执行
const router = new VueRouter({routes: [{path: '/foo',component: Foo,beforeEnter: (to, from, next) => {// ...}}]
- 组件守卫
- 为配置路由的组件添加生命周期函数
<template>...
</template>
<script>
export default{data(){//...},beforeRouteEnter (to, from, next) {// 在渲染该组件的对应路由被 confirm 前调用// 不!能!获取组件实例 `this`,可以通过next(vm => {// 通过 `vm` 访问组件实例})// 因为当守卫执行前,组件实例还没被创建},beforeRouteUpdate (to, from, next) {// 在当前路由改变,但是该组件被复用时调用// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。// 可以访问组件实例 `this`},beforeRouteLeave (to, from, next) {// 导航离开该组件的对应路由时调用// 可以访问组件实例 `this`}
}
</script>
- 触发顺序
1 触发进入其它路由
2 调用要离开路由的组件守卫beforeRouteLeave
3 调用全局的前置守卫beforeEach
4 在重用的组件里调用 beforeRouteUpdate
5 在路由配置里调用 beforeEnter
6 解析异步路由组件
7 在将要进入的路由组件中调用beforeRouteEnter
8 调用全局的解析守卫beforeResolve
9 导航被确认
10 调用全局的后置钩子afterEach。
11 触发 DOM 更新mounted。
12 执行beforeRouteEnter守卫中传给 next的回调函数
103. vue项目中你做了哪些优化
- 对象层级不要太深,否则会影响性能
- 不需要响应的数据不要放在data中,可以使用Object.freeze冻结
- v-if, v-show, computed,watch分场景使用
- 大数据表格做虚拟列表
- 防止内存泄漏,组件销毁,把全局变量和时间清理
- 图片懒加载
- 路由懒加载
- 异步路由
- 按需加载
- 防抖和节流应用
- 服务器端渲染或则预渲染
104. nextTick 原理
- 作用,vue更新dom不会立马更新,是异步的,nextTick回调实在下次dom更新循环结束后回调
- 原理,使用了微任务和宏任务,和执行环境有关
- Promise
- MutationObserver, h5新加额功能,
- 如果以前都不行,则采用setTimeout
105. Vue.set原理
了解 Vue 响应式原理的同学都知道在两种情况下修改 Vue 是不会触发视图更新的。
在实例创建之后添加新的属性到实例上(给响应式对象新增属性)
直接更改数组下标来修改数组的值。
Vue.set 或者说是 $set 原理如下
因为响应式数据 我们给对象和数组本身新增了 ob 属性,代表的是 Observer 实例。
当给对象新增不存在的属性,首先会把新的属性进行响应式跟踪 然后会触发对象 ob
的 dep 收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去
更新数组。
106. css盒子模型
- ie盒子模型
- w3c盒子模型
- 盒子模型有 内容content,填充padding, margin, border
- 区别: IE盒子模型把content算到border和padding里面去了
107. css 优先级
!import > 行内样式 > ID选择器 > 类选择器 > 标签选择器 > 通配符 > 继承 > 浏览器默认值
108. link 和 @import区别
- link是XHTML标签,除了加载CSS外,还可以定义RSS等,@imprt只加载css
- link和htm元素同时加载,@import需要等元素全部加载才会加载
- link无兼容性问题,@import在css2.1引入
- link支持js控制dom去改变元素,@import不支持
109. rem em vh px含义
- rem 相对于根元素文字大小相关
- em 相对于父元素大小
- vw vh 视口的宽高
- px 分辨率
110. 解决chrome 小于12px像素问题
- -webkit-text-size-adjust: none
110 new操作符做了什么
- 创建空对象,this指向该对象,同时继承了该函数原型
- 属性和方法加载到this引用中
- 新创建的对象由this所引用,并返回this
111. iframe 优缺点
- 优点:
- 解决加载缓存满的第三方内存,如广告等
- 安全的沙盒机制
- 并行加载脚本
- 缺点
- iframe会阻塞主页面的onload事件
- 内容为空,也需要加载时间
- 没有语意
112. href和src区别
- 请求资源类型不同
- href:指向网络资源,建立和当前元素或当前文档链接之间的联系
- src:会将资源下载到文档中,比如脚本,img图片
- 作用不同
- href:当前文档和引用资源建立关系
- src:替换当前内容
- 浏览器解析不同:
- href 不会阻塞浏览器的解析下载
- src会停止其他资源下载处理,将资源加载,编译,执行完
113. rgba 和 opacity透明效果有和不同
- rgba 设置后后代元素不会继承不透明属性
- opactiy 会继承父类的opacity属性
114 position的值,relative和absolute分别是相对谁进行定位的
- relative: 相对定位, 相对于自己本身在正常文档流中位置进行定位
- absolute:决定定位,相对于近一级定位不是static的父元素进行定位
- fixed: 绝对定位,相对于浏览器或则iframe进行定位
- static: 默认值,没有丁定位,在正常文档流中定位
- sticky:粘性定位,容器位置根据文档流计算得出
115 calc, support, media
- calc 函数,动态计算长度,支持 + - / 等
- support 主要检查浏览器支付支持css某个属性
- @media 媒体查询, 针对不同媒体类型定义不同样式
116 如何画三角形
.a {width: 0;height: 0;border-width: 100px;border-style: solid;border-color: transparent #0099CC transparent transparent;transform: rotate(90deg); /*顺时针旋转90°*/
}
<div class="a"></div>
117 css 兼容性问题如何解决
- 每个浏览器都有默认的margin和padding,解决方案: 全局 *{margin:0, padding: 0}
- chrome默认中文不小于12px,解决方案: 通过-webkit-text-size-adjust: none 解决
118 document.write 和innerHtml区别
- document.write 绘制整个界面
- innerHtml 绘制一部分
117 闭包
- 闭包三个特点
- 函数嵌套函数
- 函数内部可以引用外部的参数和变量
- 参数和变量不会被垃圾回收
118 垃圾回收机制
- 标记清除法: 进入环境,打上标记, 离开环境,清除标记
- 引用计数法:引用了一次+1,移出了引用-1
119 vuex
store状态存储响应
- State 基础数据,定义了状态的数据结构
- Getter 从基础数据派生的数据,允许store获取数据, mapGetter辅助函数将store的getter映射到局部计算属性
- Mutation 唯一的更改store的状态方法, 必须是同步
- Action 装饰器,包裹了mutations,可以异步,不能直接改变状态
- Module 模块化vuex,允许将多个store拆分成多个store,且保存在单一的状态树中
120 谈谈对 keep-alive了解
一般用作组件状态保留的,避免重新渲染
- 一般结合路由和动态组件一起使用,用于缓存组件
- 提供了include和exclude,支持字符串和正则表达式 exclude优先级高于include
- 两个钩子函数activated和deactviated,被激活和被移除触发
121 vue路由模式
- hash, 使用url hash来计算,支持所有浏览器,
- history 依赖html5 history api
- abstract 只有所有js环境,比如node.js
122 vue key的作用
key是对vue的vnode的唯一标记,通过key,diff操作会更准,更快,特别是在带有for循环的时候,需要设置key
#123 vue3 新的更改
- 监听机制, 解决了数组下表该变化和新增属性不能被监听到的问题
- 模板, 修改了作用域插槽,把插槽改成函数,自会影响子组件渲染
- 对象是组件声明
124 vue模板编译原理
- 模板字符串 转成 elementAST(解释器)
切割vue,生成AST语法树,大量正则匹配 - AST进行静态节点标记,主要来做虚拟dom的渲染优化(dom更新,不需要diff静态节点)
- 使用elementAST生成render函数代码字符串
- 组件渲染调用render函数就可以
125 性能6大指标
- 加载优化
- 执行优化
- 渲染优化
- 样式优化
- 脚本优化
6 V8 引擎优化
126 如何防止回流
- 回流产生
- 窗口调整大小
- 改变字体
- 增加样式或则删除样式
- 内容变化,input
- 操作dom
- offsetWidth 和offsetHeight
- 多层内联样式
- 如何避免
- 避免使用table布局
- dom 叶子节点更改class
- 避免多层内联样式,设置动画,最好脱离文档流
- 操作元素,可以先设置不可见, display: none, 操作完成,在显示出来
- 避免频繁操作dom
127 移动端优化
- 使用css3动画,开启硬件加速
- touch代替click
- 合理使用requestAnimationFRame,少用setTimeout
- 避免css3阴影渐变效果
127 Array.forEach 和 Array.map
- forEach 遍历每个元素但不返回新的数组
- map 遍历数组并返回新的数组
128 vue 导入图片方式
- css 静态引入
- image中的src
- import 语句
- URL 方式
129 浅拷贝 深拷贝
- 浅拷贝
let aar = [2,3,[4,6]]
let arr2 = [..arr]
arr[0] != arr2[0]
arr[2] == arr2[2]let obj = (a: 1, b: {c: 1})
let obj1 = {...obj}
let obj2 = Object.assign(obj2, obj)
- 深拷贝
let arr = [2,3,[4,6]]
let arr2 = JSON.parse(JSON.stringify(arr))function deepCopy(obj, cache = new WeakMap) {if (type obj !== 'object' || obj === null) {return obj }if (cache.has(obj)){return cache.get(obj)}const clone = Array.isArray(obj) ? [] : {}cache.set(obj, clone)for (let key in obj) {if (Object.prototype.hasOwnProperty.call(obj, key)) {clone[key] = deepCopy(obj[key], clone)}}return clone
}
130 useVModel 改变props数据通用方法
// useVModel.jsimport {computed} from 'vue'
const cacheMap = new WeakMap();
export function useVModel(props, propName, emit) {return computed({get() {if (cacheMap.has(props[propName])) {return cacheMap.get(props[propName])}const proxy = new Proxy(props[propName], {get(target, key) {return Reflect.get(target, key)},set(target, key, value) {emit('update:' + propName, {...target, [key]: value})return true}})cacheMap.set(props[propName], proxy)return proxy},set(val) {emit('update:' + propName, val)}})
}// 使用
// index.vue
<template><div class=""><child v-model="objRef"></child></div>
</template><script setup lang="ts">
import { ref } from 'vue';
import Child from './Child.vue'
const objRef = ref({name: 'jiang'})
</script><style scoped>
</style>
// Child.vue
<template><input v-model="modelValue.name"><div class="">{{ modelValue.name }}</div>
</template><script setup lang="ts">
import {useVModel} from './useVModel.js'const props = defineProps({// modelValue 是默认名字, 使用v-mode="111" 传值modelValue: {type: Object,default: {}}
})
const emits = defineEmits(['update:modelValue'])
const nameProxy = useVModel(props, 'modelValue', emits)
</script><style scoped>
</style>
140 主题切换
html[data-theme='dark']{--text-color: #fff;--bg1: #102128;
}:root {--text-color: '#333';--bg1: #fbd988;
}html
切换主题
<html data-theme="dark"></html>
141 防止debug
setInterval(function() {debuggetCheck();
}, 1000)var debuggetCheck = function () {function doCheck(a) {if (('' + a / a)['length'] !== 1 || a % 20 === 0){//动态生成匿名函数,函数体是 debugger, 动态生成的,类似eval(function(){}['constructor']('debugger')());} else {(function(){}['constructor']('debugger')());}doCheck(++a)}try{doCheck(0)}catch(err) {}
}debuggetCheck();
131 ES6 Promise 和 Promise A+
- Promise A+ 是Promise的一个规范,包含
- promise状态(pending初始状态,fulfilled最终状态,rejected最终环境)
- then方法
- promise解析过程(resolvePromise)
- ES6的Promise是PromiseA+规范的实现,并增加了all和race等
- promise状态流转
- pending->resolve->fulfilled
- pending->reject->rejected