最近精神状态不太好,没心思写博客。前段时间听说了牛客网,看到上面全是面试题,虽然并不打算找工作,但是做题家的属性觉醒了,就每天做点题玩一玩。虽然我一直自诩“语言律师”,但是看到这些刁钻的面试题,还是有点应付不过来……
不过,今天已经把JS的技能值刷满了,稍微有点心得,想了想觉得还是写个博客,摘录一些我觉得比较有意思的题目。名义上是JS的题目,其实并不完全是,还是有一些别的领域的内容。
-
如果不给cookie设置过期时间会怎么样?
会在浏览器会话结束时过期。
事实上,我还真没试过……不过我倒是想到了
sessionStorage
。 -
下列代码存在几个变量没有被回收?
var i = 1; var i = 2; var add = function() { var i = 0; return function() { i++; console.log(i); } }(); add();
3个。为啥不是2个?别忘了
add
也是一个变量……当时看到这个我差点吐血。 -
以下代码的输出结果是:
var f = function g() { return 23; }; typeof g();
报错,没输出,因为
g is not defined
。那么函数表达式里的g是干啥的?用于指定函数的name
。函数表达式在这一块和函数声明还是有很大区别的。f.name // "g"
-
以下哪些选项可以将集合A转化为数组?
Array.from(A)
√[].slice.apply(A)
[...A]
√[].map.call(A, o => o)
看起来第二个和第四个也是对的,但是实际运行起来并不会生效,返回的是一个空数组。至于为什么会这样,因为
slice
和map
是基于目标的length
属性来进行处理的,Set
并没有length
属性(用的是size
),所以不会生效。同时,
map
还有一个tricky的地方,会跳过数组中的空当;也就是说,不会对数组中empty的地方做处理。这个后面会说。 -
以下表达式不会出现错误的有:
2.toString()
2..toString()
√2 .toString()
√(2).toString()
√
这其实也是一个老生常谈的话题。由于无法分辨数字后面紧跟的那个
.
到底是小数点还是方法调用,所以直接toString
是会报错的。想要不报错,就要让小数点和方法调用区分开。多加一个点、多加一个空格、加个括号都是可以的。 -
JS的全局函数。
这个我上次专门写了一篇文章,探讨这个问题。并且,我还是对全局函数这个概念本身存在疑义。
全局函数到底是全局作用域里的函数,还是仅仅指全局对象里的函数,似乎并不太明确。如果不严格地说,全局作用域里的函数都可以算是全局函数,这其实也更符合我们平时使用时的习惯。不过,如果从狭义上说,可能在全局作用域中预定义的函数才是全局函数。
-
什么操作会触发reflow(重排,或译为回流)?
与之并列的往往还有一个问题,什么操作会触发repaint(重绘)?
我个人理解,重排是在布局可能发生变化时触发的,重绘则是在不改变布局的样式发生变化时触发的。减少重排对于性能优化来说非常重要,我个人觉得这也是骨架屏的一个意义所在,提前渲染好占位元素可以减少异步数据到达时的重排损耗。
重排主要有这么几种情况吧,我觉得这篇文章写得挺好的,摘录如下:
- 改变窗囗大小
- 改变文字大小
- 添加/删除样式表
- 内容的改变,如用户在输入框中敲字
- 激活伪类,如:hover (IE里是一个兄弟结点的伪类被激活)
- 操作class属性
- 脚本操作DOM
- 计算offsetWidth和offsetHeight(以及scrollTop、scrollLeft、scrollWidth、offsetTop、offsetLeft等等属性,主要是需要获得当前最新的位置,所以必须触发一次重排)
- 设置style属性
-
如何判断一个js对象是否是Array, arr为要判断的对象?
Object.prototype.toString.call(arr) === '[object Array]';
。事实上一直是这么用的,但是为什么用
instanceof
不行,倒是值得商榷。其实正常情况下是可以的,但是在跨frame的情况下就不行了。比如这段代码(摘自这篇文章):var iframe = document.createElement('iframe'); document.body.appendChild(iframe); xArray = window.frames[window.frames.length-1].Array; var arr = new xArray("1","2","3","4","5"); alert(arr instanceof Array); // false alert(arr.constructor === Array); // false
因为跨frame了,所以constructor.prototype肯定不会出现在目标的原型链上,就无法正确判断了。
-
require的模块查找机制
这里先留个坑,后面搞明白了会仔细说这个事情,因为我自己也搞不太清楚,只用过CommonJS(而且用得也不多,Node没怎么用过,现在主要是用Java),后面就一直用ES6的exports和import了。
主要是这么一个顺序,先是内置模块,再是按路径依次查找
.js
.json
.node
,最后是node_modules/
里的模块,然后依次往上,直到找不到报错。 -
页面生命周期,摘自这篇文章:
- script executed
- readyState : interactive
- DOMContentLoaded
- image onload
- iframe onload
- readyState : complete
- window onload
同步的Script总是先于其它事件执行,而window.onload事件总是最后执行。而image onload和iframe onload的先后顺序并不确定。
-
能够冒泡与不能冒泡的事件,我觉得这个整理挺好的:
不过,如果可以现场写代码的话,倒是可以通过这个属性来判断一下:
event.bubbles
。 -
以下哪种js中字符串连接方式较为高效:
a+=b
a = a+b
Array.join()
Array.push()
这个问题其实并不太具有参考价值,因为现代浏览器已经基本上做了优化了,不太会有很大的差别。但是在早期的时候(IE时代),对字符串数组使用
join
是最高效的。我个人怀疑这个和C++的实现有关,因为
+
运算符返回的是新创建的对象,而+=
运算符是直接在后面添加,返回的是引用。X& operator+=(const X& rhs); X operator+(const X& rhs);