老虎证劵前端一面

124 阅读6分钟

前提

面试官不让自我介绍,直接开干了...

开始

基础类型都有哪些,复杂类型都有哪些,有啥特点

当时回答:

  • 基本类型:string, number, boolean, null, undefined, symbol, bigint
  • 复杂类型:object, array, function, set, map

特点:

  • 基本类型存在栈内存中,基本类型的修改 a = 2; b = a; a = 3; b 还是 2
  • 复杂类型值存在堆内存中,指针存在栈内存中,复杂类型的修改,a = { name: 2 }; b = a; a.name = 3; b.name 就变成了 3,a 和 b 指向同一个内存地址

typeof 都能判断哪些类型?怎么判断一个对象是数组类型

当时回答:

typeOf 能判断基本类型,复杂类型只能是 function 和 object, 其他的都放好 object

判断一个对象是数组类型:

  • toString.call(arr) === '[Object Array]'
  • arr.proto === Array.prototype
  • arr.hasOwnProperty('sort')

后来:

  • arr instanceof Array
  • Array.isArray(arr)

既然提到了proto 那么看看这个 var n = 1; n.name = 2; 会报错吗?为什么?

当时回答:

会,因为 n 是基本类型,不是引用类型,无法添加属性

后来: 不会,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据(null, undefined 没有对应的构造函数形式)。

知道包装类型么

当时回答:

忘了

后来:

每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据(null, undefined 没有对应的构造函数形式)。基本包装函数,与引用类型主要区别就是对象的生存期,使用 new 操作符创建的引用类型的实例,在执行流离开当前作用域之前一直都保存在内存中,而自动创建的基本包装类型的对象,则只存在与一行代码的执行瞬间,然后被立即销毁。这也就是不能给基本类型添加属性和方法的原因了。

类型转换都有哪些

  • 隐式转换:基本类型运算会进行隐式转换,比如
const num = 1
const str = 'str' + num // 这里num会隐式转换成string
console.log(0 == false)
console.log(0 == '')
console.log(null == undefined)
const res = null
if (num) {
  consle.log('num is true')
}
  • 显示转换:
const n = number('1')
const str = String(1)
const b = Boolean(1)
const n = ParseInt('1')

下面输出什么,是一秒输出一个么?

for (var i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i)
  }, 1000)
}

当时回答:

  • 不会一秒一秒的输出,只会在一秒后连续输出 5 个 5
  • 原因是 setTimeout 是异步的,当执行到 setTimeout 的时候,开启定时任务,时间到了,把回调函数丢到消息队列里
  • 主任务执行完,也就是 for 循环执行完,i 的值是 5,然后从消息队列取一个回调函数执行,这时候的 i 已经是 5 了,所以输出是 5
  • 后续依次从消息队列取回调函数执行, 这里一共执行了 5 次,所以输出 5 个 5

怎么让它输出 1-5,都有哪些方式

当时回答:

  • 方式 1:闭包实现
for (var i = 0; i < 5; i++) {
  (function (a) {
    setTimeout(() => {
      console.log(a)
    }, 1000)
  })(i+1)
}
  • 方式2:使用 let 实现,原理是
    • 有块级作用域,每次循环都会创建一个新的i
    • 相当于自动为我们创建了闭包
for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i+1)
  }, 1000)
}

这里为啥用闭包,你这里是自执行函数吧,怎么就闭包了,如果我这么写呢,输出什么?

当时回答:

为啥是闭包:因为自执行函数的参数i是私有变量,回调函数作为子函数,可以访问上层作用域的变量i,for循环虽然结束了,但是i却被闭包起来了

for (var i = 0; i < 5; i++) {
  (function () {
    const j = i + 1
    setTimeout(() => {
      console.log(j)
    }, 1000)
  })()
}

还是闭包输出 1-5

描述下事件循环

主任务->所有微任务->宏任务->所有微任务->gui渲染->宏任务->所有微任务...

let var const 区别

当时回答:

  • var 会变量提升,let const 不会
  • var 可以重复声明,let const 不行
  • var let 可以反复赋值,可不初始化,const 不行

后来:

  • let const 有块级作用域

什么是作用域,都有哪些作用域

当时回答:

作用域就是程序就要设计一套规则来存储变量,并且方便的找到这些变量,这套规则就是就成为作用域,或者说是执行上下文

  • 全局作用域/global
  • 函数作用域/局部作用域/local
  • 块级作用域/block

后来:

在 JavaScript 中, 作用域(scope,或译有效范围)就是变量和函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期

补充下

  • script作用域 a处于script作用域中,其他地方的script中无法访问a

实现一个数组的 group 方法,参数为 size,输出 size 的分组。比如

const arr = [1, 2, 3, 4, 5]
arr.group(2) // [[1,2],[3,4],[5]]
arr.group(3) // [[1,2,3],[4,5]]

当时回答:

Array.prototype.group = function (size) {
  const newArr = []
  let newIndex = 0
  for (let i = 0; i < this.length; i ++) {
    if (!newArr[newIndex]) {
      newArr[newIndex] = []
    }
    if (newArr[newIndex].length >= size) {
      newIndex ++
      newArr[newIndex] = []
    }
   newArr[newIndex] = (newArr[newIndex] || []).concat(this[i])
  }
  return newArr
}

后来:

利用步进的思想简化下

Array.prototype.group = function (size) {
  const result = []
  for (let i = 0; i < this.length; i += size) {
    result.push(this.slice(i, i + size))
  }
  return result
}

构造函数内部 new 都做了些啥

当时回答:

  • 创建一个新对象
  • 把this指向新对象

后来:

  • 创建一个空对象 obj = {}
  • 把构造函数的原型赋值给新对象 obj.proto = constructor.prototype
  • 把this指向新对象 res = constructor.call(obj, ...args)
  • 返回res, 如果res不是对象,返回新对象obj

下面有三个正则,请你为每一个正则,选择匹配的字符串至少 3 个

const regx1 = /^1{3,5}$/
const regx2 = /^1[3,5]?$/
const regx3 = /^1(3,5)*?$/

当时回答:

const regx1 = /^1{3,5}$/
const regx2 = /^1[3,5]?$/
const regx3 = /^1(3,5)*?$/

const regx1Str1 = '111'
const regx1Str2 = '1111'
const regx1Str3 = '11111'
console.log('1 test1 =>', regx1.test(regx1Str1)) // true
console.log('1 test2 =>', regx1.test(regx1Str2)) // true
console.log('1 test3 =>', regx1.test(regx1Str3)) // true

const regx2Str1 = '1'
const regx2Str2 = '15'
const regx2Str3 = '13'
const regx2Str4 = '1,'
console.log('2 test1 =>', regx2.test(regx2Str1)) // true
console.log('2 test2 =>', regx2.test(regx2Str2)) // true
console.log('2 test3 =>', regx2.test(regx2Str3)) // true
console.log('2 test4 =>', regx2.test(regx2Str4)) // true

const regx3Str1 = '1'
const regx3Str2 = '13,53,53,5'
const regx3Str3 = '13,5'
console.log('3 test1 =>', regx3.test(regx3Str1)) // true
console.log('3 test2 =>', regx3.test(regx3Str2)) // true
console.log('3 test3 =>', regx3.test(regx3Str3)) // true

后来:

第三个正则有点迷 *?,匹配前面的字符0次或多次,但尽可能少匹配?这个?有点多余,但是执行时候也不会报错

时间差不多了,没啥问题我们结束吧

当时回答:

好的,拜拜

后来:

不是不给对方面子,下午的这个时间端,3点了,赶着回去干活呢,又不让约晚上, 哎

总结

有点和校招生一样,把《javaScript高级程序设计》这本书里的内容基本考遍了,还是需要多温习