③ 对返回值进行按位取反(所有正整数的按位取反是其本身+1的负数,所有负整数的按位取反是其本身+1的绝对值,零的按位取反是 -1。其中,按位取反也会对返回值进行强制转换,将字符串5转化为数字5,然后再按位取反。
false被转化为0,true会被转化为1。
其他非数字或不能转化为数字类型的返回值,统一当做0处理)
const visibleIndex = newSet(); //全局创建一个不重复的集合 let ob = newIntersectionObserver((entries) => { for (const e of entries) { const index = e.target.dataset.index; //isIntersecting为true就代表在视口内 if (e.isIntersecting) {
visibleIndex.add(index);
} else {
visibleIndex.delete(index);
}
} debounceLoadPage();// 防抖后的loadpage
});
3.获取集合的最大及最小的索引
functiongetRange() { if (visibleIndex.size === 0) return [0, 0]; const max = Math.max(...visibleIndex); const min = Math.min(...visibleIndex); return [min, max];
}
4.加载视口内的元素的资源
functionloadPage() { // 得到当前能看到的元素索引范围 const [minIndex, maxIndex] = getRange(); const pages = newSet(); // 不重复的页码集合 for (let i = minIndex; i <= maxIndex; i++) {
pages.add(getPage(i, SIZE));// 遍历将侦查器集合范围内的所在页面都加入到pages的集合内
} // 遍历页码集合 for (const page of pages) { const [minIndex, maxIndex] = getIndexRange(page, SIZE);//获取页码的索引范围 if (contain.children[minIndex].dataset.loaded) { //如果页码最小索引的元素有自定义属性就跳过,代表加载过 continue;
}
contain.children[minIndex].dataset.loaded = true;//如果没有就代表没有加载过,添加上自定义属性 //将当前页码传给获取资源的函数 getVideo(page, SIZE).then((res) => { //拿到当前页面需要的数据数组,遍历渲染到页面上 for (let i = minIndex; i < maxIndex; i++) { const item = contain.children[i];
item.innerHTML = `<img src="${res[i - minIndex].cover}" alt="">`;
}
});
}
}
<body> <divclass="contain"></div> <divclass="btn"> <buttonclass="full-rounded"> <span>刚刚看过</span> <divclass="border full-rounded"></div> </button> </div> <scriptsrc="./api.js"></script> <scriptsrc="./index.js"></script> <script> constSIZE = 15; let contain = document.querySelector(".contain"); let btn = document.querySelector(".btn"); // 页码 let i = 1;
const visibleIndex = newSet();
// 视口观察器 let ob = newIntersectionObserver((entries) => { for (const e of entries) { const index = e.target.dataset.index; if (e.isIntersecting) { // 将在视口内的元素添加到集合内
visibleIndex.add(index);
} else { // 将不在视口内的元素从集合内删除
visibleIndex.delete(index);
}
} debounceLoadPage();
});
functiongetRange() { if (visibleIndex.size === 0) return [0, 0]; const max = Math.max(...visibleIndex); const min = Math.min(...visibleIndex); return [min, max];
}
第一次先点击快照记录初始的内存情况,然后我们多次点击按钮后再次点击快照,记录此时的内存情况,发现从原来的1.1M内存空间变成了1.4M内存空间,然后我们选中第二条快照记录,可以看到右上角有个All objects的字段,其表示展示的是当前选中的快照记录所有对象的分配情况,而我们想要知道的是第二条快照与第一条快照的区别在哪,所以选择Object allocated between Snapshot1 and Snapshot2即展示第一条快照和第二条快照存在差异的内存对象分配情况,此时可以看到Array的百分比很高,初步可以判断是该变量存在问题,点击查看详情后就能查看到该变量对应的具体数据了
let left = startPos.current.left; let top = startPos.current.top; const width = Math.abs(e.clientX - startPos.current.left); const height = Math.abs(e.clientY - startPos.current.top);
// 当后面位置小于前面位置的时候,需要把框的坐标设置为后面的位置 if (e.clientX < startPos.current.left) {
left = e.clientX;
}
if (e.clientY < startPos.current.top) {
top = e.clientY;
}
/**
* Union type with pipe operator
* @typedef {Date | string | number} MixDate
*/
/**
* @param {MixDate} date
* @returns {void}
*/ functionshowDate(date) { // date is Date if (date instanceofDate) date; // date is string elseif (typeof date === 'string') date; // date is number else date;
}
/**
* Restrict template by types
* @template {string|number|symbol} T
* @templateY
* @param {T} key
* @param {Y} value
* @returns {{ [K in T]: Y }}
* @example signature:
* function toObject<T extends string | number | symbol, Y>(key: T, value: Y): { [K in T]: Y; }
*/ functiontoObject(key, value) { return { [key]: value };
}
类型守卫:
/**
* @param {any} value
* @return {value is YOUR_TYPE}
*/ functionisYourType(value) { let isType; /**
* Do some kind of logical testing here
* - Always return a boolean
*/ return isType;
}
笔试过程最后中出现了一点小插曲,因为笔试有时间限制,需要在规定的时间内完成,但是倒计时还没结束,不知道为什么就自动交卷了,不过那个时候已经写的差不多了,功能全部实现了,还剩下卡片的样式没完成,css 还需要完善一下,于是就在 Boss 上跟对方解释了一下,说明了情况。
过了几分钟,对面直接回复笔试过了,然后约了面试。
一面:
自我介绍
这里大概说了两分钟,介绍了过往工作经历,做过的业务以及技术栈。
七层网络模型、和 DNS 啥的
计网这方面属于知识盲区了,听到这个问题两眼一黑,思索了一会儿,直接说回答不上来。
然后问了一些 host 相关的东西
很遗憾也没回答上来,尴尬。对方问我是不是计算机专业的,我坦诚的告诉对方是建筑工程。
React 代码层的优化可以说一下么?
大概说了 class 组件和 function 组件两种情况,核心是通过减少渲染次数达到优化目的,具体的优化手段有 PureComponent、shouldComponentUpdate、React.memo、React.useMemo、React.useCallback、React.useRef 等等。
此处,onClick 有一个闭包,它保留了 element 的引用(通过 element.nodeName)。通过同时将 onClick 赋值给 element.click,就创建了循环引用,即 element -> onClick -> element -> onClick -> element ......
有趣的是,即使 element 从 DOM 中删除,上述循环自引用也会阻止 element 和 onClick 被回收,从而造成内存泄漏。
一个常见的示例是一次添加一个 DOM 元素的代码。添加 DOM 元素是一项代价昂贵的操作,连续添加多个 DOM 元素的代码效率低下,并且可能无法正常工作。
当需要添加多个 DOM 元素时,一个有效的替代方案是改用文档片段(document fragments),这能提高效率和性能。
举个栗子:
const div = document.getElementById('my_div') const fragment = document.createDocumentFragment() const elems = document.querySelectorAll('a')
for (let e = 0; e < elems.length; e++) {
fragment.appendChild(elems[e])
}
div.appendChild(fragment.cloneNode(true))
除了这种方法固有的提高效率之外,创建附加的 DOM 元素代价昂贵,而在分离时创建和修改它们,然后附加它们会产生更好的性能。
问题 6:在 for 循环中错误使用函数定义
请瞄一眼下述代码:
var elements = document.getElementsByTagName('input') var n = elements.length// 我们假设本例有 10 个元素 for (var i = 0; i < n; i++) {
elements[i].onclick = function () { console.log('This is element #' + i)
}
}
根据上述代码,如果有 10 个输入元素,单击其中任何一个都会显示“This is element #10”!这是因为,在为任何元素调用 onclick 时,上述 for 循环将完成,并且 i 的值已经是 10(对于所有元素)。
以下是我们如何纠正此问题,实现所需的行为:
var elements = document.getElementsByTagName('input') var n = elements.length// 我们假设本例有 10 个元素 var makeHandler = function (num) { // 外部函数 returnfunction () { // 内部函数 console.log('This is element #' + num)
}
} for (var i = 0; i < n; i++) {
elements[i].onclick = makeHandler(i + 1)
}
在这个修订版代码中,每次我们通过循环时,makeHandler 都会立即执行,每次都会接收当时 i + 1 的值并将其绑定到作用域的 num 变量。外部函数返回内部函数(也使用此作用域的 num 变量),元素的 onclick 会设置为该内部函数。这确保每个 onclick 接收和使用正确的 i 值(通过作用域的 num 变量)。
问题 7:误用原型式继承
令人惊讶的是,一大坨 JS 爱好者无法完全理解和充分利用原型式继承的特性。
下面是一个简单的示例:
BaseObject = function (name) { if (typeof name !== 'undefined') { this.name = name
} else { this.name = 'default'
}
}
这似乎一目了然。如果您提供一个名称,请使用该名称,否则将名称设置为“default”。举个栗子:
var firstObj = newBaseObject() var secondObj = newBaseObject('unique')
if (this.options.buildModules && !this.options._start) { // Load every devModule in sequence awaitsequence(this.options.buildModules, this.addModule)
}
// Load every module in sequence awaitsequence(this.options.modules, this.addModule)
// Load ah-hoc modules last awaitsequence(this.options._modules, this.addModule)
# ? What type of extension do you want to create? NewExtension (TypeScript) # ? What's the name of your extension? HelloWorld ### Press to choose default for all options below ###
# ? What's the identifier of your extension? helloworld # ? What's the description of your extension? LEAVE BLANK # ? Enable stricter TypeScript checking in 'tsconfig.json'? Yes # ? Setup linting using 'tslint'? Yes # ? Initialize a git repository? Yes # ? Which package manager to use? npm
对于不支持模板递归的小程序(例如微信、QQ、京东小程序等),当 DOM 层级达到一定数量后,Taro 会利用原生自定义组件来辅助递归渲染。简单来说,当 DOM 结构超过 N 层时,Taro 将使用原生自定义组件进行渲染(可以通过修改配置项 baseLevel 来调整 N 的值,建议设置为 8 或 4)。