以下是本人一年多整理的前端学习笔记,现汇总分享给大家,很多问题都是面试必问的
更多学习资源,可以点击我获取更多
原始类型: null undefined number string boolean Symbol BigInt
引用类型: 对象
本身属性判断: obj.hasOwnProperty(key)
包括了上级属性判断: key in obj
Reflect.get(obj, ‘key’) 没有返回undefined
Reflect.has(obj, ‘key’)
// 1 把 10000000 格式化成 10,000,000
let str = '10000000'
str = str.replace(/(?=\B(\d{3})+$)/g, ',')
console.log(str)
div{
scroll-behavior: smooth
}
function fn(){
if (new.target){
throw new Error("can't invoke with 'new'")
}
console.log('fn call')
}
new fn() // error
var account1 = '我\u200d是\u200d中\u200d国\u200d人',
account2 = '我是中国人';
console.log(account1.length, account2.length)
// 打印 9 5
console.log(account1, account2)
// 打印 我是中国人 我是中国人
/u200d 是零宽度字符,不会具体显示出来,作用如下
1 起到水印,防止文章盗版,别人拷贝文字,也会把零宽字符也会一起盗版
2 实现隐藏信息加密传递和解密处理
可迭代协议:就是任何一个对象,只要包含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 iterable
at <anonymous>:1:16
原因是{a: 1, b: 2} 不是一个可迭代的对象,那么如何实现迭代呢,是要让对象实现Symbol.iterator就可以
Object.prototype[Symbol.iterator] = function(){
return Object.values(this)[Symbol.iterator]()
}
null 表示是一个对象,但是此时没有对象就是null
undefined 表示是任何一个值,但是此时没有值就是undefined
以下这些都是undefined
const fontSize = 20
const color = '#00ff00'
const hi = style`
font-size: ${fontSize}px,
color: ${color}
``aaaaaa`
function style () {
console.log(arguments)
return style
}
style 是一个函数,会把模板值作为参数传给函数
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
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 arr2
if (arr2.length === 0) return arr1
var 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'))
/**
* 并发请求
* @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) return
const i = index
const 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 不能通过new
if (!(this instanceof Example)) {
throw new TypeError(' new func is error');
}
console.log(this.name)
},
enumerable: false
})
Example.prototype.func = function (){
console.log(this.name)
}
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)
})
1 污染全局
2 块级作用
3 重复生命
4 暂时性死区 TDZ
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')
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] + carry
carry = Math.floor(n / 10)
result = (n % 10) + result
}
if (carry){
result = '1' + result
}
return result
}
console.log(sum('1112222222222222222222222222222222222222222233333333333333333333333', '2'))
.box{
position: sticky;
top: 0;
}
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;
}
1 e.x
鼠标到视口窗口左侧距离,忽略滚动条
2 e.clientX
同e.x
3 e.pageX
鼠标到页面左侧距离,包含滚动条的距离
4 e.screenX
鼠标到电脑整个屏幕左侧距离
5 e.movementX
鼠标到上一次位置的横向距离
6 e.offsetX
鼠标到目标元素(e.target)左侧距离
https://www.imgeek.net/article/825358760
XXX.addEventListener('XXXX', () => {},
{
once: true
})
// 模仿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()
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
1.原型链继承
缺点:无法调用父类构造函数
2.借用构造函数维承
缺点:无法电泳父类原型函数
3.组合式继承
缺点:会调用两次构造方法
4.ES6的class类继承
当没有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不会立即获取,会等到空闲时候才会获取
不能作为构造函数使用,不能new
箭头函数就没有原型
箭头函数没有arguments
箭头函数不能使用call, apply,bind去噶便this的执行
this指向外层第一个函数的this
// 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]
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] = val
console.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] = val
console.log(k, '更改')
}
}
})
return proxy
}
const proxy = observe(obj)
proxy.a = 'aaa'
proxy.c.a = 'bbbbb'
proxy.cccc
console.log(proxy.c.a)
width: fit-content
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)
.title{
font-size: 3em;
transition: 0.5s,
mix-blend-mode: difference
}
function timeout(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time)
});
}
class SuperTask{
constructor(paralleCount){
// 并发数量
this.paralleCount = paralleCount
this.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
因为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.fetch
window.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++] = result
const 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 = 0
func()
}
err.then(reRun, reRun)
}
}
}
run(main)
img{
width: 50%;
height: 50%;
border-radius: 50%;
shape-outside: circle(50%)
}
function isPromiseLike(value) {
return (value !== null && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function')
}
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]'
// import导入只是标记一下变量,ESM符号绑定
import {n, increase} from './a'
// 结构是具体赋值
const {n, increase} = a
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)
}
}
方案一:缺点,原型链上追加属性无效
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)
有时候vue2在频繁给数组对象赋值,会很消耗性能,因为要执行Object.defineProterty
冻结对象
this.datas = Object.freeze(this.getDatas())
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')
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()
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' }
const randomColor = () =>
'#' +
Math.floor(Math.random() * 0xffffff)
.toString(16)
.padEnd(6, '0')
console.log(randomColor())
const randomString = () => Math.random().toString(36).slice(2)
console.log(randomString())
const removeTag = (fragment) =>
new DOMParser().parseFromString(fragment, 'text/html').body.textContent | ''
console.log(removeTag('你哈'))
数据变化后,会自动重新运行依赖该数据的函数
function foo(){
// setTimeout(foo, 0) // 不会栈溢出
setTimeout(foo(), 0) // 会栈溢出
}
foo()
function request(url, maxCount = 5) {
return fetch(url).catch(err => maxCount <= 0? Promise.reject(err) : request(url, maxCount--))
}
request('http://wwww', 5)
修改this指向,等同call,但不一样的是call参数不是数组
函数.apply(参数1, …参数2)
等价于
参数1.函数(…参数2)
console.log.call.call.call.call.apply((a) => a, [1,2])
// 不输入任何值
class A{
#name; // 定义私有字段
constructor(){
this.#name = 'jiang'
}
#method(){
console.log('私有方式,外面不能访问')
console.log(this.#name)
}
}
const a = new A()
A.#name // 无法访问
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
.container{
animation: rotate 20s linear paused infinite;
}
.container:hover{
animation-play-state: running;
}
元素背后的div模糊
.mark{
backdrop-filter: blur(5px)
}
span{
font-size: 12px;
transform: scale(0.5);
display:inline-block;
transform-origin: left center
}
.danmu{
-webkit-mask-image: url(./mark.svg);
-webkit-mask-position: center;
-webkit-mask-repeat: no-repeat;
}
elm.scrollIntoView({
behavior: "smooth",
block: "center"
})
var a;
console.log(b)
if (true){
console.log(a)
console.log(b)
a = 5
function a() {}
function b() {}
a = 0
console.log(a)
}
console.log(a)
console.log(b)
// 函数不管在哪里声明,都会提升到顶层,
// 如果是判断里面,提升到顶部会赋值undefine
// 当代码hi行到判断里面,函数a和b会生成块级作用域,值是本身
.item{
width: 100px;
height: 100px;
transform: rotate(120deg) translate(100px, 100px)
}
transform 执行顺序是从右往左,先平移,在旋转,且旋转都是按照平移钱为中心旋转
.container{
width: 90%;
margin: 0 auto;
aspect-ratio: 16/9.0
}
参考:https://www.douyin.com/user/MS4wLjABAAAAi2oukRVcHpgD-HbVdzsxE7tYykr91YuIKukR_X_Yy08EFWRQhRrECDF6FvbvT8Xa?is_search=1&list_name=follow&modal_id=7124153340170112268&nt=0
console.log(['1', '2', '3'].map(parseInt))
// 得到结果 [ 1, NaN, NaN ]
/**
* [
* 函数('1', 0),
* 函数('2', 1),
* 函数('3', 2)
* ]
*/
.parent{
display: flex
}
.item{
margin: auto; // 整体居中
margin-left: auto; // 居右
margin-right: auto; // 居左边
}
const people = [
{name: 'jiang', age: 20},
{name: 'mao', age: 22},
{name: 'han', age: 30},
]
function groupBy(arr, generateKey){
// 参数归一化
if (typeof generateKey === 'string') {
const propName = generateKey
generateKey = (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 ? '偶' : '奇'))
initial:主动将某一css属性设为默认值
unset:清除浏览器默认样式(全部清除all:unset)
revert:恢复浏览器默认样式 (全部恢复all:revert)
概念:每个对象都有一个原型(prototype),并从愿原型上继承属性和方法,原型本身也是一个对象,它也有自己的原型,形成一个链式结构
对象寻找属性过程:
假设 p 是对象,p.name
全称:Block Formatting Context
简称: BFC
概念: 它是一块独立的渲染区域, 它规定了在该区域钟,常规流块盒的布局
1. float: left
2. position: absolute
3. overflow: hidden
BFC具体规则:
.clearfix::after{
content: '';
display: block;
clear: both
}
或则
position: absolute
或则
float: left
或则
overflow: auto , scroll, hidden
或则
display: flow-root
参考https://www.douyin.com/user/MS4wLjABAAAAeIIkCgELXG6XdUxuE9nQ6W4AfS-aoPFbtmnBL8ytcYtBSyurgePBYZXJpB0LJBCT?modal_id=7214297604476112188
const randomNum = (min, max) => Math.random()*(max - min) + min
主线程
主线程是执行代码的地方,遇到函数,会把该函数放入执行栈
执行栈
执行结构,有函数就会从栈取出,直接执行
任务队列
在执行栈中,遇到比如setTimeout,会放入任务队列,等到计时结束,会任务队列放入执行栈判断是否要执行
任务分为宏任务和微任务,优先检查微任务是否有执行,如果没有,在检查宏任务是否有执行
微任务是在当前事件循环的尾部去执行;宏任务是在下一次事件循环的开始去执行
宏任务
有明确的异步任务需要执行和回调;需要其他异步线程支持。
setTimeout
setInterval
setImmediate
微任务
没有明确的异步任务需要执行,只有回调;不需要其他异步线程支持。
promise.then promise.catch
new MutaionObserver()
promise.nextTick()
// 对象结构
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'
})
自定义指令
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)
})
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))
function get(object, path, defaultValue) {
let obj = object
if (typeof path === 'string') {
const reg = /[^\[\].]+/g
path = 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))
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:****")
canvas.toBlob((blob) => {
const file = new File([blob], 'avatar.jpg', {
type: 'image/jpeg'
})
}, 'image/jpeg')
dataURLToBlob: function (dataurl) {
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
}
方式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
1 visibility: hiddn, 占用空间
2 opacity: 0 不会改变布局,也会触发事件
3 display: none 不占用空间, 改变布局
4 transform: scale(0) 缩放无限小,元素所在的位置保留
作用: 保证每个属性都是独一无二的,防止属性冲突
参考: ![https://blog.csdn.net/wsz123456wsz/article/details/127598604]
<template v-if="isShow">
<p v-for="item in items">
template>
computed: {
items: function() {
return this.list.filter(function (item) {
return item.isShow
})
}
}
思路: 数据劫持 + 观察者模式
vue将data中的数组进行了原型链重写,指向了自己的原型方法,如果数组包含了引用类型,会再次遍历依赖,实现了数组变化监听,有以下两种情况无法监听到变化
vm.$set(vm.items, indexOfItem, newValue)
相当于
如果在自定义组件中,会利用名为value的prop和input事件
父组件:
子组件:
{{value}}
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小红')
},
},
diff 算法就是比较新旧两颗树,找出差异化,利用打补丁的方式进行局部渲染,触发时机是 _update(_rende())
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>
了解 Vue 响应式原理的同学都知道在两种情况下修改 Vue 是不会触发视图更新的。
在实例创建之后添加新的属性到实例上(给响应式对象新增属性)
直接更改数组下标来修改数组的值。
Vue.set 或者说是 $set 原理如下
因为响应式数据 我们给对象和数组本身新增了 ob 属性,代表的是 Observer 实例。
当给对象新增不存在的属性,首先会把新的属性进行响应式跟踪 然后会触发对象 ob
的 dep 收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去
更新数组。
!import > 行内样式 > ID选择器 > 类选择器 > 标签选择器 > 通配符 > 继承 > 浏览器默认值
.a {
width: 0;
height: 0;
border-width: 100px;
border-style: solid;
border-color: transparent #0099CC transparent transparent;
transform: rotate(90deg); /*顺时针旋转90°*/
}
store状态存储响应
一般用作组件状态保留的,避免重新渲染
key是对vue的vnode的唯一标记,通过key,diff操作会更准,更快,特别是在带有for循环的时候,需要设置key
#123 vue3 新的更改
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
}
// useVModel.js
import {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>
html[data-theme='dark']{
--text-color: #fff;
--bg1: #102128;
}
:root {
--text-color: '#333';
--bg1: #fbd988;
}
html
切换主题
<html data-theme="dark">
html>
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();