js题目

Array.prototype.slice(beignIndex, beginIndex+amount)是一个纯函数; Array.prototype.splice(beginIndex, amount)不是一个纯函数;Array.prototype.filter()是一个纯函数。Array.prototype.sort不是一个纯函数

1.如何遍历一个DOM树

1
2
3
4
5
6
7
8
9
10
11
12
13
function traversalDom (node) {
if (!node || node.nodeType != 1) return; // 非节点以及非元素节点直接返回
if (node && node.nodeType == 1) {
console.log('node tag is', node.tagName);
}
for (let i = 0, len = node.childNodes.length; i < len; i++) {
let ele = node.childNodes[i];
// 只遍历元素节点
if (ele.nodeType == 1) {
traversalDom(ele);
}
}
}

2.求两个数组的交集,比如给定nums1 = [1, 2, 2, 1],nums2 = [2, 2],返回 [2, 2]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/** 随机生成数组的方法 */
let randomArr = (length, min, max) => {
let arr = [];
for (let i = 0; i < length; i++) {
let item = Math.random() * (max-min+1) + min;
arr.push(parseInt(item));
}
return arr;
}

/** 计算交集 */
let computeSame = (arr1, arr2) => {
if (!arr1 || !arr2 || !arr1.length || !arr2.length) return;
let compare = arr1.length > arr2.length;
let shortArr = compare ? arr2 : arr1;
let longArr = compare ? arr1 : arr2;
let result = [];
let map = new Map();
for (let i = 0, len = shortArr.length; i < len; i++) {
let n = shortArr[i];
if (map.has(n)) {
result.push(n);
} else if (longArr.includes(n)) {
result.push(n);
map.set(n, 'faster');
}
}
return result;
}

let arr1 = randomArr(1000000, 1, 300);
let arr2 = randomArr(100000, 1, 300);
console.time('computeSame');
let result = computeSame(arr1, arr2);
console.timeEnd('computeSame');

优化手段:遍历小数组;使用Map避免过多的查询数组是否包含某个元素;使用for循环代替map,在数据量很多的时候,使用for循环会比map更加的快。利用上诉代码在Mac上面运行基本耗时在8ms左右。

3.节流和防抖有什么区别?以及如何实现?

首先,这两种措施都是用来解决某些行为被高频触发的问题,这两种方式的不同点在于节流是规定每隔多少时间运行一次(不在取决于用户的某些高频行为),而对于防抖来说则是在高频被触发的场合下,只有最后一次触发会产生有效动作。按理说,防抖和节流都是使用在被频繁触发的行为之间间隔时间会小于你所设置的监听时间。如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/** 函数防抖包装方法 */
function debounce(fn) {
let timeout = null;
return function () {
clearTimeout(timeout); // timeout被闭包给使用了,也就是说每个cb所使用的都是同一个timeout句柄。
timeout = setTimeout(() => {
fn && fn.apply(this, arguments);
}, 2000);
}
}

function sayHi () {
console.log('是否防抖节流成功');
}

/** 函数节流包装方法 */
function throttle(fn) {
let canRun = true;
return function () {
if (!canRun) return; // 同样canRun也是通过闭包的形式被循环使用
canRun = false;
setTimeout(function () {
fn && fn.apply(this, arguments);
canRun = true;
}, 2000);
}
}

let ele = document.querySelector('.input');
ele.addEventListener('input', debounce(sayHi));

从上面的代码我们可以看出,对于防抖来说核心就是清除定时器,让你连执行的机会都没有;而对于节流来说,则是设置一个执行开关,条件满足了,执行开关打开,执行回调,否则关闭。

4.如何获取页面域名信息等数据?

window.location.href可用用来获取页面链接;window.location.pathname用来获取当前页面的路由名;window.location.search用于获取query数据。

5.DOMContentLoaded和load有什么区别?

DOMContentLoaded在完成HTML Parser阶段被触发;onLoad事件则是在所有资源被成功加载完的时候触发。一般的,DOMContentLoaded都会先与onLoad事件被触发,但是也有很多情况下,DOMContentLoaded会比onLoad更加的慢一点。但是有趣的是,哪怕在火焰图中先看到onLoad事件被触发,之后在触发DOMContentLoaded事件,DOMContentLoaded的回调方法也会比onLoad的回调方法更早调用。一个问题,我们在之前关于关键渲染路径的文章里面看到过js的执行会阻塞页面渲染流程,然后只有当整棵DOM树被解析出来后才有可能进入接下来的paint阶段,那么将script标签放在最后面又有什么好处呢?因为浏览器有一个first paint流程,这个first paint流程并不一定要求需要完整的dom和CSSOM,也就是说构建出来的DOM越多的话,那么体验将会越好,而将script放在head中的话显然会减少所构建出来的DOM。

6.为什么for会比forEach,map更快?

for循环具有更少的函数调用栈,上下文切换时间花费更少;forEach和map引入了额外操作拖慢性能。

7.数组去重

1.使用filter

1
2
3
4
function fun(arr: Array<number>): Array<number> {
let result: Array<number> = arr.filter((item, index, array) => array.indexOf(item) === index);
return result;
}

2.使用includes或者indexOf

1
2
3
4
5
6
7
8
9
10
function fun(arr: number[]): number[] {
let result: number[] = [];
for (let i = 0, len = arr.length; i < len; i++) {
let item = arr[i];
if (!result.includes(item)) { // 也可以换成indexOf
result.push(item);
}
}
return result;
}

3.使用set

1
2
3
4
5
function fun(arr: Array<number>): Array<number> {
let set: Set<number> = new Set(arr);
let result: Array<number> = Array.from(set);
return set;
}

4.排序后对比前后项

1
2
3
4
5
6
7
8
9
10
11
12
function fun(arr: Array<number>): Array<number> {
let result: Array<number> = [];
let prev: number = 0;
arr.sort((a, b) => a-b); // 从小到大进行排序
for (let i = 0, len = arr.length; i < len; i++) {
if (i === 0 || arr[i] !== prev) {
result.push(arr[i]);
}
prev = arr[i];
}
return result;
}

8.类型判断

不用多说,Object.prototype.toString.call()很强大。在背个书,typeof null === “object”。

9.怎么判断一个值是null还是undefined?

假设变量名为a,如果a == null的结果为true的话,那么就说明a要么是null要么是undefined。