美文网首页
面试造火箭,看下这些大厂原题

面试造火箭,看下这些大厂原题

作者: 猿天下 | 来源:发表于2020-11-01 20:54 被阅读0次

    前言

    需求已改活已加,加班通宵看朝霞。
    终是上线已延期,bug还是改不完。

    面试造火箭,工作拧螺丝,虽然我只想拧螺丝,可是我需要用造火箭的技术去寻找拧螺丝的工作,如何能在面试过程中让自己处于不败的地步呢,刷题是一个比较好的捷径,今天就汇总了一些比较经典的面试题进行了汇总,分享给大家。

    题目一

    题目介绍

    如下为一段代码,请完善sum函数,使得 sum(1,2,3,4,5,6) 函数返回值为 21 ,需要在 sum 函数中调用 asyncAdd 函数进行数值运算,且不能修改asyncAdd函数

    /**
     * 请在 sum函数中调用此函数,完成数值计算
     * @param {*} a 要相加的第一个值
     * @param {*} b 要相加的第二个值
     * @param {*} callback 相加之后的回调函数
     */
    function asyncAdd(a,b,callback) {
      setTimeout(function(){
       callback(null, a+b)
      },1000)
    }
    
    /**
     * 请在此方法中调用asyncAdd方法,完成数值计算
     * @param  {...any} rest 传入的参数
     */
    async function sum(...rest) {
      // 请在此处完善代码
    }
    
    let start = window.performance.now()
    sum(1, 2, 3, 4, 5, 6).then(res => {
      // 请保证在调用sum方法之后,返回结果21
      console.log(res)
      console.log(`程序执行共耗时: ${window.performance.now() - start}`)
    })
    

    本题根据程序输出时间不同,可以划分为三个难度等级

    青铜难度, 输出时长大于6秒
    白银难度, 输出时长大于3秒
    王者难度, 输出时长大于1秒

    答案

    青铜难度

    async function sum(...rest) {
      // 取出来第一个作为初始值
      let result = rest.shift()
      // 通过for of 遍历 rest, 依次相加
      for(let num of rest) {
        // 使用promise 获取相加结果
        result = await new Promise(resolve => {
          asyncAdd(result, num, (_,res) => {
            resolve(res)
          })
        })
      }
      // 返回执行结果
      return result
    }
    
    // 执行成功,执行时长大于6秒
    sum1(1, 2, 3, 4, 5,6).then(res => {
      console.log(`计算结果为:${res}`)
    })
    
    

    白银难度

    在青铜难度,我们把数组里面的每一项依次相加。但是也可以进行一些优化,可以并发执行多个,比如 sum(1,2,3,4,5,6),可以同时执行 1+2,3+4,5+6,这样就可以提升执行效率

    async function sum(...rest) {
      // 如果传的值少于2个,则直接返回
      if (rest.length <= 1) {
        return rest[0] || 0
      }
      const promises = []
      // 遍历将数组里面的值两个两个的执行
      for (let i = 0; i < rest.length; i += 2) {
        promises.push(
          new Promise(resolve => {
            // 如果 rest[i+1] 是 undefined, 说明数组长度是奇数,这个是最后一个
            if (rest[i + 1] === undefined) {
              resolve(rest[i])
            } else {
              // 调用asyncAdd 进行计算
              asyncAdd(rest[i], rest[i + 1], (_, result) => {
                resolve(result)
              })
            }
          })
        )
      }
      // 获取第一次计算结果
      const result = await Promise.all(promises)
      // 然后将第一次获取到的结果即 [3,7,11] 再次调用 sum执行
      return await sum(...result)
    }
    
    // 执行成功,执行时长大于3秒小于4秒
    sum1(1, 2, 3, 4, 5,6).then(res => {
      console.log(`计算结果为:${res}`)
    })
    

    王者难度

    async function sum(...rest) {
      let result = 0
      // 隐氏类型转换, 对象 + 数字,会先调用对象的toString 方法
      const obj = {}
      obj.toString = function() {
        return result
      }
      const promises = []
      for(let num of rest) {
        promises.push(new Promise((resolve) => {
          asyncAdd(obj, num, (_, res) => {
            resolve(res)
          })
        }).then(res => {
          // 在这里将 result的值改变之后,obj.toString 的返回值就变了,这时候下一个setTimeout调用时就使用了新值
          result = res
        }))
      }
      await Promise.all(promises)
      return result
    }
    
    // 执行成功,执行时长大于1秒小于2秒
    sum1(1, 2, 3, 4, 5,6).then(res => {
      console.log(`计算结果为:${res}`)
    })
    

    因为js是执行在单线程里面的,所以上面的代码,我们在for of将所有的计算放到promises数组里面,然后通过Promise.all去一次性执行,这时候并不需要考虑到底先执行哪两个数字相加。因为单线程的原因,我们可以保证这几个Promise是依次执行的,这时候obj.toString返回值就是上一个Promise的返回值,多跑几遍代码你就懂了哦

    题目二

    题目介绍

    请说明以下代码各输出了什么?

    console.log(typeof (() => {}))
    
    console.log(typeof [''])
    
    console.log(typeof null)
    
    console.log(typeof undefined)
    
    console.log(typeof Function.prototype)
    
    console.log('子君' instanceof String)
    
    console.log(new Date() instanceof Date)
    复制代码
    答案
    // 输出 function
    console.log(typeof (() => {}))
    
    // 输出 object
    console.log(typeof [''])
    
    // 输出 object
    console.log(typeof null)
    
    // 输出 undefined
    console.log(typeof undefined)
    
    // 输出 function 
    console.log(typeof Function.prototype)
    
    // 输出 false
    console.log('子君' instanceof String)
    
    // 输出 true
    console.log(new Date() instanceof Date)
    复制代码
    需要注意的是,对于 typeof, 可以正确判断除了null之外的所有基本类型,而对于引用类型,除了函数外其他都会被判断为object。
    对于instanceof,无法判断基本类型,但可以正确判断引用类型
    题目三
    题目介绍
    请实现一个instanceof,让以下代码可正常运行
    /**
     自定义instanceof 
    */
    function instanceOf(left, right) {
        // 请完善以下代码,不能使用原生instanceof
    }
    
    class A{}
    class B extends A {}
    class C{}
    
    const b = new B()
    // 输出 true
    console.log(instanceOf(b,B))
    // 输出 true
    console.log(instanceOf(b,A))
    // 输出 false
    console.log(instanceOf(b,C))
    

    答案

    本题主要考察instanceof的判断原理,instanceof主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false。

    /**
      自定义instanceof 
    */
    function instanceOf(left, right) {
      let proto = left.__proto__
      while(proto){
        if(proto === right.prototype){
           return true
        }
        proto = proto.__proto__
      }  
      return false
    }
    
    class A{}
    class B extends A {}
    class C{}
    
    const b = new B()
    // 输出 true
    console.log(instanceOf(b,B))
    // 输出 true
    console.log(instanceOf(b,A))
    // 输出 false
    console.log(instanceOf(b,C))
    

    题目四

    题目介绍

    请模拟实现new操作符,使下面代码正常运行

    function myNew(constructor, ...rest) {
     // 请在此处完善代码,不能直接使用 new 操作符
    }
    function Fun(name,sex) {
      this.name = name
      this.sex = sex
    }
    Fun.prototype.getUserInfo = function() {
      return `我的姓名${this.name},我的性别${this.sex}`
    }
    
    const fun = myNew(Fun,'子君','男')
    // 我的姓名子君,我的性别男
    console.log(fun.getUserInfo())
    

    答案

    这道题考察的是使用new操作符调用构造函数所经历的阶段:

    创建一个新的对象;
    将构造函数的作用域赋给新的对象;
    执行构造函数中的代码;
    返回新的对象;

    function myNew(constructor, ...rest) {
      if (typeof constructor !== 'function') {
            return constructor;
        }
        //创建新的对象,关联构造函数的原型对象
        const _constructor = Object.create(constructor.prototype);
        //执行构造函数
        const obj = constructor.apply(_constructor, rest);
        //如果构造函数执行结果是对象则返回执行结果
        if (typeof obj === 'object') {
            return obj;
        } else {
            return _constructor;
        }
    }
    function Fun(name,sex) {
      this.name = name
      this.sex = sex
    }
    Fun.prototype.getUserInfo = function() {
      return `我的姓名${this.name},我的性别${this.sex}`
    }
    
    const fun = myNew(Fun,'子君','男')
    // 我的姓名子君,我的性别男
    console.log(fun.getUserInfo())
    

    题目五

    题目介绍

    请说出以下代码输出内容

    const a = {}
    const b = Symbol('1')
    const c = Symbol('1')
    a[b] = '子君'
    a[c] = '君子'
    
    // 我是子君还是君子呢
    console.log(a[b])
    
    const d = {}
    const e = {key: '1'}
    const f = {key: '2'}
    d[e] = '子君'
    d[f] = '君子'
    
    // 我是子君还是君子呢
    console.log(d[e])
    复制代码
    答案
    const a = {}
    const b = Symbol('1')
    const c = Symbol('1')
    a[b] = '子君'
    a[c] = '君子'
    
    // 输出子君
    console.log(a[b])
    
    const d = {}
    const e = {key: '1'}
    const f = {key: '2'}
    d[e] = '子君'
    d[f] = '君子'
    
    // 输出君子
    console.log(d[e])
    

    对于第一个输出,Symbol()函数会返回「symbol」类型的值,而Symbol函数传的参数仅仅是用于标识的,不会影响值的唯一性
    对于第二个输出, 因为e和f都是对象,而对象的key只能是数值或字符,所以会将对象转换为字符,对象的toString方法返回的是[object Object], 所有输出的是君子

    题目六

    题目介绍

    请说出以下代码输出的内容

    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],然后与""相加

    第三行代码

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

    对于===, 会严格比较两者的值,但是对于==就不一样了
    所以对于上面的代码,看下面一步一步分析

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

    比如 null == undefined
    如果非number与number比较,会将其转换为number
    如果比较的双方中由一方是boolean,那么会先将boolean转换为number

    第四行代码

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

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

    $$$ 结语

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

    作者:前端进击者
    链接:https://juejin.im/post/6859121743869509646
    来源:掘金

    相关文章

      网友评论

          本文标题:面试造火箭,看下这些大厂原题

          本文链接:https://www.haomeiwen.com/subject/zukaektx.html