乘兴裸辞心甚爽,面试工作屡遭难。
幸得每日一题伴,点击关注莫偷懒。
又要到金九银十的跳槽季了,为了让更多的小伙伴能够在面试的时候取的更好的offer
,所以自上月起我每天都会在本人的公众号【前端有的玩】外面推送一到两道面试题,不便找工作的小伙伴每日都会有新的播种。本文就是小编将后期的一些比拟经典的每日一题进行了梳理,欢送大家一起来看看。本文内容首发于公众号【前端有的玩】,关注 ===
学会。
类数组面试题
什么是类数组,类数组就是 领有length
属性,且其余属性(索引)为非负整数的对象,且不具备数组所用于的办法。比方咱们罕用的document.querySelector
返回的NodeLists
就是一个类数组。这道题就是和类数组相干的内容.
题目
请说出以下代码输入的内容,须要辨别nodejs
,chrome
以及chrome
去掉splice
之后的输入内容
var obj = { '2': 3, '3': 4, 'length': 2, 'splice': Array.prototype.splice, 'push': Array.prototype.push}obj.push(1)obj.push(2)console.log(obj)
答案
这道题一共问了三种状况上面的输入,上面顺次阐明答案
node
上面输入{ '2': 1, '3': 2, length: 4, splice: [Function: splice], push: [Function: push] }
chrome
上面输入[empty × 2, 1, 2, splice: ƒ, push: ƒ]
chrome
去掉splice
上面输入{2: 1, 3: 2, length: 4, push: ƒ}
通过下面输入的内容,能够看出雷同的代码,不同状况输入的内容是有所不同的,上面进行具体合成。
题解
在解答题目之前,咱们再看看这段代码
const arr = new Array(2)// 输入 2 [empty * 2]console.log(arr.length, arr)arr.push(1)// 输入 3 [empty * 2, 1]console.log(arr.length, arr)
能够看到push
办法会将数组的length + 1
, 而后将值放在索引为length - 1
的地位,比方下面的代码,因为在初始化数组的时候,曾经将数组长度指定为了2
, 所以在push
之后length
就变成了3
,而后arr[3 - 1] = 1
在MDN
上面对push
的办法的解释是:
push
办法具备通用性。该办法和call()
或apply()
一起应用时,可利用在相似数组的对象上。push
办法依据length
属性来决定从哪里开始插入给定的值。如果length
不能被转成一个数值,则插入的元素索引为 0,包含length
不存在时。当length
不存在时,将会创立它。
依据MDN
解释,push
既能够应用到数组中,也能够应用到类数组中。而依据前文中对类数组的解释,能够看到题目中的obj
就是一个规范的类数组,那就能够在obj
下面应用数组的push
办法。
再看obj.push(1)
, 因为obj.length = 2
, 所以会将length + 1
就变成了3
, 这时候 索引值时obj[3 - 1] = 1
即obj[2] = 1
, 同理 obj.push(2)
也一样的。因为在obj
中曾经有了属性(索引)2
和3
,所以在push
的时候会笼罩掉2
和3
下面的默认值。
所以在nodejs
中就会输入
{ '2': 1, '3': 2, length: 4, splice: [Function: splice], push: [Function: push] }
然而在chrome
控制台中输入
[empty × 2, 1, 2, splice: ƒ, push: ƒ]
很奇怪,为什么会输入这样呢?这一块有一个很非凡的陷阱,就是chrome
控制台是如何判断打印的内容是数组还是其余对象呢?对于这个,chrome
就是通过判断对象下面是否有splice
和length
这两个属性来判断的,所以如果你将splice
去掉之后,就会输入以下内容
{2: 1, 3: 2, length: 4, push: ƒ}
你也能够试试上面的代码:
console.log({splice:function(){},length:1})console.log({slice:function(){},length:1})
逻辑面试题之小鼠喝毒药
小编当年毕业的时候面试就遇到过好几次逻辑类的面试题,这道题就是一道逻辑类的面试题,一起来看看。
题目
有16瓶水,其中只有一瓶水有毒,小白鼠喝一滴之后一小时会死,请问起码用多少只小白鼠,在1小时内肯定能够找出有毒的水?
答案与题解
答案是至多须要4
只小鼠,怎么了解呢?咱们能够用二进制去推理一下:
假如有4只小鼠,别离是甲乙丙丁,应用二进制来示意小鼠喝药的程序,1
代表喝药,0
代表不喝药
甲: 1111 1111 0000 0000
乙: 1111 0000 1111 0000
丙: 1100 1100 1100 1100
丁: 1010 1010 1010 1010
那么咱们就能够这样去判断:
- 甲乙丙丁都死了,阐明第一瓶有毒
- 甲乙丙死了,阐明第二瓶有毒
- 甲乙丁死了,阐明第三瓶有毒
- 甲乙死了,阐明第四瓶有毒
- 甲丙丁死了,阐明第五瓶有毒
- 。。。 顺次类推
其实对于这道题,能够应用2
的n
次方来判断,比方有32
瓶水,那么就是2
的5
次方,所以就须要5
只小鼠。
arguments 面试题
在ES6
中,咱们如果一个函数参数个数不确定,咱们个别会应用扩大运算符即function(...rest){}
,失去一个参数数组rest
,然而在ES6
之前,咱们是不能应用扩大运算符的,这时候就须要思考应用arguments
题目
请说出以下程序输入的内容(chrome输入内容)
let obj = { arg: 18, foo: function(func) { func() arguments[0]() }}var age = 10function fn() { console.log(this.age)}obj.foo(fn)
答案
本题的答案是:
// 第一个输入 10func()// 第一个输入 undefinedarguments[0]()
有点出其不意了吗?
先来解释一下第一个,为什么不是输入18
呢,尽管func()
是在foo
函数外面调用的,然而并没有显式指明作用域,这时候会应用默认作用域window
,而对于浏览器来说,在全局通过var
申明的变量会主动挂载到window
下面,所以var age = 10
相当于window.age = 10
, 而第一个func()
外面的this.age
相当于window.age
第二个可能许多人有点蒙,为啥是undefined
,先看一下上面的代码
const arr = [function() {console.log(this[1])}, '我是子君']// 输入 我是子君console.log(arr[0]())
咱们通过arr[0]
获取到函数,这时候函数的作用域就是这个数组,所以再调用的时候,this
就是arr
, 所以this[1]
就是数组第二项。
这时候回过头来看arguments
,这个其实是一个类数组,外面存的是函数传入的参数,第一项就是传入的函数,和下面例子一样,arguments[0]
的作用域就是arguments
,而arguments
下面并没有age
属性,所以是undefined
this指向问题
this
指向问题始终是比拟凌乱的,在箭头函数呈现之前,this
的指向与代码在哪里定义并没有关系,而是取决于是被谁执行的,正因为此,所以许多开发人员常常会搞不清楚this
到底是谁。上面的两道题都是和this
指向相干的问题。
题目一(青铜)
请说出以下代码输入的内容
let num = 1;let obj = { num: 2, add: function() { this.num = 3; (function() { console.log(this.num); this.num = 4; })(); console.log(this.num); }, sub: function() { console.log(this.num) }}obj.add();console.log(obj.num);console.log(num);const sub = obj.sub;sub();
题目二(黄金)
请说出以下代码输入的内容
var num = 10const obj = {num: 20}obj.fn = (function (num) { this.num = num * 3 num++ return function (n) { this.num += n num++ console.log(num) }})(obj.num)var fn = obj.fnfn(5)obj.fn(10)console.log(num, obj.num)
答案
题目一
输入后果: 1
,3
,3
,4
,4
, 你答对了吗?上面咱们来看看代码解析
var num = 1;let obj = { num: 2, add: function() { this.num = 3; // 这里的立刻指向函数,因为咱们没有手动去指定它的this指向,所以都会指向window (function() { // 所有这个 this.num 就等于 window.num console.log(this.num); this.num = 4; })(); console.log(this.num); }, sub: function() { console.log(this.num) }}// 上面逐行阐明打印的内容/** * 在通过obj.add 调用add 函数时,函数的this指向的是obj,这时候第一个this.num=3 * 相当于 obj.num = 3 然而外面的立刻指向函数this仍然是window, * 所以 立刻执行函数外面console.log(this.num)输入1,同时 window.num = 4 *立刻执行函数之后,再输入`this.num`,这时候`this`是`obj`,所以输入3 */obj.add() // 输入 1 3// 通过下面`obj.add`的执行,obj.name 曾经变成了3console.log(obj.num) // 输入3// 这个num是 window.numconsole.log(num) // 输入4// 如果将obj.sub 赋值给一个新的变量,那么这个函数的作用域将会变成新变量的作用域const sub = obj.sub// 作用域变成了window window.num 是 4sub() // 输入4
题目二
输入后果为: 22
23
65
30
, 你答对了吗? 上面咱们解析一下
var num = 10const obj = {num: 20}obj.fn = (function (num) { this.num = num * 3 num++ return function (n) { this.num += n num++ console.log(num) }})(obj.num)var fn = obj.fnfn(5)obj.fn(10)console.log(num, obj.num)
咱们把下面的代码分为以下几步进行剖析
- 先看第三行代码,是一个赋值操作,咱们晓得赋值操作是从右向左的,而
=
号左边是一个立刻执行函数,所以会优先执行立刻执行函数,立刻执行函数没有手动指定this
,这时候this = window
,而立刻函数的参数num
是传进来的obj.num
,所以num
参数默认值是20
- 第四行相当于
window.num = 20 * 3
- 第五行为传入的参数加一,所以
num = 20 + 1
第六行
return
了一个函数,而这个函数就是obj.fn
的值, 然而因为return
的函数援用了立刻执行函数外面的num
,所以造成了闭包。这时候obj.fn = function(n) { this.num += n // 这个num是立刻执行函数外面的num num++ console.log(num)}
var fn = obj.fn
, 将obj.fn
赋值给新的变量,而这个变量的作用域是window
- 在调用
fn(5)
的时候, 在第二步,window.num
的值曾经变成了60
, 而后因为这时候fn
的this
是window
,this.num += n
相当于window.num += n
, 即window.num = 65
num++
, 因为闭包的起因,第三步num
是21
,所以这一步num
变成了22
, 同时输入22
- 而后
obj.fn(10)
,这时候fn
的this
为obj
,obj.num
默认值是20
,this.num += n
相当于obj.num += 10
- 和第七步一样,
num + 1
输入23
console.log(num, obj.num)
相当于console.log(window.num, obj.num)
,从下面几步可知,window.num = 65
,obj.num = 30
。
扩大题
如果将下面两道题的 var
改成 let
, 又会输入什么后果呢?
数据类型转换问题
尽管在日常开发中,咱们隐氏类型转换用的比拟少(不肯定),然而这个还是面试常问问题,把握还是要把握的,一起来看看这道题目吧.
题目(王炸/青铜,我也不晓得)
请说出以下代码输入的内容
console.log([] + [])console.log({} + [])console.log([] == ![])console.log(true + false)
答案
一起来看看答案吧
- 第一行代码
// 输入 "" 空字符串console.log([] + [])
这行代码输入的是空字符串""
, 包装类型在运算的时候,会先调用valueOf
办法,如果valueOf
返回的还是包装类型,那么再调用toString
办法
// 还是 数组const val = [].valueOf()// 数组 toString 默认会将数组各项应用逗号 "," 隔开, 比方 [1,2,3].toSting 变成了"1,2,3",空数组 toString 就是空字符串const val1 = val.toString() // val1 是空字符串
所以下面的代码相当于
console.log("" + "")
第二行代码
// 输入 "[object Object]"console.log({} + [])
和第一题情理一样,对象
{}
隐氏转换成了[object Object]
,而后与""
相加第三行代码
// 输入 trueconsole.log([] == ![])
对于
===
, 会严格比拟两者的值,然而对于==
就不一样了- 比方
null == undefined
- 如果非
number
与number
比拟,会将其转换为number
- 如果比拟的单方中由一方是
boolean
,那么会先将boolean
转换为number
- 比方
所以对于下面的代码,看上面一步一步剖析
// 这个输入 falseconsole.log(![])// 套用下面第三条 将 false 转换为 数值// 这个输入 0console.log(Number(false))// 包装类型与 根本类型 == 先将包装类型通过 valueOf toString 转换为根本类型 // 输入 ""console.log([].toString())// 套用第2条, 将空字符串转换为数值、// 输入 0console.log(Number(""))// 所以console.log(0 == 0)
第四行代码
// 输入 1console.log(true + false)
两个根本类型相加,如果其中一方是字符,则将其余的转换为字符相加,否则将类型转换为
Number
,而后相加,Number(true)
是1
,Number(false)
是0
, 所以后果是1
总结
面试造火箭,工作拧螺丝。尽管我只想拧螺丝,然而我却须要通过造火箭来找到拧螺丝的工作,每日一题,每天都有新的面试题目,欢送关注公众号【前端有的玩】,拉你进入前端技术交换群,每日一题等着你来一起答题。
结语
不要吹灭你的灵感和你的想象力; 不要成为你的模型的奴隶。 ——文森特・梵高