2024年Web前端最全【前端面试题】2024年 国庆 前端面试真题之JS篇,满满干货

React

  • 介绍一下react

  • React单项数据流

  • react生命周期函数和react组件的生命周期

  • react和Vue的原理,区别,亮点,作用

  • reactJs的组件交流

  • 有了解过react的虚拟DOM吗,虚拟DOM是怎么对比的呢

  • 项目里用到了react,为什么要选择react,react有哪些好处

  • 怎么获取真正的dom

  • 选择react的原因

  • react的生命周期函数

  • setState之后的流程

  • react高阶组件知道吗?

  • React的jsx,函数式编程

  • react的组件是通过什么去判断是否刷新的

  • 如何配置React-Router

  • 路由的动态加载模块

  • Redux中间件是什么东西,接受几个参数

  • redux请求中间件如何处理并发

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

你可能会用到 void 操作符:void function foo(){ }();。但是,这种做法是有问题的。表达式的值是 undefined,所以如果你的 IIFE 有返回值,不要用这种做法。例如

const foo = void (function bar() {
  return 'foo';
})();

console.log(foo); // undefined

null、undefined和未声明变量之间有什么区别?如何检查判断这些状态值?

当你没有提前使用 var、let 或 const 声明变量,就为一个变量赋值时,该变量是未声明变量(undeclared variables)。未声明变量会脱离当前作用域,成为全局作用域下定义的变量。在严格模式下,给未声明的变量赋值,会抛出 ReferenceError 错误。和使用全局变量一样,使用未声明变量也是非常不好的做法,应当尽可能避免。要检查判断它们,需要将用到它们的代码放在 try/catch 语句中。

function foo() {
  x = 1; // 在严格模式下,抛出 ReferenceError 错误
}

foo();
console.log(x); // 1

当一个变量已经声明,但没有赋值时,该变量的值是 undefined。如果一个函数的执行结果被赋值给一个变量,但是这个函数却没有返回任何值,那么该变量的值是 undefined。要检查它,需要使用严格相等(===);或者使用 typeof,它会返回 'undefined' 字符串。请注意,不能使用非严格相等(==)来检查,因为如果变量值为 null,使用非严格相等也会返回 true

var foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === 'undefined'); // true

console.log(foo == null); // true. 错误,不要使用非严格相等!

function bar() {}
var baz = bar();
console.log(baz); // undefined

null 只能被显式赋值给变量。它表示空值,与被显式赋值 undefined 的意义不同。要检查判断 null 值,需要使用严格相等运算符。请注意,和前面一样,不能使用非严格相等(==)来检查,因为如果变量值为 undefined,使用非严格相等也会返回 true

var foo = null;
console.log(foo === null); // true

console.log(foo == undefined); // true. 错误,不要使用非严格相等!

作为一种个人习惯,我从不使用未声明变量。如果定义了暂时没有用到的变量,我会在声明后明确地给它们赋值为 null

什么是闭包(closure),为什么使用闭包?

闭包是函数和声明该函数的词法环境的组合。词法作用域中使用的域,是变量在代码中声明的位置所决定的。闭包是即使被外部函数返回,依然可以访问到外部(封闭)函数作用域的函数。

为什么使用闭包?

  • 利用闭包实现数据私有化或模拟私有方法。这个方式也称为模块模式(module pattern)。
  • 部分参数函数(partial applications)柯里化(currying).

请说明.forEach循环和.map()循环的主要区别,它们分别在什么情况下使用?

为了理解两者的区别,我们看看它们分别是做什么的。

forEach
  • 遍历数组中的元素。
  • 为每个元素执行回调。
  • 无返回值。
const a = [1, 2, 3];
const doubled = a.forEach((num, index) => {
  // 执行与 num、index 相关的代码
});

// doubled = undefined

map
  • 遍历数组中的元素
  • 通过对每个元素调用函数,将每个元素“映射(map)”到一个新元素,从而创建一个新数组。
const a = [1, 2, 3];
const doubled = a.map((num) => {
  return num * 2;
});

// doubled = [2, 4, 6]

.forEach 和 .map() 的主要区别在于 .map() 返回一个新的数组。如果你想得到一个结果,但不想改变原始数组,用 .map()。如果你只需要在数组上做迭代修改,用 forEach

匿名函数的典型应用场景是什么?

匿名函数可以在 IIFE 中使用,来封装局部作用域内的代码,以便其声明的变量不会暴露到全局作用域。

(function () {
  // 一些代码。
})();

匿名函数可以作为只用一次,不需要在其他地方使用的回调函数。当处理函数在调用它们的程序内部被定义时,代码具有更好地自闭性和可读性,可以省去寻找该处理函数的函数体位置的麻烦。

setTimeout(function () {
  console.log('Hello world!');
}, 1000);

匿名函数可以用于函数式编程或 Lodash(类似于回调函数)。

const arr = [1, 2, 3];
const double = arr.map(function (el) {
  return el * 2;
});
console.log(double); // [2, 4, 6]

.call和.apply有什么区别?

.call 和 .apply 都用于调用函数,第一个参数将用作函数内 this 的值。然而,.call 接受逗号分隔的参数作为后面的参数,而 .apply 接受一个参数数组作为后面的参数。一个简单的记忆方法是,从 call 中的 C 联想到逗号分隔(comma-separated),从 apply 中的 A 联想到数组(array)。

function add(a, b) {
  return a + b;
}

console.log(add.call(null, 1, 2)); // 3
console.log(add.apply(null, [1, 2])); // 3

请说明Function.prototype.bind的用法。

摘自MDN:

bind()方法创建一个新的函数, 当被调用时,将其 this 关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。

根据我的经验,将 this 的值绑定到想要传递给其他函数的类的方法中是非常有用的。在 React 组件中经常这样做。

请尽可能详细地解释 Ajax。

Ajax(asynchronous JavaScript and XML)是使用客户端上的许多 Web 技术,创建异步 Web 应用的一种 Web 开发技术。借助 Ajax,Web 应用可以异步(在后台)向服务器发送数据和从服务器检索数据,而不会干扰现有页面的显示和行为。通过将数据交换层与表示层分离,Ajax 允许网页和扩展 Web 应用程序动态更改内容,而无需重新加载整个页面。实际上,现在通常将 XML 替换为 JSON,因为 JavaScript 对 JSON 有原生支持优势。

XMLHttpRequest API 经常用于异步通信。此外还有最近流行的 fetch API

使用 Ajax 的优缺点分别是什么?
优点
  • 交互性更好。来自服务器的新内容可以动态更改,无需重新加载整个页面。
  • 减少与服务器的连接,因为脚本和样式只需要被请求一次。
  • 状态可以维护在一个页面上。JavaScript 变量和 DOM 状态将得到保持,因为主容器页面未被重新加载。
  • 基本上包括大部分 SPA 的优点。
缺点
  • 动态网页很难收藏。
  • 如果 JavaScript 已在浏览器中被禁用,则不起作用。
  • 有些网络爬虫不执行 JavaScript,也不会看到 JavaScript 加载的内容。
  • 基本上包括大部分 SPA 的缺点

请说明 JSONP 的工作原理,它为什么不是真正的 Ajax?

JSONP(带填充的 JSON)是一种通常用于绕过 Web 浏览器中的跨域限制的方法,因为 Ajax 不允许跨域请求。

JSONP 通过 <script> 标签发送跨域请求,通常使用 callback 查询参数,例如:https://example.com?callback=printData。 然后服务器将数据包装在一个名为 printData 的函数中并将其返回给客户端。

<!-- https://mydomain.com -->
<script>
  function printData(data) {
    console.log(`My name is ${data.name}!`);
  }
</script>

<script src="https://example.com?callback=printData"></script>

// 文件加载自 https://example.com?callback=printData
printData({name: 'Yang Shun'});

客户端必须在其全局范围内具有 printData 函数,并且在收到来自跨域的响应时,该函数将由客户端执行。

JSONP 可能具有一些安全隐患。由于 JSONP 是纯 JavaScript 实现,它可以完成 JavaScript 所能做的一切,因此需要信任 JSONP 数据的提供者。

现如今,跨来源资源共享(CORS) 是推荐的主流方式,JSONP 已被视为一种比较 hack 的方式。

请解释变量提升(hoisting)。

变量提升(hoisting)是用于解释代码中变量声明行为的术语。使用 var 关键字声明或初始化的变量,会将声明语句“提升”到当前作用域的顶部。 但是,只有声明才会触发提升,赋值语句(如果有的话)将保持原样。我们用几个例子来解释一下。

// 用 var 声明得到提升
console.log(foo); // undefined
var foo = 1;
console.log(foo); // 1

// 用 let/const 声明不会提升
console.log(bar); // ReferenceError: bar is not defined
let bar = 2;
console.log(bar); // 2

函数声明会使函数体提升,但函数表达式(以声明变量的形式书写)只有变量声明会被提升。

// 函数声明
console.log(foo); // [Function: foo]
foo(); // 'FOOOOO'
function foo() {
  console.log('FOOOOO');
}
console.log(foo); // [Function: foo]

// 函数表达式
console.log(bar); // undefined
bar(); // Uncaught TypeError: bar is not a function
var bar = function () {
  console.log('BARRRR');
};
console.log(bar); // [Function: bar]

请描述事件冒泡。

当一个事件在 DOM 元素上触发时,如果有事件监听器,它将尝试处理该事件,然后事件冒泡到其父级元素,并发生同样的事情。最后直到事件到达祖先元素。事件冒泡是实现事件委托的原理(event delegation)。

=的区别是什么

== 是抽象相等运算符,而 === 是严格相等运算符。== 运算符是在进行必要的类型转换后,再比较。=== 运算符不会进行类型转换,所以如果两个值不是相同的类型,会直接返回 false 。使用 == 时,可能发生一些特别的事情,例如:

1 == '1'; // true
1 == [1]; // true
1 == true; // true
0 == ''; // true
0 == '0'; // true
0 == false; // true

我的建议是从不使用 == 运算符,除了方便与 null 或 undefined 比较时,a == null 如果 a 为 null 或 undefined 将返回 true

var a = null;
console.log(a == null); // true
console.log(a == undefined); // true

**前端面试题库 (**面试必备)推荐:★★★★★

地址:前端面试题库

请解释关于 JavaScript 的同源策略。

同源策略可防止 JavaScript 发起跨域请求。源被定义为 URI、主机名和端口号的组合。此策略可防止页面上的恶意脚本通过该页面的文档对象模型,访问另一个网页上的敏感数据。

你对 Promises 及其 polyfill 的掌握程度如何?

掌握它的工作原理。Promise 是一个可能在未来某个时间产生结果的对象:操作成功的结果或失败的原因(例如发生网络错误)。 Promise 可能处于以下三种状态之一:fulfilledrejected 或 pending。 用户可以对 Promise 添加回调函数来处理操作成功的结果或失败的原因。

一些常见的 polyfill 是 $.deferred、Q 和 Bluebird,但不是所有的 polyfill 都符合规范。ES2015 支持 Promises,现在通常不需要使用 polyfills。

Promise代替回调函数有什么优缺点?

优点
  • 避免可读性极差的回调地狱。
  • 使用.then()编写的顺序异步代码,既简单又易读。
  • 使用Promise.all()编写并行异步代码变得很容易。
缺点
  • 轻微地增加了代码的复杂度(这点存在争议)。
  • 在不支持 ES2015 的旧版浏览器中,需要引入 polyfill 才能使用。

请解释同步和异步函数之间的区别。

同步函数阻塞,而异步函数不阻塞。在同步函数中,语句完成后,下一句才执行。在这种情况下,程序可以按照语句的顺序进行精确评估,如果其中一个语句需要很长时间,程序的执行会停滞很长时间。

异步函数通常接受回调作为参数,在调用异步函数后立即继续执行下一行。回调函数仅在异步操作完成且调用堆栈为空时调用。诸如从 Web 服务器加载数据或查询数据库等重负载操作应该异步完成,以便主线程可以继续执行其他操作,而不会出现一直阻塞,直到费时操作完成的情况(在浏览器中,界面会卡住)。

什么是事件循环?调用堆栈和任务队列之间有什么区别?

事件循环是一个单线程循环,用于监视调用堆栈并检查是否有工作即将在任务队列中完成。如果调用堆栈为空并且任务队列中有回调函数,则将回调函数出队并推送到调用堆栈中执行。

使用let、var和const创建变量有什么区别?

用 var 声明的变量的作用域是它当前的执行上下文,它可以是嵌套的函数,也可以是声明在任何函数外的变量。let 和 const 是块级作用域,意味着它们只能在最近的一组花括号(function、if-else 代码块或 for 循环中)中访问。

function foo() {
  // 所有变量在函数中都可访问
  var bar = 'bar';
  let baz = 'baz';
  const qux = 'qux';

  console.log(bar); // bar
  console.log(baz); // baz
  console.log(qux); // qux
}

console.log(bar); // ReferenceError: bar is not defined
console.log(baz); // ReferenceError: baz is not defined
console.log(qux); // ReferenceError: qux is not defined

if (true) {


### 最后

![](https://img-blog.csdnimg.cn/img_convert/f20f0910be6054bb181fa30ba9c36890.webp?x-oss-process=image/format,png)

![](https://img-blog.csdnimg.cn/img_convert/437f08133348f99ddabbb49ef1dd6a5a.webp?x-oss-process=image/format,png)

**资料过多,篇幅有限**

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/topics/618166371)**

>自古成功在尝试。不尝试永远都不会成功。勇敢的尝试是成功的一半。



z
  console.log(qux); // qux
}

console.log(bar); // ReferenceError: bar is not defined
console.log(baz); // ReferenceError: baz is not defined
console.log(qux); // ReferenceError: qux is not defined

if (true) {


### 最后

[外链图片转存中...(img-mWFPnOmL-1714837598127)]

[外链图片转存中...(img-6071LVd5-1714837598128)]

**资料过多,篇幅有限**

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://bbs.csdn.net/topics/618166371)**

>自古成功在尝试。不尝试永远都不会成功。勇敢的尝试是成功的一半。



  • 11
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值