作者:陈大鱼头
github: KRISACHAN
如果不希望职业生涯过早结束,持续学习对于开发者来说是必不可少的。
最近《前端面试之道》的作者为了让一些人能在这块地方记录自己学习到的内容而建立起了一个学习仓库。
仓库地址如下:
这些内容通常会是一个小点,可能并不足以写成一篇文章。但是这个知识点可能很多人也不知道,那么通过这种记录的方式让别人同样也学习到这个知识点就是一个很棒的事情了。
具体的知识点如下:
JS
键盘弹出后挡表单的解决方案
window.addEventListener('resize', function () { if ( document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA' || document.activeElement.getAttribute('contenteditable') == 'true' ) { window.setTimeout(function () { if ('scrollIntoView' in document.activeElement) { document.activeElement.scrollIntoView(); } else { // @ts-ignore document.activeElement.scrollIntoViewIfNeeded(); } }, 0); } })
图片加载相关
首先是实现图片懒加载
<ul> <li><img src="./img/default.png" data="./img/1.png" alt=""></li> <li><img src="./img/default.png" data="./img/2.png" alt=""></li> <li><img src="./img/default.png" data="./img/3.png" alt=""></li> <li><img src="./img/default.png" data="./img/4.png" alt=""></li> <li><img src="./img/default.png" data="./img/5.png" alt=""></li> <li><img src="./img/default.png" data="./img/6.png" alt=""></li> <li><img src="./img/default.png" data="./img/7.png" alt=""></li> <li><img src="./img/default.png" data="./img/8.png" alt=""></li> </ul> <script> let imgs = document.querySelectorAll('img') // 窗口可视区高度 let clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; // img 距离窗口可视区顶部的距离 imgs[i].getBoundingClientRect().top function lazyLoadImg () { for (let i = 0; i < imgs.length; i ++) { if((imgs[i].getBoundingClientRect().top + imgs[i].height)>=0&&imgs[i].getBoundingClientRect().top < clientHeight ){ imgs[i].src = imgs[i].getAttribute('data') } } } window.addEventListener('scroll', lazyLoadImg); </script>
但是这种方式会引起图片下载过程中闪白一下,可以通过 JS 预先加载图片解决。
同时上述的懒加载解决方案已经很老了,可以使用最新的 API Intersection_Observer 来做这件事,会更简单而且可控一些。
无loop生成指定长度的数组
const List1 = len =''.padEnd(len, ',').split('.') const List2 = len =[...new Array(len).keys()]
异步的 Promise的 then 方法的回调是何时被添加到microtasks queue中的?
今天刷博客的时候看到一个题:
const pro = new Promise((resolve, reject) ={ const pro1 = new Promise((resolve, reject) ={ setTimeout(() ={ resolve(3); }, 0); }); resolve(4); pro1.then((args) ={ console.log(args); }); }); pro.then((args) ={ console.log(args); });
很多人都知道这道题的输出结果是4,3;但是我对题主的这个问题产生了很大的疑问,因为个人并没有着手实现过符合promise A/A+规划的promise,所以每次做这种题都是凭着平时的使用经验,实际上内心虚得很,然后自己查阅了 spec:ECMAScript 2018 Language Specification 根据 spec,如果调用 then 时 promise 是 pending 状态,回调会进入 promise 的 [[PromiseFulfill/RejectReactions]] 列表里;否则会进入 PromiseJobs。
PromiseJob 以及 Job Queue 是 ES 中的说法,而 macroTask 和 microTask 是浏览器中的概念,包括 setTimeout 也是宿主环境提供的。因此输出 4 3 是 ECMAScript 和 浏览器两种规范共同约束的结果。
PromiseJob 对应浏览器中的一个 microTask。对于调用 then 时 promise 处于 pending 状态,回调函数进入到对应的 reactions 队列中。当该 promise 被 fulfill 或 reject 时,则 flush 对应的 reactions 队列 ,其中的每个 reaction 对应一个 PromiseJob 被按序 enqueue 到 Job Queue如果调用 then 时 promise 处于其他两个状态,JS 引擎就直接 enqueue 一个对应的 PromiseJob 到 Job Queue示例中的代码。
在浏览器中如下顺序:
0. current cycle of evevt loop start 1. Install Timer,Timer get enqueued 2. Resovle pro, because there is no fulfillReaction binding to pro, do nothing 3. call then() at pro1, because pro1 is pending, add fulfillReaction to pro1 4. call then() at pro, because pro is reolved,immediately enqueue a PromiseJob 5. current macroTask is finished 6. run all PromiseJobs(microTasks) in order, 7. console.log(4) 8. current cycle of event loop is finishedanother cycle starts 9. Timer Fires, and pro1 is resolved 10. at this time, pro1 hasfulfillReactions,enqueue every fulfillReaction as a PromiseJob in order 11. current macro Job is finished 12. run all PromiseJobs in order 13. console.log(3) 14. current cycle of event loop is finished
移动端打开指定App或者下载App
navToDownApp() { let u = navigator.userAgent if (/MicroMessenger/gi.test(u)) { // 如果是微信客户端打开,引导用户在浏览器中打开 alert('请在浏览器中打开') } if (u.indexOf('Android') -1 || u.indexOf('Linux') -1) { // Android if (this.openApp('en://startapp')) { this.openApp('en://startapp') // 通过Scheme协议打开指定APP } else { //跳转Android下载地址 } } else if (u.indexOf('iPhone') -1) { if (this.openApp('ios--scheme')) { this.openApp('ios--scheme') // 通过Scheme协议打开指定APP } else { // 跳转IOS下载地址 } } }, openApp(src) { // 通过iframe的方式试图打开APP,如果能正常打开,会直接切换到APP,并自动阻止a标签的默认行为 // 否则打开a标签的href链接 let ifr = document.createElement('iframe') ifr.src = src ifr.style.display = 'none' document.body.appendChild(ifr) window.setTimeout(function() { // 打开App后移出这个iframe document.body.removeChild(ifr) }, 2000) }
利用 a 标签解析 URL
function parseURL(url) { var a = document.createElement('a'); a.href = url; return { host: a.hostname, port: a.port, query: a.search, params: (function(){ var ret = {}, seg = a.search.replace(/^\?/,'').split('&'), len = seg.length, i = 0, s; for (;i<len;i++) { if (!seg[i]) { continue; } s = seg[i].split('='); ret[s[0]] = s[1]; } return ret; })(), hash: a.hash.replace('#','') }; }
数组去重
var array = [1, 2, 1, 1, '1']; function unique(array) { var obj = {}; return array.filter(function(item, index, array){ return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true) }) }
利用一个空的 Object 对象,我们把数组的值存成 Object 的 key 值,比如 Object[value1] = true,在判断另一个值的时候,如果 Object[value2]存在的话,就说明该值是重复的。
因为 1 和 '1' 是不同的,但是这种方法会判断为同一个值,这是因为对象的键值只能是字符串,所以我们可以使用 typeof item + item 拼成字符串作为 key 值来避免这个问题
JS 函数对象参数的_陷阱_
上周在实现某个弹层功能的时候,用到了rc-util
里的 contains
方法函数, 结果 code-review
的时候同事对该代码提出了疑问:
rc-util 源码仓库
export default function contains(root, n) { let node = n; while (node) { if (node === root) { return true; } node = node.parentNode; } return false; }
上述代码是 antd
内部抽象的一个工具方法,用来判断某个dom是否为另一个dom的祖先节点。
同事疑问的是 let node = n;
这段代码是不是多余的?
首先一开始的理解是 函数参数 n
是一个对象,一个dom
节点对象。 如果用 node
保存 n
的值,防止 node = node.parentNode
这段代码执行的时候,会改变传入的实参 n
对应的值。
毕竟以下的代码我们都很熟悉:
function contains(root, n) { if(n) { n.a = 3 } } const A = {a:1}; const B = {a:2}; contains(A,B) console.log(B) // {a:3}
即当实参为对象时,函数内部是可以改变该对象的值从而影响函数之外的实参。
但是测试另外一段代码,发现和理解的不一样:
function contains(root, n) { if(n) { n = {a:3} } } const A = {a:1}; const B = {a:2} contains(A,B) console.log(B) // {a:2}
即 n.a = 3
和 n = {a:3}
这两段代码是不一样的。
网上也有相关资料,其实可以简单的理解为: 当函数一开始执行时,n
是指向实参 B
的一个引用.
n.a = 3
是在引用上再关联了一个属性,此时和 B
还是同一个引用,因此会改变实参B
的值。
而 n = {a:3}
则使得 n
不再指向实参 B
, 而是指向一个新对象{a:3}
,也就是 n
与 B
彻底断绝了关系,因此不会改变实参 B
的值。
是不是可以给蚂蚁的团队提个issue建议删除该代码,不过有这句代码也不会有什么bug~
相关资料:JavaScript深入之参数按值传递
其他
kill 指定端口
以下命令可以 kill 掉 8080 端口,当然你也可以选择通过 npm 命令的方式指定需要 kill 的端口。
lsof -i tcp:8080 | grep LISTEN | awk '{print $2}'| awk -F"/" '{ print $1 }' | xargs kill -9 复制代码
另外以上命令在 windows 上是不可用的。如果有多平台的需求的话,可以直接使用 Kill-port-process。
Linux下通过命令行替换文本
# 将wxml文件的i标签替换为text grep '<i ' -rl . --include='*.wxml' --exclude-dir=node_module --exclude-dir=dist | xargs sed -i -e 's/<i /<text /g' grep '</i>' -rl . --include='*.wxml' --exclude-dir=node_module --exclude-dir=dist | xargs sed -i -e 's/<\/i>/<\/text>/g'
如何判断文件中的换行符是 LF(\n) 还是 CRLF(\r\n)
文章链接,通过这篇文章可以了解到换行符到底是什么。
另外这位大佬每天都将学习到的知识记录了下来,感兴趣的可以 阅读一下。
总结
工作中建立起来的一些心得
要根据任务四象限划分好每天要做的事,按紧急度去完成任务(重要紧急,重要不紧急,紧急不重要,不重要不紧急);
学会自我总结(写周报或日志汇总所遇到的问题,时刻翻阅);
拥有快速定位并解决问题的能力(通过已知的条件去判断所遇到的问题);
要多与上下游沟通,了解团队大家所做的事以及进度(方便自己合理安排任务);
积极乐观不抱怨(最好可以时常给团队带来正能量,而不是满腹抱怨);
多运动,多休息(身体革命的本钱,没有一个好的身体就什么都做不了)。