前提
面试官不让自我介绍,直接开干了...
开始
基础类型都有哪些,复杂类型都有哪些,有啥特点
当时回答:
- 基本类型: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高级程序设计》这本书里的内容基本考遍了,还是需要多温习