前端高频面试题---js篇(四)

1. javascript中的闭包

在 JavaScript 中,闭包是一种函数,它有权访问其自己的范围,外部函数的范围,以及全局变量的范围。换句话说,闭包可以访问在创建它的函数范围中定义的所有变量和函数。此外,闭包还可以访问它自己的范围(即闭包的变量和函数),外部函数的变量和函数,以及全局变量和函数。

闭包是由函数创建的,并且可以由函数返回。因此,闭包提供了一种将函数和变量封装在一起的方式,从而允许我们创建私有变量和函数,这些变量和函数只能通过公共接口(即闭包的返回值)进行访问。

闭包在许多情况下都非常有用,例如在创建私有变量和函数时,在创建高阶函数时,以及在实现模块化代码时。

以下是一个简单的闭包示例:

javascript 复制代码
function outerFunction(outerVariable) {
  return function innerFunction(innerVariable) {
    console.log('outerVariable:', outerVariable);
    console.log('innerVariable:', innerVariable);
  };
}

const newFunction = outerFunction('outside');
newFunction('inside'); // logs: outerVariable: outside, innerVariable: inside

在这个例子中,outerFunction是一个外部函数,它接受一个参数outerVariable。这个函数返回一个内部函数innerFunction,这个内部函数可以访问在outerFunction范围内定义的变量outerVariable。当我们调用outerFunction('outside')时,它返回innerFunction,我们可以将这个返回的函数存储在newFunction中。然后,当我们调用newFunction('inside')时,它会打印出outerVariable: outsideinnerVariable: inside。这是因为innerFunction可以访问outerVariable,并且我们可以将innerVariable传递给它。这就是闭包的基本概念。

2. javascript中的事件委托

事件委托是一种在父元素上设置事件监听器,以便在一个或更多的子元素上触发特定的事件时运行的技术。在事件委托中,事件处理程序是在祖先元素上设置的,而不是直接在要触发事件的元素上设置的。当事件在派生元素上触发时,事件会冒泡到祖先元素,然后触发在祖先元素上设置的事件处理程序。

在 JavaScript 中,可以使用 addEventListener 方法实现事件委托。下面是一个简单的例子:

javascript 复制代码
document.querySelector("#parent").addEventListener("click", function(event) {
  // 检查事件的目标元素是否是 .child 元素
  if (event.target.classList.contains("child")) {
    console.log("Child element clicked");
  }
});

在这个例子中,我们在 id 为 "parent" 的元素上设置了一个 click 事件监听器。当点击事件冒泡到 "parent" 元素时,它会运行这个监听器。在监听器中,我们检查事件的目标元素是否是 class 为 "child" 的元素。如果是,那么我们就在控制台中打印一条消息。这样,即使我们点击的是 "parent" 元素中的任何地方,只要这个地方包含了一个 class 为 "child" 的子元素,我们就能在控制台中看到消息。

3. javascript ES5和ES6的区别有哪些?ES6新增了什么?

ES5 和 ES6 是 JavaScript 的两个重要版本,它们之间有很多重要的区别。以下是一些主要的区别:

  1. 新增特性

    • ES6 引入了许多新特性,如 let 和 const 关键字(块级作用域)、箭头函数(Arrow Functions)、模块化(使用 import 和 export 代替了 ES5 的函数库)、解构赋值、Promise、生成器、Map、Set、Proxy、Reflect 等。
    • ES6 还提供了更强大的原型系统,允许你通过 Symbol 来创建唯一的键,MapSet 数据结构使用 Symbol 作为键。
  2. 语法糖

    • ES6 引入了一些语法糖,例如类(Class)、模板字符串、默认、剩余、展开(Rest/Spread)等。
  3. 性能提升

    • ES6 相比于 ES5 在某些操作上提供了更好的性能,例如创建对象和数组。
  4. 移除特性

    • ES6 移除了一些 ES5 的特性,例如 Document Object Model (DOM) 脚本、宿主对象和宿主方法(如 encodeURIComponenteval)等。
  5. 全局变量

    • ES6 通过 importexport 关键字引入了模块化的概念,从而限制了全局变量的使用。在 ES6 中,尽量避免使用全局变量,以防止命名冲突和其他相关的问题。
  6. 错误处理

    • ES6 引入了新的错误处理机制,包括 try/catch 语句和新的 Error 类型。
  7. 支持的浏览器

    • ES6 相比于 ES5 支持更多的浏览器,包括较旧的浏览器版本。然而,这取决于具体的浏览器和其版本。
  8. 工具和库

    • ES6 提供了许多新的工具和库,如 Babel 和 webpack 等,以帮助开发者更有效地开发和管理代码。
  9. 异步函数

    • ES6 中的 async/await 为异步编程提供了更好的支持,使得异步代码看起来更像同步代码。
  10. 模块系统改进

    • ES6 中的模块系统更加完善和灵活,允许导入和导出任意数量的模块和命名空间。此外,它还解决了 ES5 中模块加载和链接的问题。
  11. 其他改进

    • ES6 还对许多其他方面进行了改进,如箭头函数、解构赋值、默认参数等。

4. javascript中改变函数内部 this 指针的指向函数(bind,apply,call的区别),内在分别是如何实现的?

在 JavaScript 中,this 是一个非常重要的关键字,它用于表示函数被调用时的上下文。在函数被调用时,this 的值会被设定为调用函数的对象。但是,如果你在函数中直接使用 this,那么 this 的值实际上是全局对象(在浏览器中是 window)。为了避免这种情况,你可以使用 bindapplycall 方法来改变函数的 this 指针的指向。

这三个方法的区别在于它们如何处理函数中的参数。

  1. bind()

bind() 方法创建一个新的函数,该函数的 this 值是由提供的值决定的。此外,bind() 可以创建一个函数,该函数在调用时不执行任何操作(也就是说,它不返回任何值)。它只改变了 this 的值。

javascript 复制代码
function MyFunction() {
    console.log(this);
}

let obj = {name: 'My Object'};
let myFunctionBound = MyFunction.bind(obj); // 创建一个新的函数,其 this 值被设定为 obj
myFunctionBound(); // 输出:{name: 'My Object'}
  1. apply()

apply() 方法类似于 call() 方法,但是有一个区别:apply() 将函数的参数作为数组传递,而 call() 将参数作为单独的参数传递。这意味着你可以将任意数量的参数传递给 apply(),而无需明确地列出它们。

javascript 复制代码
function MyFunction(arg1, arg2) {
    console.log(this, arg1, arg2);
}

let obj = {name: 'My Object'};
let args = ['hello', 'world'];
MyFunction.apply(obj, args); // 输出:{name: 'My Object'} hello world
  1. call()

call() 方法与 apply() 方法类似,都可以改变函数的 this 指针的指向。但是,call() 方法将参数作为单独的参数传递,而不是作为数组。这意味着你必须明确地列出所有参数。

javascript 复制代码
function MyFunction(arg1, arg2) {
    console.log(this, arg1, arg2);
}

let obj = {name: 'My Object'};
let args = ['hello', 'world'];
MyFunction.call(obj, args[0], args[1]); // 输出:{name: 'My Object'} hello world

5. javascript中的节流和防抖

在JavaScript中,"节流"(throttling)和"防抖"(debouncing)是两个常用的概念,它们都可以用来优化函数性能和减少不必要的执行。

防抖(Debouncing)

防抖的基本思想是:在一段时间内,如果持续触发某个事件,则只有当停止触发后,才去执行一次事件处理函数。

例如,在用户进行键盘输入的时候,我们可能希望只有在用户停止输入一段时间后再去执行某些操作,而不是每输入一个字符就执行一次。下面是一个简单的防抖函数:

javascript 复制代码
function debounce(func, wait) {
  let timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, arguments), wait);
  }
}

节流(Throttling)

节流的基本思想是:在一段时间内,如果持续触发某个事件,则只执行一次事件处理函数,并忽略其他所有触发。

例如,在滚动页面的时候,我们可能希望只在最后一次滚动后的一段时间内执行某些操作,而不是每次滚动都执行。下面是一个简单的节流函数:

javascript 复制代码
function throttle(func, limit) {
  let inThrottle;
  return function() {
    if (!inThrottle) {
      func.apply(this, arguments);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  }
}

以上,就是JavaScript中的防抖和节流的概念以及它们的实现。

相关推荐
Redstone Monstrosity9 分钟前
字节二面
前端·面试
东方翱翔16 分钟前
CSS的三种基本选择器
前端·css
Fan_web39 分钟前
JavaScript高级——闭包应用-自定义js模块
开发语言·前端·javascript·css·html
yanglamei19621 小时前
基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue
前端·数据库·flask
千穹凌帝1 小时前
SpinalHDL之结构(二)
开发语言·前端·fpga开发
dot.Net安全矩阵1 小时前
.NET内网实战:通过命令行解密Web.config
前端·学习·安全·web安全·矩阵·.net
Hellc0071 小时前
MacOS升级ruby版本
前端·macos·ruby
UestcXiye1 小时前
面试算法题精讲:求数组两组数差值和的最大值
面试·数据结构与算法·前后缀分解
严格格1 小时前
三范式,面试重点
数据库·面试·职场和发展
前端西瓜哥1 小时前
贝塞尔曲线算法:求贝塞尔曲线和直线的交点
前端·算法