关于javascript:当裸辞遇到面试难这些面试题你需要了解一下

32次阅读

共计 6321 个字符,预计需要花费 16 分钟才能阅读完成。

乘兴裸辞心甚爽,面试工作屡遭难。
幸得每日一题伴,点击关注莫偷懒。

又要到金九银十的跳槽季了,为了让更多的小伙伴能够在面试的时候取的更好的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)

答案

这道题一共问了三种状况上面的输入,上面顺次阐明答案

  1. node上面输入

    { '2': 1,
      '3': 2,
      length: 4,
      splice: [Function: splice],
      push: [Function: push] }
  2. chrome上面输入

    [empty × 2, 1, 2, splice: ƒ, push: ƒ]
  3. 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] = 1obj[2] = 1, 同理 obj.push(2) 也一样的。因为在obj 中曾经有了属性 (索引)23,所以在 push 的时候会笼罩掉 23下面的默认值。

所以在 nodejs 中就会输入

{ '2': 1,
  '3': 2,
  length: 4,
  splice: [Function: splice],
  push: [Function: push] }

然而在 chrome 控制台中输入

[empty × 2, 1, 2, splice: ƒ, push: ƒ]

很奇怪,为什么会输入这样呢?这一块有一个很非凡的陷阱,就是 chrome 控制台是如何判断打印的内容是数组还是其余对象呢?对于这个,chrome就是通过判断对象下面是否有 splicelength这两个属性来判断的,所以如果你将 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

那么咱们就能够这样去判断:

  1. 甲乙丙丁都死了,阐明第一瓶有毒
  2. 甲乙丙死了,阐明第二瓶有毒
  3. 甲乙丁死了,阐明第三瓶有毒
  4. 甲乙死了,阐明第四瓶有毒
  5. 甲丙丁死了,阐明第五瓶有毒
  6. 。。。顺次类推

其实对于这道题,能够应用 2n次方来判断,比方有 32 瓶水,那么就是 25次方,所以就须要 5 只小鼠。

arguments 面试题

ES6 中,咱们如果一个函数参数个数不确定,咱们个别会应用扩大运算符即 function(...rest){}, 失去一个参数数组rest,然而在ES6 之前,咱们是不能应用扩大运算符的,这时候就须要思考应用arguments

题目

请说出以下程序输入的内容(chrome 输入内容)

let obj = {
  arg: 18,
  foo: function(func) {func()
    arguments[0]()}
}

var age = 10
function fn() {console.log(this.age)
}

obj.foo(fn)

答案

本题的答案是:

// 第一个输入 10
func()
// 第一个输入 undefined
arguments[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 = 10
const 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.fn
fn(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 曾经变成了 3
console.log(obj.num) // 输入 3
// 这个 num 是 window.num
console.log(num) // 输入 4
// 如果将 obj.sub 赋值给一个新的变量,那么这个函数的作用域将会变成新变量的作用域
const sub = obj.sub
// 作用域变成了 window window.num 是 4
sub() // 输入 4 
题目二

输入后果为: 22 23 65 30, 你答对了吗?上面咱们解析一下

var num = 10
const 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.fn
fn(5)
obj.fn(10)
console.log(num, obj.num)

咱们把下面的代码分为以下几步进行剖析

  1. 先看第三行代码,是一个赋值操作,咱们晓得赋值操作是从右向左的,而 = 号左边是一个立刻执行函数,所以会优先执行立刻执行函数,立刻执行函数没有手动指定 this, 这时候this = window,而立刻函数的参数num 是传进来的 obj.num, 所以num 参数默认值是 20
  2. 第四行相当于window.num = 20 * 3
  3. 第五行为传入的参数加一,所以 num = 20 + 1
  4. 第六行 return 了一个函数,而这个函数就是 obj.fn 的值, 然而因为 return 的函数援用了立刻执行函数外面的num,所以造成了闭包。这时候

    obj.fn = function(n) {
      this.num += n
      // 这个 num 是立刻执行函数外面的 num
      num++
      console.log(num)
    }
  5. var fn = obj.fn, 将 obj.fn 赋值给新的变量,而这个变量的作用域是window
  6. 在调用 fn(5) 的时候,在第二步,window.num的值曾经变成了 60, 而后因为这时候fnthiswindow, this.num += n 相当于window.num += n, 即window.num = 65
  7. num++, 因为闭包的起因,第三步 num21, 所以这一步 num变成了22, 同时输入22
  8. 而后 obj.fn(10), 这时候fnthisobj,obj.num 默认值是 20, this.num += n 相当于 obj.num += 10
  9. 和第七步一样,num + 1 输入 23
  10. 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)

答案

一起来看看答案吧

  1. 第一行代码
// 输入 "" 空字符串
console.log([] + [])

这行代码输入的是空字符串 "",包装类型在运算的时候,会先调用valueOf 办法,如果 valueOf 返回的还是包装类型,那么再调用 toString 办法

// 还是 数组
const val = [].valueOf()
// 数组 toString 默认会将数组各项应用逗号 "," 隔开, 比方 [1,2,3].toSting 变成了 "1,2,3", 空数组 toString 就是空字符串
const val1 = val.toString() // val1 是空字符串

所以下面的代码相当于

console.log(""+"")
  1. 第二行代码

    // 输入 "[object Object]"
    console.log({} + [])

    和第一题情理一样,对象 {}隐氏转换成了 [object Object], 而后与"" 相加

  2. 第三行代码

    // 输入 true
    console.log([] == ![])

    对于 ===, 会严格比拟两者的值,然而对于== 就不一样了

    1. 比方 null == undefined
    2. 如果非 numbernumber比拟,会将其转换为number
    3. 如果比拟的单方中由一方是 boolean, 那么会先将boolean 转换为number

所以对于下面的代码, 看上面一步一步剖析

// 这个输入 false
console.log(![])
// 套用下面第三条 将 false 转换为 数值
// 这个输入 0
console.log(Number(false))
// 包装类型与 根本类型 == 先将包装类型通过 valueOf toString 转换为根本类型 
// 输入 ""
console.log([].toString())
// 套用第 2 条,将空字符串转换为数值、// 输入 0
console.log(Number(""))
// 所以
console.log(0 == 0)
  1. 第四行代码

    // 输入 1
    console.log(true + false)

    两个根本类型相加,如果其中一方是字符,则将其余的转换为字符相加,否则将类型转换为 Number, 而后相加, Number(true)1, Number(false)0, 所以后果是 1

总结

面试造火箭,工作拧螺丝。尽管我只想拧螺丝,然而我却须要通过造火箭来找到拧螺丝的工作,每日一题,每天都有新的面试题目,欢送关注公众号【前端有的玩】,拉你进入前端技术交换群,每日一题等着你来一起答题。

结语

不要吹灭你的灵感和你的想象力; 不要成为你的模型的奴隶。——文森特・梵高

正文完
 0