前言
大家好,我是HoMeTown,最近发现自己的小群也有一百多个兄弟了,寻思着为兄弟们做一些什么!让大家一起进步!
思来想去,俗话说得好:“不积跬步,无以至千里;不积小流,无以成江海
”,打算启动一个为期17天的日积月累计划
,开始每天分享一些前端相关的知识与见解(指北罢了),方向不局限于整个前端领域,有可能会包含一些面试技巧之类的。
时间的话,目前暂定2022-09-13 ~ 2022-09-30
,因为我本身也是一个一线码农,精力实在有限,所以先暂定一个短期的小目标,希望大家可以一起坚持下来!想加入的兄弟请查看下方目录 我要加入。
见解(指北):不代表官方正确答案,如有错误,请友善指出
日积月累
2022-09-13 🐳
《0.1 + 0.2 != 0.3?为什么?》
部分浮点数在十进制下是有限小数,但是在二进制下尾数是无限数,比如0.1、0.2 尾数0011无限循环。
由于计算精度有限,计算机底层根据IEEE 754舍入规则进行处理后存储了一个近似值(近似值的长度与精度有关),近似值0.3
与 值0.3
在二进制上表现不同,是不能用来比较的,因为近似值和任何数比较都可能返回false。
2022-09-14🐢
《js 闭包是什么,什么作用?》
闭包在Js中是一种特殊的机制,本质就是上级作用域内变量的生命周期,因为被下级作用域内引用,所以没有被释放。就导致上级作用域内的变量,等到下级作用域执行完以后才正常得到释放。
那么如果闭包过多,就会导致变量不能及时释放,被GC回收。
利用闭包的特性可以完成很多高级设计模式,比如单例模式、柯里化函数、组合函数、函数防抖等等~
《css rem是什么 自适应怎么写?》
关于rem
W3C对其的描述为font size of the root element
,即rem是相对于根元素,容易引起混淆的是em
单位(font size of the element),em是相对于父元素的字体大小的单位。
1rem
等于根元素 html
的 font-size(默认16px)
,rem自适应方案的本质 其实是通过使用 rem + js 改变 html 标签的 font-size(整体缩放)
来实现不同设备下的良好统一适配。
比较成熟的rem布局方案库:flexible
、postcss-pxtorem
。
思考:vm 与 rem适配的优势在哪?
2022-09-15 🦆
js 实现柯里化函数
柯里化是一种关于函数高阶技巧,主要是将多个参数的函数func(1,2,3)
转化成一系列使用一个参数func(1)(2)(3)
的技术,白话来讲,柯里化其实是将简单的问题复杂化,但是这种复杂化的处理方式给了我们更高的自由度!
function curry(func) { return function curried(...args) { // 入参长度是否等于或者大于原始args长度,相等则执行原始函数 if (args.length >= func.length) { return func.apply(this, args); } else { return function(...args2) { return curried.apply(this, args.concat(args2)); } } }; } function sum(a, b, c) { return a + b + c; } let curriedSum = curry(sum); alert( curriedSum(1, 2, 3) ); // 6,仍然可以被正常调用 alert( curriedSum(1)(2,3) ); // 6,对第一个参数的柯里化 alert( curriedSum(1)(2)(3) ); // 6,全柯里化 复制代码
js 实现防抖函数
防抖(debounce)函数可以确保你的代码在每个用户输入时只被触发一次。搜索框建议、文本字段自动保存和消除双键点击都是防抖的用例。
function debounce(func, timeout = 300){ // settimout 存储 let timer; return (...args) => { if (timer) clearTimeout(timer) timer = setTimeout(() => { func.apply(this, args); }, timeout); }; } function saveInput(){ console.log('Saving data'); } const processChange = debounce(() => saveInput()); 复制代码
<input type="text" onkeyup="processChange()" /> 复制代码
js 实现单例设计模式
限制类实例化次数只能一次,一个类只有一个实例,并提供一个访问它的全局访问点
function Single(address) { this.address = address; } // 原型扩展类的一个方法getAddress() Single.prototype.getAddress = function() { console.log(this.address) }; // 获取类的实例 Single.getInstance = (function() { var instance = null; return function(address) { if(!this.instance) { this.instance = new Singleton(address); } return this.instance } })(); // 地址1 var add1 = Single.getInstance('a'); // 地址2 var add2 = Single.getInstance('b'); console.log(add1 === add2); // true 复制代码
思考:通过👆3个经典的例子,找一找他们的共通点?你会发现,原来是这样!
2022-09-16🐺
事件循环原理
首先js是单线程的,这是因为作为浏览器脚本语言,js主要用途是与用户互动以及操作dom元素,这决定了它只能是单线程,多线程会带来很多复杂的问题,比如一个线程在appenddom,另一个线程在removedom,浏览器应该以哪个为准呢?如果在做一个线程管理多个线程,复杂度直接飙升。(毕竟网景公司当时只是为了用js搞一些简单的表单校验...)
但是不要把js和浏览器混为一谈,浏览器是多线程的,我们说js是单线程指的是浏览器分配给js负责解析执行js代码的线程是单线程的,浏览器本身是多线程,除了js引擎线程,还有gui渲染线程、事件触发线程、定时器触发线程、异步http请求线程等等
单线程的js按照代码顺序自上而下执行,但是js又可以异步,是不是感觉很冲突?其实不冲突,这是因为js有了事件循环的机制!
事件循环为了对任务队列中的任务调度更为精确,将任务拆分成了宏任务与微任务,执行顺序为
- 执行主执行栈 代码自上而下顺序执行(这里一般就是一段script脚本)
- 遇到宏任务(事件回调、setTimeout、setInterval)执行,产生微任务,进入第3步骤
- 执行微任务(Promise.then),如果产生了新的微任务,则继续执行第3步
- 当前宏任务以及微任务处理完毕,执行第2步
async await的原理
async 函数是什么?大家一般回答最多的就一句话: Generator 函数的语法糖。
async 函数的实现原理,其实就是将 Generator 函数和自动执行器,包装在一个函数里。(大家可以去github上看一下一个叫 co 的库,co是async await提案的垫脚石)看完co,你就都明白了。
co 的出现是因为 generator 函数不会自动执行,需要手动调用它的 next() 函数,co 的作用就是自动执行 generator 的 next() 函数,直到 done 的状态变成 true 为止。
async/await, generator, promise这三者的关联和区别?
promise
- 状态:pending(进行中)、resolved(成功)、rejected(失败)
- 无法取消Promise,一旦新建就会立即执行,无法中途取消
- 当处于Pending状态时,无法得知目前进展到那个阶段
- promise真正执行回调的时候,定义Promise那部分实际上已经走完了,所以Promise的报错堆栈上下文不太友好
generator
- ES6 引入,可以暂停和继续执行函数
- 可以当做一个迭代器(iterator)来用,也可以在内部存一些状态,成为一个状态机
- yield(关键字)表示暂停 yield表单式本身没有返回值或者说总是返回undefined
- next方法可以带一个参数,该参数就会被当做上一个yield的返回值
async
- async 对应的是 * 。
- await 对应的是 yield 。
- async/await 自动进行了 Generator 的流程控制(co!co!co!看完你就都明白了!)。
总结的来说:async/await
是ES7引入的终极异步编程解决方案,本质其实就是generator
+ promise
的语法糖,自带auto run的buff,不用引入其他任何库(que co),对于await
后面的类型无限制,可读性更好,容易理解。