前言
笔者最近在准备春招中,整理了一些前端高频考点面试题给各位大佬参考,这些天我会一直更新这些高频考点题,如果对你有些许帮助的话,欢迎点赞👍,收藏 ❤️,加关注😍,感谢各位。
10.普通函数与箭头函数的区别
-
箭头函数没有自己的
this
,他的this
基于作用域链继承,所以箭头函数的this
的指向在它定义时就已经确定了,之后不会改变
-
箭头函数更简洁,比较适合匿名函数或回调函数,如果没有参数,直接写一个空括号就行,如果只有一个参数,也可以省去参数的括号。
-
普通函数可以当做构造函数使用,用new来实例化对象,而箭头函数没有自己的this,所以不能当做构造函数来用
-
箭头函数没有
prototype
-
普通函数可以直接使用
arguments
对象获取所有参数;箭头函数不支持arguments
,但可以用剩余参数...args
代替。
11.call、apply、bind的区别
-
call
:会立即执行函数,并允许你指定this
的值,同时可以传递一个参数列表给被调用的函数。 -
apply
:与call
类似,也是立即调用函数并指定this
的值,但接受的参数是一个数组。 -
bind
:不会立即调用函数,而是返回一个新的函数,这个新函数的this
值被永久绑定到了bind
的第一个参数上,无论之后如何调用这个新函数,其this
值都不会改变。
简而言之,call
和apply
用于立即执行函数,区别在于参数传递的方式;而bind
用于创建一个具有不同this
值的新函数,而不立即执行。
12.深拷贝和浅拷贝的区别
-
深拷贝:
- 会递归的复制所有对象的
所有层级
,包括所有嵌套的对象和数组,这就意味着这些嵌套的对象或数组也会生成新的副本,而不会与原对象共享引用,所以修改拷贝对象不会影响
原对象,反之亦如此 - 可以使用
JSON.parse(JSON.stringify(obj)
进行简单深拷贝,但这种方法有局限性,比如无法处理函数、循环引用等
- 会递归的复制所有对象的
-
浅拷贝:只拷贝一层对象,如果是基本数据类型的话,直接复制值,如果是引用类型,则复制其引用地址
13.数组中有哪些方法
js中数组提供了许多内置方法,这里就列举一些常用的数组方法
-
增删改查类:
push()
:在数组末尾添加一个或多个元素,并返回新的长度。pop()
:删除并返回数组的最后一个元素。unshift()
:在数组开头添加一个或多个元素,并返回新的长度。shift()
:删除并返回数组的第一个元素。splice()
:可以用于添加新项、移除现有项或同时进行这两种操作。
-
合并与分割:
concat()
:连接两个或更多的数组,不改变原数组,返回新数组。join()
:将数组的所有元素连接成一个字符串,默认用逗号分隔。split()
:虽然不是数组的方法,但常用来从字符串生成数组(根据指定分隔符)。
-
遍历与搜索:
forEach()
:对数组每个元素执行一次提供的函数。map()
:创建一个新数组,其结果是该数组中的每个元素都调用一次提供的函数。filter()
:创建一个新数组,包含通过所提供函数实现的测试的所有元素。find()
:返回数组中满足提供的测试函数的第一个元素的值。indexOf()
:返回找到的第一个匹配项的索引,如果未找到则返回-1。lastIndexOf()
:从后向前搜索,其他同indexOf()
。
-
排序与反转:
sort()
:对数组元素进行排序,支持传入比较函数来自定义排序规则。reverse()
:颠倒数组中元素的位置顺序。
-
归约:
reduce()
:对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值。reduceRight()
:类似reduce()
,但是从数组的末尾开始。
-
检查与测试:
every()
:测试数组的所有元素是否都通过了测试函数的实现。some()
:测试数组中是否有至少一个元素通过了测试函数的实现。
-
其他:
includes()
:判断数组是否包含某个指定的值,返回布尔值。flat()
:按深度递归遍历数组,并将所有元素和子数组元素合并为一个新数组。flatMap()
:结合map()
和flat()
,首先使用映射函数映射每个元素,然后将结果扁平化。
14.对promise的理解
promise
是异步编程的一种解决方案,解决了回调地狱,它有三个状态pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)- promise的实例有两个过程,
pending->fulfilled:resovled
(已完成),
pending->rejected
:(已拒绝),一旦从进行状态变为其他状态就永远不能更改状态了。 - 链式调用:通过多次调用
then
方法可以创建一个promise链,每个then方法返回一个新的promise
,允许前一个promise的结果作为下一个promise的输入,这使得异步代码更加线性和易于管理 - 在promise链中,如果任何一个promise被拒绝了,后序的then方法都将被跳过,直到遇到一个catch方法来处理错误
15.什么是作用域和作用域链
作用域是定义变量可访问范围的一套规则,按查找规则可以分为:
- 词法作用域:在代码编写时确定,由代码嵌套结构决定,变量查找是从当前作用域向上查找,直到全局作用域
- 动态作用域:变量查找是在函数调用时根据调用栈来决定,而不是代码结构,比如
this
。
按作用域范围可以分为:
- 全局作用域:在代码最外层定义的变量拥有全局作用域,它们可以在代码的任意地方被访问。
- 函数作用域:在函数内部定义的变量拥有函数作用域,它们只在该函数内部及嵌套的函数内部可见
- 块级作用域:es6引入的,
let
和const
声明的变量具有块级作用域,它只在定义它们的代码块(如条件语句或循环)内有效。
作用域链决定了变量查找的顺序,在一个特定的作用域中尝试访问一个变量时,js引擎先会在当前作用域中查找变量,如果找不到则会沿着作用域链向上一级作用域查找,直到找到该变量或者到达全局作用域为止,如果到全局作用域都没有找到,会抛出未定义错误。
16.什么是尾调用及尾调用的优势
- 尾调用指的是函数的最后一步调用另一个函数,并且这个调用的结果直接被当前函数返回。
- 在执行尾调用时,当前函数的状态已经不再需要了,因此理论上可以重用当前函数的栈帧来执行被调用的函数,而不是为新的函数调用分配新的栈帧。
- 通过重新调用栈帧,可以避免因为深度递归而导致的栈帧出错误。
- 由于不用为每一个递归调用新的栈帧,减少了内存分配和释放的操作,从而提升性能。
- ES6的尾调用优化只在
严格模式
下开启,正常模式是无效的
17.for、for...of和for...in 的区别
for
:最基础的循环结构,适用于各种情况,包括数组和对象,但需要手动控制循环变量和条件,可以精准控制循环次数。for...in
:用于遍历对象的可枚举性(包括继承的属性),获取的是对象的键名,不适用于数组,不能保证属性的顺序。for...of
:用于遍历可迭代对象的值,获取的是对象的键值,适用于数组、字符串、Set
、Map
等,但不适用普通对象,可以保证遍历的顺序
18.什么是函数柯里化
函数柯里化是将一个接受多个参数的函数转变为多个只接受一个参数的函数,比如说给定一个函数f
接受参数a
、b
、c
,即f(a,b,c)
,柯里化会将其转换成一系列嵌套函数的形式,如f(a)(b)(c)
,每个函数返回一个新的函数,直到所有参数都被提供,最终返回结果