【前端面经】八股文面试中,发现一些公司前端对深度的把握不足

1,083 阅读2分钟

最近在找新的机会,于是看了投了一些大厂的简历。可惜的是,国内的游戏厂依旧是架子极高,而相对的其他厂则慢慢走向僵化。

或许以前是为了筛选人才。但以现在这种搜索一下,3分钟就能知道答案的情况来看,业界不仅在走下坡路,而且创新能力也在下降,面试的水平不敢恭维。

第一家

一面

常见八股文:

  1. http1.1和http2.0的区别。 答案: 多路复用。
  2. BFC的问题结局. 答案: overflow: hidden;
  3. 变量提升问题:
for(let i = 0; i <= 5; i++ ) {
    console.log(i)
}

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


const a = [1,2,3];
for(var j = 0; j < a.length; j++ ) {
    console.log(a[j])
}
  1. map获取key-value 面试过程中发现面试官对Map的理解只停留在表面,而对实际内部对如何迭代和取值并不清楚。

原题:

const a = new Map([["a", 1], ["b", 2]])
console.log(a.entries())

面试官只是照本宣科去考察对基础语法的概念,却没有注意到实际下的问题。

stackoverflow's iterator这篇帖子为例。

首先,先看调用Entries不生效的场景。

function pushAndLog(array, values) {
    array.push(...values);
    for (const value of values.entries()) {
        console.log(value);
    }
}

const array = [];
const map = new Map([["a", 1], ["b", 2]]);
pushAndLog(array, map.keys());

在这种情况下,将不会进入到for循环内部。

原因:

  • Map.keys() / Map.values() / Map.entries()方法返回的都是迭代器而不是可迭代对象。
  • 和可迭代对象相反的是,迭代器是一个有状态的对象。一旦使用,则不可复用。如果需要使用,则需要自己去构造一个新的迭代器。

答案:

  1. 修复上述代码最简单的办法是解构为函数。因为函数是一个可迭代对象,可以多次迭代,也就是自定义迭代器
pushAndLog(array, [...map.keys()]);

本质就是:

var test = {}
testIterator[Symbol.iterator] = function*() {
    yield 1;
    yield 2;
};

[...testIterator] // [1,2]

但是这种办法不是最高效的,会消耗内存和CPU的时间。于是,我们可以写更为高效的办法:

export function getIterableKeys<K, V>(map: Iterable<readonly [K, V]>): Iterable<K> {
    return {
        [Symbol.iterator]: function* () {
            for (const [key, _] of map) {
                yield key;
            }
        }
    };
}

export function getIterableValues<K, V>(map: Iterable<readonly [K, V]>): Iterable<V> {
    return {
        [Symbol.iterator]: function* () {
            for (const [_, value] of map) {
                yield value;
            }
        }
    };
}

二面

  1. 你们公司的BFF架构.
  2. BFF为什么要和SSO通讯。
  3. 架构升级的话,你主要是做了什么。
  4. 升级API gateway的好处。
  5. Schema-AST这种低代码平台的应用?
  6. 怎么管理团队的?
  7. WebGL主要做了什么?

第二家

1. 八股文

问题: js的对象在堆还是栈? 答案: 堆 问题: js的字符串在堆还是栈? 答案: 栈

1. var-结果

var a
if('a' in window) {
    a = 1
}

console.log(a) // 1

2. let-结果

let a
if('a' in window) {
    a = 1
}

console.log(a) // undefined

3. use strict-结果

'use strict'

let a
if('a' in window) {
    a = 1
}

console.log(a) // undefined

2. TypeScript

已经声明一个接口后,转而对这个接口进行增查删改。

1. Pick

interface IUser {
    name: string
    age: string
    location: string
}

interface ITest {
    name: string
    age: string
    map: string[]
}

type ITest1 = Pick<IUser, "name" | "age">

interface ITest2 extends ITest1 {
    map: string[]
}

const a: ITest2 =  {
    name: '1',
    age: '2',
    map: [
        '1'
    ]
}

简化版:

interface IUser {
    name: string
    age: string
    location: string
}

interface ITest extends Pick<IUser, "name" | "age"> {
    map: string[]
}


const a: ITest = {
    name: '1',
    age: '1',
    map: ["1"],
}

2. Omit

interface IUser {
    name: string
    age: string
    location: string
}

type ITest3 = Omit<IUser, "location">
interface ITest4 extends ITest3  {
    map: string[]
}

const b: ITest4 = {
    map: [
        "1",
    ],
    name: '1',
    age: '1'
}

3. Pick的问题

问题: 上述的IUsername改变,下面的声明是否会改变? 答案: 会改变。