当前位置:首页 > 科技  > 软件

13个 JavaScript 面试难题及代码实现

来源: 责编: 时间:2024-07-15 17:11:30 60观看
导读今天我将带你深入了解 14 个常见的 JavaScript 高级面试问题。这些问题涵盖了 JavaScript 的面向对象、事件循环机制、Promise 等高级概念,以及函数柯里化和深度复制等实用技术。我们不仅从概念层面对每个问题进行了分

今天我将带你深入了解 14 个常见的 JavaScript 高级面试问题。这些问题涵盖了 JavaScript 的面向对象、事件循环机制、Promise 等高级概念,以及函数柯里化和深度复制等实用技术。Qj528资讯网——每日最新资讯28at.com

Qj528资讯网——每日最新资讯28at.com

我们不仅从概念层面对每个问题进行了分析,还提供了具体的代码实现。Qj528资讯网——每日最新资讯28at.com

那我们现在就开始吧。Qj528资讯网——每日最新资讯28at.com

1.this关键字指向

this关键字指向当前执行上下文中的一个对象。在函数中,this关键字通常指向函数的调用者。Qj528资讯网——每日最新资讯28at.com

问题:以下代码输出什么?为什么?Qj528资讯网——每日最新资讯28at.com

const obj = {  name: 'obj',  getName: function() {    return function() {      return this.name;    }  }}const fn = obj.getName();fn();

答案:undefinedQj528资讯网——每日最新资讯28at.com

分析:因为getName函数内部是在全局作用域内执行的,这里的this指向window/global,而window/global没有name属性,所以返回undefined。Qj528资讯网——每日最新资讯28at.com

如果想让内部函数的this也指向obj,可以使用箭头函数或者bind绑定this:Qj528资讯网——每日最新资讯28at.com

const obj = {  name: 'obj',  getName: function() {    return () => {       return this.name;    }  }}

2.闭包的实现与应用

问题:实现一个计数器工厂函数:Qj528资讯网——每日最新资讯28at.com

function createCounter() {  let count = 0;  return function() {    return count++;   }} const counter1 = createCounter();const counter2 = createCounter();counter1(); // 1counter1(); // 2 counter2(); // 1

解析:不同的计数器之所以能独立递增,是因为利用了闭包的特性。createCounter函数创建了一个闭包,在其外层作用域中可以访问变量count,counter1和counter2引用不同的闭包函数实例,从而实现了计数独立性。Qj528资讯网——每日最新资讯28at.com

3.事件循环机制

问:对事件循环机制做一下解释说明。Qj528资讯网——每日最新资讯28at.com

答:事件循环机制主要有以下几个流程:Qj528资讯网——每日最新资讯28at.com

  1. 同步任务在主线程执行,形成执行上下文栈。
  2. 执行栈中同步任务执行完后,系统会读取队列中的异步任务,如Promise.then()、setTimeout、AJAX回调等。
  3. 异步任务会被加入到任务队列中
  4. 清空执行栈后,系统会检查任务队列,如果不为空,则取出第一个任务放到执行栈中执行。
  5. 主线程重复了栈和队列交替执行的过程,从而实现了线程的排队执行。

事件循环允许在同一个线程中交替执行同步任务和异步任务,充分利用CPU资源。这对于支持UI交互和响应性的JavaScript来说很重要。Qj528资讯网——每日最新资讯28at.com

4.Promise对象

问题:实现Promise的简单版本:Qj528资讯网——每日最新资讯28at.com

Promise对象是处理异步事件的异步编程解决方案。Promise对象可以表示异步操作的状态,包括:Qj528资讯网——每日最新资讯28at.com

  • pending
  • fulfilled
  • rejected

Promise对象的实现如下:Qj528资讯网——每日最新资讯28at.com

class MyPromise {  constructor(executor) {    this._state = "pending";    this._value = undefined;    this._reason = undefined;    this._onFulfilledCallbacks = [];    this._onRejectedCallbacks = [];    executor(this.resolve.bind(this), this.reject.bind(this));  }  resolve(value) {    if (this._state !== "pending") {      return;    }    this._state = "fulfilled";    this._value = value;    setTimeout(() => {      for (const callback of this._onFulfilledCallbacks) {        callback(value);      }    });  }  reject(reason) {    if (this._state !== "pending") {      return;    }    this._state = "rejected";    this._reason = reason;    setTimeout(() => {      for (const callback of this._onRejectedCallbacks) {        callback(reason);      }    });  }  then(onFulfilled, onRejected) {    return new MyPromise((resolve, reject) => {      if (this._state === "pending") {        this._onFulfilledCallbacks.push((value) => {          setTimeout(() => {            try {              const result = onFulfilled(value);              resolve(result);            } catch (error) {              reject(error);            }          });        });        this._onRejectedCallbacks.push((reason) => {          setTimeout(() => {            try {              const result = onRejected(reason);              resolve(result);            } catch (error) {              reject(error);            }          });        });      } else {        setTimeout(() => {          try {            if (this._state === "fulfilled") {              const result = onFulfilled(this._value);              resolve(result);            } else {              const result = onRejected(this._reason);              resolve(result);            }          } catch (error) {            reject(error);          }        });      }    });  }  catch(onRejected) {    return this.then(null, onRejected);  }  isFulfilled() {    return this._state === "fulfilled";  }  isRejected() {    return this._state === "rejected";  }}

分析:Qj528资讯网——每日最新资讯28at.com

  1. MyPromise类是一个自定义的Promise类,其构造函数接受一个执行函数作为参数。
  2. 构造函数中的执行函数会被立即执行,并接受两个参数resolve和reject,用于修改Promise的状态。
  3. resolve方法用于将Promise的状态从“pending”修改为“fulfilled”,并将值传递给后续的handler。
  4. reject方法用于将Promise的状态从“pending”修改为“rejected”,并将原因传递给后续的handler。
  5. then方法用于注册一个回调函数,在Promise完成或被拒绝时执行,它接受两个参数onFulfilled和onRejected,分别在Promise完成或被拒绝时调用。
  6. then方法返回一个新的MyPromise实例,以支持链式调用。如果 onFulfilled 或 onRejected 返回一个值,它将被用作下一个 MyPromise 实例的解析值。
  7. catch 方法是 then(null, onRejected) 的简写形式。
  8. isFulfilled 方法用于检查 Promise 是否处于已实现状态。
  9. isRejected 方法用于检查 Promise 是否处于已拒绝状态。

5.类继承实现

原型链是每个对象的一个属性,它指向该对象构造函数的原型对象。一个构造函数的原型对象指向另一个构造函数的原型对象,依此类推。Qj528资讯网——每日最新资讯28at.com

问题:要实现一个 People 类,可以通过构造函数或 new 操作符实例化对象。同时,它有一个继承 Person 类的方法。Person 类有一个 sayHi 方法:Qj528资讯网——每日最新资讯28at.com

class Person {  constructor(name) {    this.name = name;  }  sayHi() {    console.log(`Hello ${this.name}`)  }}class People extends Person {  constructor(name) {    super(name);  }  method() {    console.log('people method')  }}const people = new People('John')people.sayHi() // Hello Johnpeople.method() // people method

分析:通过构造函数调用super继承属性,原型链实现方法继承。Qj528资讯网——每日最新资讯28at.com

6.MVC与MVVM模式

问:简述MVC与MVVM的概念和区别?Qj528资讯网——每日最新资讯28at.com

答:MVC模式中:Qj528资讯网——每日最新资讯28at.com

  1. Model负责管理数据逻辑
  2. View负责显示界面
  3. Controller连接Model与View并传递数据

MVVM模式中:Qj528资讯网——每日最新资讯28at.com

  1. Model负责管理数据逻辑
  2. View负责显示界面
  3. ViewModel作为View与Model的交互代理,将Model同步到View,将View的变化同步回Model。

区别在于:Qj528资讯网——每日最新资讯28at.com

  • MVVM中没有Controller的角色,View直接将数据绑定到ViewModel。
  • ViewModel负责将数据转换成View可以识别的格式,提供给View使用。
  • ViewModel可以将View的变化通知回Model层,实现双向数据绑定。
  • MVVM可以解耦View与Model之间的紧耦合,利于单元测试和组件开发。

7.Ajax实现

问题:实现一个ajax请求函数:Qj528资讯网——每日最新资讯28at.com

ajax('/api/users', {  method: 'GET'  }).then(data => {  console.log(data)})

回答:Qj528资讯网——每日最新资讯28at.com

function ajax(url, options) {  return new Promise((resolve, reject) => {    const xhr = new XMLHttpRequest();    const method = options.method || 'GET';    const headers = options.headers || {};    const body = options.body || null;    const timeout = options.timeout || 0;    xhr.open(method, url);    xhr.onload = () => {      if (xhr.status >= 200 && xhr.status < 300) {        resolve(xhr.response);      } else {        reject(new Error(xhr.statusText));      }    };    xhr.onerror = () => reject(xhr.error);    xhr.ontimeout = () => reject(new Error('Request timeout'));    xhr.timeout = timeout;    for (const [header, value] of Object.entries(headers)) {      xhr.setRequestHeader(header, value);    }    xhr.send(body);  });}

实现一个支持Promise的ajax请求函数:Qj528资讯网——每日最新资讯28at.com

  • 使用XMLHttpRequest对象发送请求
  • 初始化open方法,配置请求方法和url
  • 添加onload和onerror回调函数
  • onload判断状态码是否在200–300范围内resolve,否则reject

onerror直接rejectQj528资讯网——每日最新资讯28at.com

  • 请求成功后resolve返回response,失败后reject报错。
  • 支持options配置请求参数和请求体
  • 返回一个Promise对象,可以使用then/catch进行外部处理

分析:使用Promise封装异步ajax请求,实现同步编程风格。Qj528资讯网——每日最新资讯28at.com

8.JSONP跨域实现

问题:实现一个JSONP跨域请求:Qj528资讯网——每日最新资讯28at.com

jsonp('/api/data', {  params: {    name: 'jsonp'  }  })

回答:Qj528资讯网——每日最新资讯28at.com

function jsonp(url, options) {  return new Promise((resolve, reject) => {    const script = document.createElement('script');    const callbackName = `jsonpCallback_${Date.now()}_${Math.floor(Math.random() * 10000)}`;    const timer = setTimeout(() => {      cleanup();      reject(new Error('JSONP request timeout'));    }, options.timeout || 5000);    function cleanup() {      delete window[callbackName];      clearTimeout(timer);      script.remove();    }    window[callbackName] = function(data) {      cleanup();      resolve(data);    };    options.params = options.params || {};    options.params['callback'] = callbackName;    const paramsArr = Object.keys(options.params).map(key => {      return `${encodeURIComponent(key)}=${encodeURIComponent(options.params[key])}`;    });    script.src = `${url}?${paramsArr.join('&')}`;    script.onerror = () => {      cleanup();      reject(new Error('JSONP request error'));    };    document.body.appendChild(script);  });}

分析:创建脚本节点script.src,设置回调函数callbackName,解析参数并拼接URL,动态插入body中实现JSONP跨域请求,返回Promise接口。Qj528资讯网——每日最新资讯28at.com

9.实现深度克隆

问题:实现一个函数deepClone,实现对象的深度克隆:Qj528资讯网——每日最新资讯28at.com

答案:Qj528资讯网——每日最新资讯28at.com

function deepClone(source, clonedMap) {  clonedMap = clonedMap || new Map();  if (source === null || typeof source !== 'object') {    return source;  }  if (clonedMap.has(source)) {    return clonedMap.get(source);  }  var result;  var type = getType(source);  if (type === 'object' || type === 'array') {    result = type === 'array' ? [] : {};    clonedMap.set(source, result);    for (var key in source) {      if (source.hasOwnProperty(key)) {        result[key] = deepClone(source[key], clonedMap);      }    }  } else {    result = source;  }  return result;}function getType(source) {  return Object.prototype.toString    .call(source)    .replace(/^/[object (.+)/]$/, '$1')    .toLowerCase();}const obj = {  a: 1,  b: {    c: 2  }}const clone = deepClone(obj)

分析:递归实现对象和数组的深度克隆,直接返回基本类型,引用类型递归分层调用深度克隆。Qj528资讯网——每日最新资讯28at.com

10.函数柯里化

问题:实现一个可以把1+2+3加起来的add函数:Qj528资讯网——每日最新资讯28at.com

function add() {     // When executing for the first time, define an array specifically to store all parameters     var _args = [].slice.call(arguments);     // Declare a function internally, use the characteristics of closure to save _args and collect all parameter values     var adder = function () {         var _adder = function() {             // [].push.apply(_args, [].slice.call(arguments));             _args.push(...arguments);             return _adder;         };         //Using the characteristics of implicit conversion, implicit conversion is performed when it is finally executed, and the final value is calculated and returned.         _adder.toString = function () {             return _args.reduce(function (a, b) {                 return a + b;             });         }         return _adder;     }     // return adder.apply(null, _args);     return adder(..._args);}var a = add(1)(2)(3)(4); // f 10var b = add(1, 2, 3, 4); // f 10var c = add(1, 2)(3, 4); // f 10var d = add(1, 2, 3)(4); // f 10// You can use the characteristics of implicit conversion to participate in calculationsconsole.log(a + 10); // 20console.log(b + 20); // 30console.log(c + 30); // 40console.log(d + 40); // 50// You can also continue to pass in parameters, and the result will be calculated using implicit conversion again.console.log(a(10) + 100); // 120console.log(b(10) + 100); // 120console.log(c(10) + 100); // 120console.log(d(10) + 100); // 120//In fact, the add method in Shangli is the curried function of the following function, but we do not use the general formula to convert, but encapsulate it ourselves.function add(...args) {     return args.reduce((a, b) => a + b);}

分析:add函数的柯里化是通过递归调用一个不断接受参数的函数来实现的。Qj528资讯网——每日最新资讯28at.com

11.实现promise.all方法

问题:实现一个myAll方法,类似Promise.all:Qj528资讯网——每日最新资讯28at.com

myAll([  myPromise1,   myPromise2]).then(([res1, res2]) => {  //...})

回答:Qj528资讯网——每日最新资讯28at.com

function myAll(promises) {  return new Promise((resolve, reject) => {    const result = new Array(promises.length);    let count = 0;    promises.forEach((p, index) => {      p.then(res => {        result[index] = res;        count++;        if (count === promises.length) {          resolve(result);        }      })      .catch(reject);    });  });}

分析:利用Promise.all原理,通过计数器和结果数组同步Promise状态。Qj528资讯网——每日最新资讯28at.com

12.实现Instanceof

问题:实现一个instanceof操作符Qj528资讯网——每日最新资讯28at.com

答案:Qj528资讯网——每日最新资讯28at.com

function instanceof(left, right) {  if (arguments.length !== 2) {    throw new Error("instanceof requires exactly two arguments.");  }  if (left === null) {    return false;  }  if (typeof left !== "object") {    return false;  }  let proto = Object.getPrototypeOf(left);  while (proto !== null) {    if (right.prototype === proto) {      return true;    }    proto = Object.getPrototypeOf(proto);  }  return false;}

以上代码用于判断一个对象是否是另一个对象的实例。Qj528资讯网——每日最新资讯28at.com

JavaScript 中的 instanceof 运算符可用于判断一个对象是否是另一个对象的实例。但是,instanceof 运算符有一些限制,例如:Qj528资讯网——每日最新资讯28at.com

  1. instanceof 运算符只能确定与原型链直接相连的对象。
  2. instanceof 运算符无法检测具有循环原型链的对象。
  3. 因此,以上代码提供了一个更通用的 instanceof 函数,可以确定任意两个对象之间的关系。
  4. 该函数的实现原理是:
  5. instanceof 函数接收两个参数:left 和 right。
  6. 首先,代码检查参数数量是否为 2,如果不是,则抛出错误。
  7. 接下来,代码检查左操作数 left 是否为 null。如果是,则直接返回 false,因为 null 不能是任何对象的实例。
  8. 然后,代码检查左操作数 left 的类型是否为对象。如果不是,则直接返回false,因为只有对象才能是构造函数的实例。
  9. 接下来,代码使用Object.getPrototypeOf()获取左操作数left的原型,并将其赋值给变量proto。
  10. 在循环中,代码继续遍历proto的原型链,直到proto为null。
  11. 在循环中,代码检查右操作数right的原型是否等于当前的proto,如果相等,则表示左操作数left是右操作数right的实例,返回true。
  12. 如果在循环结束时没有找到匹配的原型,即proto为null,则表示左操作数left不是右操作数right的实例,返回false。

该函数可以在以下场景中使用:Qj528资讯网——每日最新资讯28at.com

  1. 判断一个对象是否是另一个对象的实例。
  2. 判断一个对象是否从另一个对象继承而来。
  3. 判断一个对象是否属于某个特定的类。

13. 实现 debounce 防抖功能

问题:实现 debounce 防抖功能Qj528资讯网——每日最新资讯28at.com

答案:Qj528资讯网——每日最新资讯28at.com

function debounce(fn, delay = 500) {   let timer;   return function(...args) {     clearTimeout(timer);     timer = setTimeout(() => {       fn.apply(this, args);     }, delay);     // Return to clear function     return () => {       clearTimeout(timer);     }   }}// useconst debouncedFn = debounce(fn, 1000);const cancel = debouncedFn();// clearcancel();


Qj528资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-100982-0.html13个 JavaScript 面试难题及代码实现

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Python excel、word报表生成神器

下一篇: 大厂内幕:SpringBoot项目为何弃用Tomcat,转投Undertow怀抱?

标签:
  • 热门焦点
  • 六大权益!华为8月服务日开启:手机免费贴膜、维修免人工费

    六大权益!华为8月服务日开启:手机免费贴膜、维修免人工费

    8月5日消息,一年一度的华为开发者大会2023(Together)日前在松山湖拉开帷幕,与此同时,华为8月服务日也式开启,到店可享六大专属权益。华为用户可在华为商城Ap
  • 摸鱼心法第一章——和配置文件说拜拜

    摸鱼心法第一章——和配置文件说拜拜

    为了能摸鱼我们团队做了容器化,但是带来的问题是服务配置文件很麻烦,然后大家在群里进行了“亲切友好”的沟通图片图片图片图片对比就对比,简单对比下独立配置中心和k8s作为配
  • K8S | Service服务发现

    K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • 谷歌KDD'23工作:如何提升推荐系统Ranking模型训练稳定性

    谷歌KDD'23工作:如何提升推荐系统Ranking模型训练稳定性

    谷歌在KDD 2023发表了一篇工作,探索了推荐系统ranking模型的训练稳定性问题,分析了造成训练稳定性存在问题的潜在原因,以及现有的一些提升模型稳定性方法的不足,并提出了一种新
  • 多线程开发带来的问题与解决方法

    多线程开发带来的问题与解决方法

    使用多线程主要会带来以下几个问题:(一)线程安全问题  线程安全问题指的是在某一线程从开始访问到结束访问某一数据期间,该数据被其他的线程所修改,那么对于当前线程而言,该线程
  • 2023年,我眼中的字节跳动

    2023年,我眼中的字节跳动

    此时此刻(2023年7月),字节跳动从未上市,也从未公布过任何官方的上市计划;但是这并不妨碍它成为中国最受关注的互联网公司之一。从2016-17年的抖音强势崛起,到2018年的&ldquo;头腾
  • 大厂卷向扁平化

    大厂卷向扁平化

    来源:新熵作者丨南枝 编辑丨月见大厂职级不香了。俗话说,兵无常势,水无常形,互联网企业调整职级体系并不稀奇。7月13日,淘宝天猫集团启动了近年来最大的人力制度改革,目前已形成一
  • iQOO 11S或7月上市:搭载“鸡血版”骁龙8Gen2 史上最强5G Soc

    iQOO 11S或7月上市:搭载“鸡血版”骁龙8Gen2 史上最强5G Soc

    去年底,iQOO推出了“电竞旗舰”iQOO 11系列,作为一款性能强机,iQOO 11不仅全球首发2K 144Hz E6全感屏,搭载了第二代骁龙8平台及144Hz电竞屏,同时在快充
  • 苹果MacBook Pro 2021测试:仍不支持平滑滚动

    苹果MacBook Pro 2021测试:仍不支持平滑滚动

    据10月30日9to5 Mac 消息报道,苹果新的 14 英寸和 16 英寸 MacBook Pro 2021 上市后获得了不错的评价,亮点包括行业领先的性能,令人印象深刻的电池续航,精美丰
Top