Skip to content

2023-07-17 前端面试题

2023-07-17-js-实战.doc

  1. 写一个方法,比如有一个刷新 token 的接口请求,还有其他的请求,当刷新 token 的请求正在进行的时候,同时其他请求也发出来的时候,要等待 token 接口请求返回后再继续其他的请求?
  2. 分别使用 Proxy、Object.definePropty 实现对一个对象进行劫持打印出读取和赋值的值?
  3. 简单说一下 JavaScript 的继承、原型和原型链?
  4. 你觉得做得最好的项目是哪一个?(简历)
  5. Hr 或者老大会问:说 3 个你的优势?
  6. 项目亮点难点,如何提炼亮点难点? (粉丝提问)

1. 写一个方法,比如有一个刷新 token 的接口请求,还有其他的请求,当刷新 token 的请求正在进行的时候,同时其他请求也发出来的时候,要等待 token 接口请求返回后再继续其他的请求?

javascript
let refreshTokenPromise = null; // 用来存放刷新token的请求Promise

async function refreshToken() {
  // 这里模拟实现刷新token的操作
  return new Promise((resolve) =>
    setTimeout(() => {
      resolve("new_token");
    }, 1000)
  );
}

// 添加请求拦截器
axios.interceptors.request.use(
  async function (config) {
    // 在发送请求之前做些什么
    const userStore = store.state.user; // 从store中获取用户token和过期时间
    let userToken = userStore.userToken; // 用户token
    const expire = userStore.expire; // 过期时间 时间戳

    // 如果token 不存在或者token 过期
    if (!userToken || Date.now() > expire) {
      if (!refreshTokenPromise) {
        // 如果还没有开始刷新token,则开始刷新,并保存Promise对象
        refreshTokenPromise = refreshToken().finally(() => {
          refreshTokenPromise = null; // 无论刷新成功还是失败,确保清除refreshTokenPromise
        });
      }

      userToken = await refreshTokenPromise; // 等待刷新操作完成
      store.state.user.userToken = userToken; // 更新store中的用户token
    }

    config.headers["Authorization"] = `Bearer ${userToken}`; // 使用新的token
    return config;
  },
  function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// 添加响应拦截器
axios.interceptors.response.use(
  function (response) {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response;
  },
  function (error) {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    return Promise.reject(error);
  }
);

2. 分别使用 Proxy、Object.defineProperty 实现对一个对象进行劫持打印出读取和赋值的值?

javascript
// 使用 Proxy
let obj = { name: "Tom", age: 20 };
let handler = {
  get(target, key) {
    console.log(`读取${key}: ${target[key]}`);
    return Reflect.get(target, key);
  },
  set(target, key, value) {
    console.log(`设置${key}为: ${value}`);
    return Reflect.set(target, key, value);
  },
};
let proxyObj = new Proxy(obj, handler);
proxyObj.name; // 读取name: Tom
proxyObj.age = 21; // 设置age为: 21

// 使用 Object.defineProperty
let obj2 = { name: "Jerry", age: 18 };
function defineReactive(obj, key, val) {
  Object.defineProperty(obj, key, {
    get() {
      console.log(`读取${key}: ${val}`);
      return val;
    },
    set(newVal) {
      console.log(`设置${key}为: ${newVal}`);
      val = newVal;
    },
  });
}
for (let key in obj2) {
  defineReactive(obj2, key, obj2[key]);
}
obj2.name; // 读取name: Jerry
obj2.age = 19; // 设置age为: 19

希望是一个复杂的对象,要考虑到对象还嵌套这对象

如果需要处理嵌套对象,那我们就需要对我们的劫持逻辑进行改进,使其可以递归处理对象。以下是一个改进的版本:

js
// 使用 Proxy
function deepProxy(obj) {
  let handler = {
    get(target, key) {
      console.log(`读取${key}: ${target[key]}`);
      if (typeof target[key] === "object" && target[key] !== null) {
        return new Proxy(target[key], handler);
      } else {
        return Reflect.get(target, key);
      }
    },
    set(target, key, value) {
      console.log(`设置${key}为: ${value}`);
      return Reflect.set(target, key, value);
    },
  };
  return new Proxy(obj, handler);
}

let obj = { name: "Tom", age: 20, info: { hobby: "reading" } };
let proxyObj = deepProxy(obj);
proxyObj.info.hobby; // 读取hobby: reading
proxyObj.info.hobby = "gaming"; // 设置hobby为: gaming

// 使用 Object.defineProperty
function defineReactive(obj) {
  Object.keys(obj).forEach((key) => {
    let internalValue = obj[key];
    if (typeof internalValue === "object" && internalValue !== null) {
      defineReactive(internalValue);
    }
    Object.defineProperty(obj, key, {
      get() {
        console.log(`读取${key}: ${internalValue}`);
        return internalValue;
      },
      set(newVal) {
        console.log(`设置${key}为: ${newVal}`);
        if (typeof newVal === "object" && newVal !== null) {
          defineReactive(newVal);
        }
        internalValue = newVal;
      },
    });
  });
}

let obj2 = { name: "Jerry", age: 18, info: { hobby: "drawing" } };
defineReactive(obj2);
obj2.info.hobby; // 读取hobby: drawing
obj2.info.hobby = "singing"; // 设置hobby为: singing

3. 简单说一下 JavaScript 的继承、原型和原型链?

我们先简单解释一下 JavaScript 中的继承、原型和原型链,然后通过具体的例子来展示。

  • 继承(Inheritance):在 JavaScript 中,对象可以继承另一个对象的属性和方法。这种继承关系并不是通过复制来实现的,而是通过原型链来实现的。

  • 原型(Prototype):在 JavaScript 中,每一个对象都有一个指向其原型对象的内部链接。原型对象也有自己的原型,这样层层向上直到一个对象的原型为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。

  • 原型链(Prototype Chain):当试图访问一个对象的属性时,如果对象自身没有这个属性,那么 JavaScript 会沿着原型链向上查找,如果所有的原型对象都没有找到,则返回 undefined。

下面我们来看一个具体的例子:

javascript
// 创建一个构造函数
function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

// 在 Person 的原型对象上添加方法
Person.prototype.getFullName = function () {
  return this.firstName + " " + this.lastName;
};

// 创建一个新的 Person 对象
var john = new Person("John", "Doe");

// 这个对象由 Person 构造函数创建,因此它的原型是 Person.prototype
// 当我们调用 john 的 getFullName 方法时,JavaScript 首先检查 john 对象是否有这个方法
// 如果没有,它会沿着原型链向上查找,这里它在 Person.prototype 中找到了这个方法
console.log(john.getFullName()); // 输出 "John Doe"

// 如果我们尝试访问一个不存在的属性,比如 john.age,JavaScript 会沿着原型链向上查找
// 如果所有的原型对象都没有找到,则返回 undefined
console.log(john.age); // 输出 undefined

这就是 JavaScript 中的继承、原型和原型链的基本概念和示例。在 JavaScript 中,理解这些概念是非常重要的,因为它们是 JavaScript 对象和继承的基础。

4. 你觉得做得最好的项目是哪一个?

这个问题的答案因人而异,取决于你的个人经验和项目。你应该提到你认为最具挑战性、最有创新性或者对你职业生涯影响最大的项目。具体来说,你可以描述项目的目标、你的角色、你的贡献以及项目的结果。

5. Hr 或者老大会问:说 3 个你的优势?

这个问题的答案也因人而异,要根据自己的经验和能力来回答。你可能会提到你的专业技能、沟通能力、解决问题的能力、团队合作的能力等等。

6. 项目亮点难点,如何提炼亮点难点?

在描述项目的亮点和难点时,重要的是要讲故事,让听者了解你是如何克服挑战、取得成功的。亮点可能是创新的解决方案,优秀的团队合作,或者项目对业务产生了重大影响。难点可能是技术上的挑战,或者项目管理上的问题。在描述难点时,应该强调你是如何解决问题的,而不仅仅是问题本身。

例子:https://gitee.com/fe521/electron-ffmpeg-video-to-gif