学习心得1"/>
近期分享学习心得1
1、数据类型和typeof
原始类型undefined null number string boolean symbol bigint
引用类型 对象
函数就是可以被调用(被执行)的对象,函数比对象多了个键名为[[call]]的属性
2、大厂为什么不允许赋值undifined?
必须let a=void 0;效果一样
void是关键字,后面跟表达式,无论跟啥最终都为undefined,但是习惯写0。
因为在js里面undefined压根不是关键字(见上图不报错),而是window.undefined,window的一个属性,可能是js设计缺陷。
window.undefined=1,不可以设置,因为是只读属性。
因为undefined不是关键字,
但是除了全局作用域,还有函数作用域,
function m(){
let undefined=1;
let a=undefined;
}
于是bug出现了
同事扩展了一个,源码里赋真假值,用的是!0和!1,节省字符,
!0具有节省2个字符的额外好处,而!1具有节省3个字符的额外好处
3、随机颜色
常规是这么做
进阶版
‘#’+Math.random().toString(16).substring(2,8)
可能会存在位数不够的问题,if判断,用以下办法,不足6位后面补0
‘#’+Math.random().toString(16).substring(2,8).padEnd(6,‘0’)
生成固定长度随机数,超出11位,递归
4、判断对象中是否存在某个属性
function hasProperty(key,obj){return key in obj;
}
属性可能在原型链上
所以不能用for in
for in 循环,遍历自身和原型上的可枚举属性,symbol 类型的属性遍历不出
for in 不会遍历enumrable=false的属性(可枚举,还有可设置configurable,可写writable),但 in 判断不管enumrable的值为true或false
不能用Object.keys
Object.keys, Object.vlaues, Object.entries 遍历自身可枚举的属性。
不包括 原型 和 symbol类型的属性
不能用Object.getOwnPropertyNames(obj)
遍历自身属性,不包含symbol类型,不可枚举属性也能遍历出
不能用Object.hasOwnProperty
不包括 原型
5、fixed失效情况
6、黑白滤镜
了解一下
转换矩阵
7、数组去重
什么叫做相同?
方式一去重:===严格相等判定重复,但是数组里面有两个相同的对象,那就判断不出来了
必须一致
引用去重方式也是有很多限制的,大部分情况应对的也只是正常的数据结构,而且这里定义什么叫重复也是很有业务场景的,比如定义symbol的重复,作者的场景基本应付的就是一般的比如服务器返回对象数组数据这种
如果考虑周到,首先要面对的就是环数据,就是比如对象互相引用,用递归是会死循环的,另外还要考虑递归的堆栈溢出,还得用蹦床函数才是安全的
另外,数组去重,考点还有一个,一般考这个的话,数组的成员数据类型为数字或者字符串,考点是去重效率,作者的开头的set解构和后面的O(n2)复杂度,都是低效率的
8、两种阴影
box-shadow针对整个盒子做阴影
drop-shadow针对像素点做阴影
去掉阴影
filter:drop-shadow(0 0 10px #fff)
父元素drop-shadow
9、console.log可能存在内存泄漏
函数运行完本来会进行销毁,由于打印了,他会在控制台保持这种引用
webpack工具集成了terser代码压缩工具配置
vite项目需要安装terser插件
10、[‘1‘,‘2‘,‘3‘].map(parseInt)
1. map函数
将数组的每个元素传递给指定的函数处理,并返回处理后的数组,所以 [‘1’,‘2’,‘3’].map(parseInt) 就是将字符串1,2,3作为元素;0,1,2作为下标分别调用 parseInt 函数。即分别求出 parseInt(‘1’,0), parseInt(‘2’,1), parseInt(‘3’,2)的结果。
2. parseInt函数(重点)
概念:以第二个参数为基数来解析第一个参数字符串,通常用来做十进制的向上取整(省略小数)如:parseInt(2.7) //结果为2
特点:接收两个参数parseInt(string,radix)
string:字母(大小写均可)、数组、特殊字符(不可放在开头,特殊字符及特殊字符后面的内容不做解析)的任意字符串,如 ‘2’、‘2w’、‘2!’
radix:解析字符串的基数,基数规则如下:
1) 区间范围介于2~36之间;
2 ) 当参数为 0、NaN、Infinity,parseInt() ,先判断 string 是否以 “0x” 开头,直接解析为十六进制的整数,其他则十进制;
3 ) 如果忽略该参数,默认的基数规则:
如果 string 以 “0x” 开头,parseInt() 会把 string 的其余部分解析为十六进制的整数;parseInt(“0xf”) //15
如果 string 以 0 开头,其后的字符解析为八进制或十六进制的数字;parseInt(“08”) //8
如果 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数;parseInt(“88.99f”) //88
只有字符串中的第一个数字会被返回。parseInt(“10.33”) //返回10;
开头和结尾的空格是允许的。parseInt(" 69 10 ") //返回69
如果字符串的第一个字符不能被转换为数字,返回 NaN。parseInt(“f”) //返回NaN 而parseInt(“f”,16) //返回15
3. 结果
[‘1’,‘2’,‘3’].map(parseInt)即
parseInt(‘1’,0);radix 为 0,parseInt() 会根据十进制来解析,所以结果为 1;
parseInt(‘2’,1);radix 为 1,超出区间范围,所以结果为 NaN;
parseInt(‘3’,2);radix 为 2,用2进制来解析,应以 0 和 1 开头,所以结果为 NaN。
[‘0b11’,‘0x12’,‘013’].map(parseInt)结果为[0, NaN, 1]
0b11,只转换b之前的数字
013,只转换3之前的数字
11、手写防抖
传参版本
function debounce(fn,delay){let timerreturn function(){let args = argumentstimer && clearTimeout(timer)timer = setTimeout(()=>{// apply: 作用就是改变方法内部this的指向, 并能将参数传递给该方法, 最后立即执行这个函数fn.apply(this,args)},delay)}
}
12、不要用setTimeInteval计时器做动画
会导致空帧,跳帧
应该用requestAnimationFrame
永远跟着渲染帧走
哪些情况css动画实现不了,必须使用js动画?
13、数字格式化
let str='1000000000'
//格式改成
'10,000,000,000'
前瞻运算符,匹配位置不匹配字符,?=
$表示字符串是结束的
不仅仅是连续的三位数字,字符串也是结束的
str.replace(/(?=(\d{3})+$)/g,',')
//小括号括起来+不分表示一次或多次
可能会出现
,100,000,000
情况
str.replace(/(?=\B(\d{3})+$)/g,‘,’)
14、平滑滚动
回到顶部,使用css3
滚动整个页面的滚动条需要在根节点html加上scroll-behavior:smooth
15、原型链
见个人Word文档
16、粘性布局
position:stick;要配合定位否则不生效
top:0表示dt依次找父元素,找第一个设置overflow属性的元素,跟他的相对距离,假如没有,就是相对于整个视口,也就是固定定位fixed的参考系,直到父元素也离开这个区域
17、监听元素重叠
加载更多假如是在滚动到底部的时候去触发会导致滚动事件触发太频繁,
换种思路,不监听滚动条,而是监控滚动什么时候出现在画面,
h5提供了构造函数,监控函数有没有出现在区域内
以下函数表示重叠10%(thresholds),重叠进入的时候加载更多(isIntersecting),对象第一个值是回调函数,第二个值是配置,第一次刷新不调用(loading)
问题:懒加载如何实现?
18、失活页面计时器
切换其他标签页,浏览器觉得其他标签页隐藏,为了提升效率,减少电量消耗,不会频繁执行计时器,会把settimeinterval调成最少1秒钟,谷歌浏览器这个策略,有部分浏览器调成15分钟,所有小于1000ms的计时器都会调成1s
一个思路是,可以在切换页面的时候把页面暂停,监听页面有无隐藏,即可见度,
19、鼠标位置问题
20、保持宽高比
比如,哔哩哔哩的视频
给元素直接设置aspect-ratio:16/9;
21、手写promise.all
Promise.all([p1, p2, p3]).then(res=>{});
(1)只有 p1、p2、p3 的状态都变成 fulfilled, p 的状态才会变成 fulfilled,
此时p1、p2、p3 的返回值组成一个数组,传递给 p的回调函数。
(2)只要 p1、p2、p3 之中有一个被 rejected,p 的状态就变成 rejected,
此时第一个被 reject 的实例的返回值,会传递给 p 的回调函数。
[p1, p2, p3]数组是迭代器的一种
Promise.myAll=function(proms){let reso,rejc;conse p=new Promise((resolve,reject)=>{reso=resolve;rejc=reject;})//设置p状态const result=[]//假如迭代器为空的情况写一定是成功let count=0let fulFilledCount=0//记录完成的数量//迭代器只能用for of循环for(const prom of proms){const i=countcount++;Promise.resolve(prom).then(data=>{//then的第二个参数和catch捕获异常时会采取就近原则,当两者都存在时,则只有then的第二个参数能捕获到,如果then的第二个参数不存在,则catch方法会捕获到;//1、将成功的数据汇总到result里result[i]=data//2、判断是不是全部完成fulFilledCount++if(fulFilledCount===count){reso()}},rejc)}if(count===0){reso(result)//判断迭代器为空情况}return p;
}Promise.all([p1, p2, p3]).then(res=>{});
22、比较版本号
常规是使用split方法或者正则,但是上百个版本号比较,效率就会变低,
迭代器
function* walk(str){let part='';let terminals=['.','-'];for(let i=0;i<str.length;i++){if(terminals.includes(str[i])){yield part;//评论说yield是暂停for循环并返回结果part=''}else{part+=str[i]}}if(part){//比如12.1.8的8情况yield part;}
}
const iterator=walk('1.5.6-alpha.1')
let n=iterator.next()
console.log(n)
23、零宽字符
不参与显示的字符叫零宽字符
零宽字符,作用一:隐藏式的水印,作用二:隐藏信息
24、让代码成立
let [a,b]={a:1,b:2}
可迭代协议,任何一个对象只要包含一个属性,并且是个函数
{[Symbol.iterator]:function(){return 迭代器}
}
平常最常见的可迭代的,是数组
let [a,b]={a:1,b:2}是用数组的方式在解构,
只需要把对象变成可迭代的对象就行
Object.prototype[Symbol.iterator]=function(){Object.values(this)[Symbol.iterator]()
}
25、null和undefined到底有什么区别
null表示no object
undefined表示no value
获取不存在的dom元素,因为dom元素是对象
26、x到y是否变化
27、多行文本溢出
父元素里有一个省略号元素和文字元素
首先设置float:right;再设置margin-top;但是文字上面部分并没有环绕。
所以思路并不是margin-top,在文字上面再放一个元素,把元素设置高一点,把所有东西往下挤,
父元素设置伪类displayblock;再设置高度80px,文字元素设置margin-top:-80px;
但是其实这个方法是有问题的,假如字数不够,还不需要…
28、标签化模板
let b=15
let a=tag`Fifteen is ${b},111${b}`//变成了函数的调用,再把函数的返回结果赋值给a
function tag(){return 'abc'//a的结果就是abc
}
打印Arguments,结果是
[Arguments]{'0':['Fifteen is ', ",111" ,''],'1':15,'2':15
}
假如return tag呢,返回了tag函数,继续作为他的标记
let b=15
let a=tag`Fifteen is ${b},111${b}``继续`//变成了函数的调用,再把函数的返回结果赋值给a
function tag(){console.log(arguments)return tag//a的结果就是abc
}
应用
比方说一个随机背景,随机字体颜色的东西,
可以优化成下面这个样子
命令式编程变成了声明式编程
29、锥形渐变
background: conic-gradient(#f00,#ff0,#0f0,#0ff,#00f,#f0f,#f00);
也可以用border做扇形,但是只能是90的倍数
<div class="demo"></div><div class="fan"></div>.demo{width: 150px;height: 150px;border: 5px solid #000;border-radius: 50%;background: conic-gradient(#f40,#00f 120deg,transparent 120deg);}.fan {width: 80px;height: 80px;border-radius: 100px 0 0;background: #aaa;transform: rotate(45deg);transform-origin: bottom right;}
30、按键映射
function test(a) {let obj = {2: ['a', 'b', 'c'],3: ['d', 'e', 'f'],4: ['g', 'h', 'i'],5: ['j', 'k', 'l'],6: ['m', 'n', 'o'],7: ['p', 'q', 'r', 's'],8: ['t', 'u', 'v'],9: ['w', 'x', 'y', 'z'],}let arr = []for (let i = 0; i < a.length; i++) {arr = f(arr, obj[a[i]])}function f(arr1, arr2) {let result = []if (arr1.length === 0) return arr2if (arr2.length === 0) return arr1for (let i = 0; i < arr1.length; i++) {for (let j = 0; j < arr2.length; j++) {result.push(arr1[i] + arr2[j])}}return result}console.log(arr)return arr}test('2345')
31、判断回文
去掉特殊字符,正读反读一样
function test(str) {let newStr = str.toLowerCase()newStr = newStr.replace(/\s/g, '')console.log(newStr)let i = 0, j = str.length - 1const isValid = (str) => (str >= 'a' && str <= 'z' || str >= '0' && str <= '9')//或正则while(j>=i){const left=str[i].toLowerCase(),right=str[j].toLowerCase()if(!isValid(left)){i++}else if(!isValid(right)){j--}else if(left===right){i++;j--}else{return false}}return true}test('A man, a plan, a canal : Panama')
32、锚点更好的替换api(scrollIntoView)
资料
轮播图也可以使用
当校验没有通过的时候,使用scrollIntoView滚动到上方
if(!this.form.eventType){this.$refs.eventType.$el.scrollIntoView({behavior:"smooth", block: "start"})}
33、console
console.time和console.timeEnd可以打印函数耗时时间
console.dir可以打印dom对象结构
console.trace展示堆栈
34、B站弹幕人像闪避
首先是存在AI生成的图片
35、中文输入法多次触发监听
可以在input监听里,设置true/false值
36、统计字符个数
中庸版本
let str='fgasdfadfdasd'let obj={}for (let i = 0; i < str.length; i++) {if(obj[str[i]]){obj[str[i]]++}else{obj[str[i]]=1}}
let str = 'fgasdfadfdasd'let result = str.split('').reduce((a, b) => (a[b]++ || (a[b] = 1), a), {})
reduce首先把第二个参数作为初始值,传到形参a里去,把遍历到的值传到b里去
undefined++等于NaN
括号运算符(x,xx,xxx,xxxx)一定会返回最后一个值
37、parseInt和Math.floor
function random(min,max){max++//可以取到最大值return Math.floor(Math.random()*(max-min)+min)
}
38、为什么原型链上自己定义的值可以被遍历,而其他值无法被遍历?
enumerable可遍历的
如果c不想被遍历,那么这样定义↓
39、容器均分三列
我以前经常使用的是33.33333333%,但不是最优解
三个子元素宽度不设置的话就是0,flex-grow:1表示每个子元素都吃一份
假如三个子元素加上内容,就不均分了
flex-grow表示在当前元素宽度的基础上,吃掉剩余空间。第一个元素本身就有宽度,剩余空间再三等分
解决方法是.part{flex-basis:0;//表示起跑线,默认值auto}
缩写就是flex :1 0(这个值是压缩) 0;
40、相等比较
左边转空字符,右边转false,然后转成数字
41、call和apply还能这样比较
像a.b.c.d.e.f()这种表达式,最后值肯定是f
函数.apply(参数1,[1,2])相当于参数1.函数(1,2)
本题转换为(a=>a).call(1,2),绑定this为1,传入参数2
所以运行不会有结果,外部加个console.log才输出2
改成这样还是2
42、形参数量的问题
输出2和1和1,为什么?
length表示这个函数他期望的最少的形参数量,不会包含剩余参数
43、是否会堆栈溢出?
每次调用会在栈里面加上个执行上下文,吞掉内存,函数执行完成才会被丢出栈,如果你在这个函数里面调用自身,就会导致还没有运行结束,又在调用自己,把栈堆满。
但是这个不会溢出是因为,调用自己是用异步操作的,异步操作不会导致栈溢出,扔到别的队列里去了
function foo() {setTimeout(foo(),0)
}//会溢出,时间到了,先执行foo(),把这个foo作为计时器到达后要执行的东西,所以溢出
44、如何触发迅雷下载?
45、动态属性值
const r1=add[1][2][3]+4//输出10
const r2=add[10][20]+30//输出60
const r3=add[100][200][300]+400//输出1000
柯里化,有参考下文
add 是对象,通过链式传入属性求和返回结果,咱们会想到代理proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
其中 target 是我们要代理的对象,handler 则是以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。详细介绍请看 MDN 文档。
let add = new Proxy({_store:0},{get(target, property, receiver) {target['_store'] += +propertyconsole.log(target['_store'])return receiver}});add[2][3][10]+3
运行到+3的时候,上面+p报错
其中 target 是目标对象,property 是被获取的属性名,receiver 是 Proxy 或者继承 Proxy 的对象。
let add = new Proxy({_store: 0},{get(target, property, receiver) {// console.log(property)//Symbol(Symbol.toPrimitive)// 遇到 + 的操作,会触发隐式类型转换if (property === Symbol.toPrimitive) {return () => {return target['_store']}} else {target['_store'] += +property// console.log(target['_store'])return receiver}}});console.log(add[2][3][10] + 3)
46、ESM符号绑定造成的神奇现象
只有导入这里会出现符号绑定,即两个东西是一块内存空间
会导致上述方法在文件一调用increase方法后,把变量莫名其妙改动了,所以解决方法是,文件二let改成const
这种情况用的也不是一块内存空间
47、CommonJs常见考点
require函数伪代码
第三步,五种参数,require方法主要写在这里,
这串代码放在目标模块代码这里
this、exports、module.exports三个值指向同一个东西
最后导出的是{d:4}
48、webpack配置
vite生成项目,装rollup-plugin-visualizer
其他生成项目,装webpack-bundle-analyzer
在vue.config.js里导入webpack配置
49、let和var的区别
1、污染全局
一旦var声明后,该变量将会挂载到全局window上,let不会
2、块级作用域
3、重复声明
另,前面用var后面用let也是会报错的
4、暂时性死区(TDZ)
let、const都会提升,
在翻阅MDN文档时,发现中文翻译,原文会砍半
let虽然提升了,但是做了个处理,在提升的第一行和声明语句前,形成了暂时性死区,
在暂时性死区内,是不能访问的
50、复制文本到剪切板
navigator.clipboard.writeText('哦豁')
navigator.clipboard对象只能在安全网络环境中才能使用,也就是localhost中,或者https中才能正常使用
直接在本地192.168获取不到该对象,显示undefined
更多推荐
近期分享学习心得1
发布评论