专栏首页前端有的玩一起回归一下每日一题这些经典面试题

一起回归一下每日一题这些经典面试题

“需求已改活已加,加班通宵看朝霞。终是上线已延期,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)
  },100)
}

/**
 * 请在此方法中调用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}`)
})

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

  1. 青铜难度, 输出时长大于6秒
  2. 白银难度, 输出时长大于3秒
  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操作符调用构造函数所经历的阶段:

  1. 创建一个新的对象;
  2. 将构造函数的作用域赋给新的对象;
  3. 执行构造函数中的代码;
  4. 返回新的对象;
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函数传的参数仅仅是用于标识的,不会影响值的唯一性

对于第二个输出, 因为ef都是对象,而对象的key只能是数值或字符,所以会将对象转换为字符,对象的toString方法返回的是[object Object], 所有输出的是君子

题目六

题目介绍

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

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("" + "")

第二行代码

// 输出 "[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
  • 如果比较的双方中由一方是boolean,那么会先将boolean转换为number

第四行代码

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

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

结语

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

本文分享自微信公众号 - 前端有的玩(gh_918bae0a9616),作者:前端进击者

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-08-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一些经典的Http面试题

    还记得这道经典的面试题目吗?从 URL 在浏览器被被输入到页面展现的过程中发生了什么?

    程序IT圈
  • Linux经典面试题,了解一下!

    绝对路径用什么符号表示?当前目录、上层目录用什么表示?主目录用什么表示? 切换目录用什么命令?

    框架师
  • Linux经典面试题,了解一下!

    绝对路径用什么符号表示?当前目录、上层目录用什么表示?主目录用什么表示? 切换目录用什么命令?

    lyb-geek
  • 面试遇坎,每日一题我精选了这些题目的答案

    每日工作之余,我会将自己整理的一些前端面试题笔试题整理成每日一题,然后在公众号中推送给大家,每天仅需几分钟做一道题,经过日积月累,在换工作的时候一定能让你拿到一...

    用户1308196
  • 每日一道面试题 【征集令】& 【eBay】 面试经验分享

    最近参加了不少面试,深深感受到了面试的侧重点和平时工作所用到的东西差异还是不小的,或许是我本来基础缺失,所以有些面试经历都不是很愉快。

    周三不加班
  • 一次Flutter面试经验,这些问题你一定要知道!必问!!

    说实话,第一个、第三个、第六个我准备的话应该能答出来的,但是一个多月没碰Flutter了,忘了都差不多。。。 等下把二面的答案写出来,希望能帮助后来人。 此外G...

    猿哥
  • 【每日算法Day 76】经典面试题:中序遍历的下一个元素,5大解法汇总!

    首先本题中的二叉树还是个二叉搜索树,也就是中序遍历是单调递增的,所以我们可以利用这个性质来简化查找过程。

    godweiyang
  • 十二条Linux运维面试必备经典笔试/面试题,来挑战一下!

    又到了一年一度的秋招,作为运维方向,看了一些面经,收集了一些笔试面试题,总结了一下,贴出来仅供参考,有错误的地方还请指出. 1.Linux设置环境变量 暂时的:...

    小小科
  • 当裸辞遇到面试难,这些面试题你需要了解一下

    又要到金九银十的跳槽季了,为了让更多的小伙伴可以在面试的时候取的更好的offer,所以自上月起我每天都会在自己的公众号【前端有的玩】里面推送一到两道面试题,俗称...

    用户1308196
  • 十二条Linux运维面试必备经典笔试/面试题,来挑战一下吧!

    运维行业正在变革,推荐阅读:30万年薪Linux运维工程师成长魔法 又到了一年一度的秋招,作为运维方向,看了一些面经,收集了一些笔试面试题,总结了一下,贴出来仅...

    小小科
  • 【每日算法Day 74】经典面试题:约瑟夫环,我敢打赌你一定不会最后一种方法!

    这 个数字排成一个圆圈,从数字 开始,每次从这个圆圈里删除第 个数字。求出这个圆圈里剩下的最后一个数字。

    godweiyang
  • 测试一下25道Spring经典面试题你会几道?循环依赖面试详解

    当一个ClassA依赖于ClassB,然后ClassB又反过来依赖ClassA,这就形成了一个循环依赖:

    程序员追风
  • 经典面试题-Extjs使用中firebug返回一个TypeError:c is undfined,请问意思

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    cwl_java
  • 经典面试题-列举JSP的内置对象,同一应用中页面有哪些方法

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    cwl_java
  • 剖析一些经典的CSS布局问题,为前端开发+面试保驾护航

    这样做的优点就是在图文混排的时候可以很好的使文字环绕在图片周围。另外当元素浮动了起来之后,它有着块级元素的一些性质例如可以设置宽高等,但它与inline-blo...

    桃翁
  • 备战金九银十:当你裸辞遇到了面试难,你需要了解一下这些面试题

    又要到金九银十的跳槽季了,为了让更多的小伙伴可以在面试的时候取的更好的offer,不定期都会分享BAT常问面试题,下面这些面试内容你都会了,30K不在话下,由于...

    Android技术干货分享
  • 经典面试题:在浏览器地址栏输入一个 URL 后回车,背后发生了什么

    在浏览器的地址栏输入一个 URL 后回车,背后到底发生了什么才能使得一个界面完美的展现在我们眼前?

    飞天小牛肉
  • Android面试遇坎,我精选了这些题目与答案,你离大厂还有多远测试一下就知道!

    又要到金九银十的跳槽季了,为了让更多的小伙伴可以在面试的时候取的更好的offer,每日工作之余,我都会将自己整理的一些Android面试题笔试题整理成每日一题,...

    Android技术干货分享
  • 【每日算法Day 85】图解算法:一行代码解决约瑟夫环的变体

    给定一个从 到 排序的整数列表。首先,从左到右,从第一个数字开始,每隔一个数字进行删除,直到列表的末尾。第二步,在剩下的数字中,从右到左,从倒数第一个数字开...

    godweiyang

扫码关注云+社区

领取腾讯云代金券