蔚来前端——人生中第一次面试

2024 年 5 月 10 日 星期五(已编辑)
/ ,
107
1
这篇文章上次修改于 2024 年 5 月 11 日 星期六,可能部分内容已经不适用,如有疑问可询问作者。

阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。

总结

第一次面试不得不说还是有点紧张,拿大厂练手了也是,难度还好基本上都是问的我简历上面的知识点,面试官比我想象的年轻,可惜不是一个女生,我觉得女面试官应该会更温柔一点哈哈哈,需要改进的地方还有很多,慢慢加油

1.三元组算法

给定一个数组num[-1,1,0,2,3,5]

如果满足i!=j i!=k j!=k 同时 nums[i]+nums[j]+nums[k]=0 则为一个三元组

输出[[-1,1,0]],重复不算

1.1 纯暴力写法

#include <iostream>
#include <vector>
using namespace std;
​
vector<vector<int> > solove(vector<int> nums){
    vector<vector<int> > v;
    int len = nums.size();
​
    for(int i=0;i<len;i++){
            for(int j=i+1;j<len;j++){
                for(int k=j+1;k<len;k++){
                    if(nums[i]+nums[j]+nums[k] == 0){
                        vector<int> n;
                        n.push_back(nums[i]);
                        n.push_back(nums[j]);
                        n.push_back(nums[k]);
                        v.push_back(n);
                    }
                }
            }
        }
​
    return v;
}
​
int main(){
    vector<int> nums(6);
​
    for(int i=0;i<6;i++){
        cin>>nums[i];
    }
​
    vector<vector<int> > v = solove(nums);
    cout<<'[';
    for(int i=0;i<v.size();i++){
        int count=1;
        cout<<'[';
        for(int j=0;j<v[i].size();j++){
            cout<<v[i][j];
            count++;
            if(count<=3) cout<<',';
        }
        cout<<']';
    }
    cout<<']';
}

1.2 二分优化

#include <iostream>
#include <vector>
using namespace std;
​
vector<vector<int> > solove(vector<int> nums){
    vector<vector<int> > v;
    sort(nums.begin(),nums.end());
    int len = nums.size();
    int left=0,right=len-1;
    for(int i = 0; i < len - 2; i++){
        if (i > 0 && nums[i] == nums[i - 1]) // Skip duplicates
            continue;
​
        int left = i + 1;
        int right = len - 1;
​
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            if (sum == 0) {
                vector<int> triplet;
                triplet.push_back(nums[i]);
                triplet.push_back(nums[left]);
                triplet.push_back(nums[right]);
                v.push_back(triplet);
                while (left < right && nums[left] == nums[left + 1])
                    left++;
                while (left < right && nums[right] == nums[right - 1])
                    right--;
                left++;
                right--;
            } else if (sum < 0) {
                left++;
            } else {
                right--;
            }
        }
    }
​
    return v;
}
​
int main(){
    vector<int> nums(6);
​
    for(int i=0;i<6;i++){
        cin>>nums[i];
    }
​
    vector<vector<int> > v = solove(nums);
    cout<<'[';
    for(int i=0;i<v.size();i++){
        int count=1;
        cout<<'[';
        for(int j=0;j<v[i].size();j++){
            cout<<v[i][j];
            count++;
            if(count<=3) cout<<',';
        }
        cout<<']';
    }
    cout<<']';
}

2.实现一个事件管理器

没写出来。。。

class EventManager {
    constructor() {
        this.events = {};
    }
​
    on(event, callback) {
        if (!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(callback);
    }
  
    emit(event, ...args) {
        const eventCallbacks = this.events[event];
        if (eventCallbacks) {
            eventCallbacks.forEach(callback => {
                callback(...args);
            });
        }
    }
​
    off(event, callbackToRemove) {
        const eventCallbacks = this.events[event];
        if (eventCallbacks) {
            this.events[event] = eventCallbacks.filter(callback => callback !== callbackToRemove);
        }
    }
​
    offAll(event) {
        delete this.events[event];
    }
}
​
const eventManager = new EventManager();
​
function handleClick1() {
    console.log('Button 1 clicked');
}
function handleClick2() {
    console.log('Button 2 clicked');
}
eventManager.on('click', handleClick1);
eventManager.on('click', handleClick2);
eventManager.emit('click');
​
eventManager.off('click', handleClick1);
eventManager.emit('click');
eventManager.offAll('click');
eventManager.emit('click');
​

3.问ts泛形

TypeScript 中的泛型允许编写可以在多种类型上重复使用的代码。泛型是在定义函数、类或接口时不指定具体类型,而是在使用时再指定具体类型的一种机制。泛型的主要优势在于它可以增加代码的灵活性和可重用性,同时提高了代码的类型安全性。

4.ES6新特性

  1. 箭头函数(Arrow Functions):提供了更简洁的函数定义方式,同时固定了函数内部的 this 指向。
  2. 常量声明(const)和块级作用域(let):引入了 const 和 let 关键字,允许声明块级作用域的变量,解决了 var 存在的一些问题。
  3. 模板字符串(Template Literals):使用反引号 ` 可以创建多行字符串和在字符串中嵌入变量,更加方便易读。
  4. 解构赋值(Destructuring Assignment):允许通过模式匹配的方式从数组或对象中提取数据,赋值给变量。
  5. 扩展运算符(Spread Operator)和剩余参数(Rest Parameters):通过 ... 运算符可以将数组展开为多个参数,也可以用于收集函数参数成为一个数组。
  6. 默认参数(Default Parameters):在函数定义中可以为参数设置默认值,简化了函数调用时的逻辑。
  7. 类(Class):引入了类和面向对象的概念,更加清晰地定义了对象的构造和行为。
  8. 模块化(Modules):提供了 import 和 export 关键字,使得 JavaScript 可以更好地组织和管理代码。
  9. Promise:提供了一种更优雅的方式来处理异步操作,解决了回调地狱的问题。
  10. 迭代器和生成器(Iterators and Generators):引入了用于自定义迭代行为的迭代器和生成器,简化了处理集合的操作。
  11. 新的数据结构:包括 Map、Set、WeakMap 和 WeakSet,提供了更灵活和高效的数据结构选项。
  12. Symbol:引入了一种新的基本数据类型,用于创建唯一的对象属性。
  13. 新的方法:如 Array.prototype.includes()、Object.entries() 等,提供了更便捷的方法来操作数组和对象。

5.Promise常用方法

  1. then(onFulfilled, onRejected):then 方法用于指定 Promise 对象的成功(onFulfilled)和失败(onRejected)的回调函数。它返回一个新的 Promise 对象,可以进行链式调用。
  2. catch(onRejected):catch 方法用于指定当 Promise 对象状态变为 rejected 时的回调函数。它是 then 方法的一个特例,专门用于捕获异常。
  1. finally(onFinally):finally 方法用于指定当 Promise 对象状态变为 fulfilled 或 rejected 时都会执行的回调函数,无论 Promise 的状态如何都会执行。
  2. Promise.resolve(value):Promise.resolve 方法返回一个以给定值解析后的 Promise 对象。如果传入的值是一个 Promise 对象,则直接返回这个对象;否则返回一个新的以该值解析后的 Promise 对象。
  3. Promise.reject(reason):Promise.reject 方法返回一个状态为 rejected 的 Promise 对象,并将给定的 reason 作为该 Promise 对象的失败原因。
  4. Promise.all(iterable):Promise.all 方法接收一个可迭代对象(比如数组),并返回一个新的 Promise 对象,该对象在传入的所有 Promise 对象都成功解析后才会解析,如果其中一个 Promise 对象失败,则整个 Promise 对象将被拒绝。
  5. Promise.race(iterable):Promise.race 方法接收一个可迭代对象(比如数组),并返回一个新的 Promise 对象,该对象在传入的 Promise 对象中有一个状态改变时,它的状态就跟着改变,第一个解析或拒绝的 Promise 对象的结果会传递给返回的 Promise 对象。

6.问登录逻辑

  1. 在前端登录页面收集登录所需的账号密码,POST提交至后端
  2. 后端检查数据库是否存在对应的一个账号密码,如果存在则用JWT生成一个Token返回
  3. 前端在收到Token后会在LocalstorageRedux各存一份,然后跳转至后台
  4. 后台的路由用HOC(高阶组件)包裹,高阶组件会检查是否携带Token,如果有则跳转至后台页面,没有就返回

5.问防抖节流

没有要实现代码,就是大概实现逻辑

// 定义一个防抖函数
function debounce(fn,wait) {
​
    // 创建一个定时器并初始化为null
    var timer = null;
​
    return function () {
        // 保存函数执行上下文和参数
        var context = this,
        args = [...arguments];
​
        // 如果定时器存在则清除定时器
        if(timer){
            clearTimeout(timer);
            timer = null;
        }
​
        // 重新定时
        timer  = setTimeout(() => {
            // fn.apply(context, args) 是 JavaScript 中的一种函数调用方式,
            // 它的作用是在指定的上下文(context)中调用函数(fn),并且以数组形式传递参数(args)给该函数。
            fn.apply(context,args);
        },wait)
    }
}
function throttle(fn,wait){
    var preTime = Date.now()
​
    return function(){
        var context = this,
        args = [...arguments],
        nowTime = Date.now()
​
        if(nowTime - preTime >= wait) {
            preTime = Date.now()
            return fn.apply(context,args)
        }
    }
}

7.项目有没有自定义Hooks

回答的封装的防抖节流的Hooks

8.问项目

主要聊了一下怎么优化的,我说得在客户端层面就是懒加载,打包层面就是代码拆分,打包压缩,如何就是服务器方面Nginx开启缓存和Gzip,然后就是在请求后端数据时候是怎么去调用的(这段没有答好,有点不记得了,好像是挑选了一个最重的页面问的请求)

9.闲聊反问

问了一下这是社招还是校招,好像投错成社招了🤣,还好面试官说是校招,还有就是有没有需要学习的地方,面试官回答要多刷题

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...