JavaScript 中的 this - 动态绑定和实现细节

在 JavaScript 中,this 关键字是一个非常重要的概念。它代表了当前函数执行的上下文环境,可以帮助我们方便地操作 DOM 元素、调用对象方法等等。但是,this 在实际开发中经常会出现一些意想不到的问题,因为它的值取决于函数被调用的方式以及上下文环境的不同,而这些情况可能非常复杂。因此,在本篇文章中,我们将详细讨论 JavaScript 中的 this,并分析其动态绑定和实现细节,以及如何避免一些常见的问题。

1、this 的动态绑定

在 JavaScript 中,this 的值是动态绑定的,也就是说在执行函数时才确定它的值。一般情况下,this 的值取决于函数的调用方式,有以下几种情况。

1.1、全局对象

当函数不作为对象的方法调用时,this 指向全局对象(浏览器中为 window 对象,在 Node.js 环境中为 global 对象)。

例如:

-------- ------ -
  ---------------- --- -------- -- ----
-

------- -- -- ----

在上面的例子中,当我们不给函数 test 指定上下文对象时,test 函数中的 this 指向的是全局对象 window,因此输出 true。

1.2、对象的方法调用

当函数作为对象的方法调用时,this 指向该对象。

例如:

--- --- - -
  -- --
  -- ---------- -
    --------------------
  --
--

-------- -- -- -

在上面的例子中,函数 b 是对象 obj 的一个方法,因此在函数 b 中的 this 指向的是对象 obj,输出结果为 1。

1.3、构造函数调用

当一个函数作为构造函数来调用时(使用 new 关键字),this 指向新创建的对象。

例如:

-------- ------------ ---- -
  --------- - -----
  -------- - ----
-

--- - - --- ------------- ----

------------------- ------- -- -- ------ --

在上面的例子中,当我们使用 new 关键字调用 Person 函数时,this 就指向新创建的对象 p,用来保存其属性值。

1.4、apply 和 call 方法调用

apply 和 call 方法可以改变函数的 this 指向,并立即调用该函数。其中,apply 方法接收一个数组参数,该数组包含了作为参数传递给函数的值;call 方法接收多个参数,每个参数依次作为函数的参数传递进去。

例如:

--- ---- - -
  -- --
--

--- ---- - -
  -- --
--

-------- ------ -
  --------------------
-

----------------- -- -- -
---------------- -- -- -

在上面的例子中,我们分别使用 apply 和 call 方法来调用 test 函数,并将 this 的值分别指向了 obj1 和 obj2 对象。因此,第一个输出结果为 1,第二个输出结果为 2。

2、this 的实现细节

所谓 this 的实现细节,主要指 this 绑定规则的优先级。在 JavaScript 中,this 的值是动态绑定的,即取决于调用函数的方式和函数的执行上下文。但是,在实际开发中,我们经常会遇到一些特殊情况,例如嵌套函数、箭头函数、闭包等,这些情况可能会导致 this 的值出现一些问题。因此,在本节中,我们将讨论 this 的实现细节,包括以下几点。

2.1、嵌套函数

在嵌套函数中,this 的值可能会出现一些问题。当嵌套函数作为对象的方法调用时,this 的值会指向该对象;但是如果嵌套函数单独调用时,this 的值会指向全局对象。

例如:

--- - - --

--- --- - -
  -- --
  -- ---------- -
    -------------------- -- -

    -------- --- -
      -------------------- -- -
    -

    ----
  --
--

--------

在上面的例子中,因为嵌套函数 c 是单独调用的,所以 this 的值指向全局对象 window,因此第二个输出结果为 1,而不是 2。为了避免这种问题,我们可以在外层函数中将 this 保存到一个名为 that 或 self 的变量中,从而在嵌套函数中使用。

例如:

--- --- - -
  -- --
  -- ---------- -
    --- ---- - -----

    -------------------- -- -

    -------- --- -
      -------------------- -- -
    -

    ----
  --
--

--------

在上面的例子中,我们使用了 that 变量来保存 this 的值,从而在嵌套函数中使用。

2.2、箭头函数

在箭头函数中,this 的值不是动态绑定的,而是继承自父级作用域中的 this。

例如:

--- - - --

--- --- - -
  -- --
  -- ---------- -
    -------------------- -- -

    --- - - -- -- -
      -------------------- -- -
    --

    ----
  --
--

--------

在上面的例子中,箭头函数 c 中的 this 指向的是外层函数 b 的 this,因此输出结果都为 2。正是因为这种特殊的 this 绑定规则,使得箭头函数在处理回调函数时非常方便和简洁,可以避免使用 that 或 self 变量来保存 this。

2.3、构造函数和原型方法

在构造函数中和原型方法中,this 的值的取决关系比较复杂,取决于具体的代码实现。

在使用构造函数创建对象时,我们可以使用 this 来定义该对象的属性和方法,同时也可以在构造函数的作用域链中使用 this 来引用其他变量和函数。

例如:

-------- ------------ ---- -
  --------- - -----
  -------- - ----

  ------------- - ---------- -
    ------------------- -- ---- -- - - --------- - -- --- - - -------- - - ----- -------
  --
-

--- - - --- ------------- ----

------------- -- -- ------- -- ---- -- ---- --- -- ----- -----

在上面的例子中,我们使用构造函数 Person 创建了一个对象 p,使用 this 关键字来实现其属性和方法,而在 sayHello 方法中,我们又使用了 this 来引用 Person 对象的属性 name 和 age。

在原型中,this 的值可以引用该对象的属性和方法,也可以引用该对象的构造函数的属性和方法。我们可以使用 this 关键字来定义或访问原型方法,使用 this.constructor 来访问该对象的构造函数。

例如:

-------- ------------ ---- -
  --------- - -----
  -------- - ----
-

------------------------- - ---------- -
  ------------------- -- ---- -- - - --------- - -- --- - - -------- - - ----- -------
--

--------------------- - ---------

--- - - --- ------------- ----

------------- -- -- ------- -- ---- -- ---- --- -- ----- -----
-------------------- -- -- --------

在上面的例子中,我们将 sayHello 方法和 type 属性定义到 Person 构造函数的原型中,使用 this 来引用该对象的属性和方法,同时也使用 this.constructor 来访问该对象的构造函数。当我们使用 p.sayHello() 调用对象 p 的方法时,this 的值指向对象 p,因此输出结果正确。当我们使用 p.type 来访问对象 p 的 type 属性时,this 的值也指向对象 p,但是该属性并不存在于对象 p 中,而是存在于其原型对象中,因此该输出结果也是正确的。

3、避免常见问题

虽然使用 this 关键字可以方便地访问对象的属性和方法,但是很多时候也容易出现一些常见问题,例如嵌套函数的 this 绑定、setTimeout 函数中的 this 绑定等等。为了避免这些问题,我们可以使用以下几种方法。

3.1、使用 apply 和 call 明确指定 this 的值

我们可以使用 apply 和 call 方法明确指定要绑定的 this 的值,从而避免 this 绑定规则的复杂性。例如:

--- --- - -
  -- --
  -- ---------- -
    --------------------
  --
--

--- - - --

----------------- -- -
----------------- -- -

在上面的例子中,我们分别使用了 call 和 apply 方法来明确指定函数 b 的 this 的值,从而避免了嵌套函数的 this 绑定问题。

3.2、使用箭头函数

使用箭头函数可以避免在嵌套函数中使用 that 或 self 变量来保存目标对象的引用。例如:

--- - - --

--- --- - -
  -- --
  -- ---------- -
    -------------------- -- -

    --- - - -- -- -
      -------------------- -- -
    --

    ----
  --
--

--------

在上面的例子中,我们使用箭头函数 c 来避免了嵌套函数的 this 绑定问题,从而使得代码更加简洁和易读。

3.3、使用闭包

使用闭包可以在函数外部保存目标对象的引用,从而避免嵌套函数的 this 绑定问题。例如:

--- - - --

--- --- - -
  -- --
  -- ---------- -
    -------------------- -- -

    --- ---- - -----

    -------- --- -
      -------------------- -- -
    -

    ------ --
  --
--

--- - - --------
----

在上面的例子中,我们使用了闭包来保存了函数 b 的 this 指向对象 obj 的引用,从而在嵌套函数 c 中可以使用该引用访问该对象的属性和方法。当我们在外部调用 f 函数时,打印结果也是正确的。这种方法尽管有些复杂,但是可以避免 this 带来的一些不必要的问题。

4、总结

在 JavaScript 中,this 是一个非常重要的概念,其绑定规则决定了该关键字的值取决于函数的调用方式和函数的执行上下文。在实际开发中,我们经常会遇到一些特殊情况,例如嵌套函数、箭头函数、闭包等,这些情况可能会导致 this 的值出现一些问题。为了避免这些问题,我们需要了解 this 的动态绑定和实现细节,并使用 apply 和 call 方法、箭头函数以及闭包等方法来明确指定 this 的值或保存目标对象的引用,从而写出具有效率和可读性的 JavaScript 代码。

来源:JavaScript中文网 ,转载请联系管理员! 本文地址:https://www.javascriptcn.com/post/64a92b7d48841e9894573cac


猜你喜欢

  • 利用 Webpack-dev-server 模拟服务器数据

    前端开发不仅需要运用各种前端技术,还需要和后端开发人员密切合作。其中一个重要的环节是前后端数据交流。常见的方法是让后端提供一份 API 文档,并在前端请求时调用。然而在开发初期,API 尚未开发完成,...

    1 年前
  • 如何在 Jest 中使用 Hooks 进行测试

    如何在 Jest 中使用 Hooks 进行测试 React Hooks 是 React 16.8 中一个新的特性,它让我们可以在函数组件中使用状态和其他 React 特性。

    1 年前
  • 基于 Chai 扩展的状态转换机工具的实现

    前言 在前端开发中,状态转换机作为一种常见的模型,可以被广泛应用于各种场景,如有限状态机、自动机等。而针对状态转换机的实现,Chai 是一个备受推崇的断言库,不少开发者都很熟悉。

    1 年前
  • 如何在 React 中实现无限滚动列表

    如何在 React 中实现无限滚动列表 实现无限滚动列表是前端应用的一个重要组成部分,尤其是当应用需要展示大量数据时。在 React 中,我们可以使用一些工具和技巧来实现无限滚动列表。

    1 年前
  • ES8 的更少冗余且极简的 JavaScript 代码方式

    JavaScript 是前端开发中必不可少的一门语言,它不仅能够让网页更加生动和有趣,还能够为用户提供更好的交互体验。随着技术的不断发展,ES8 的出现为我们带来了更少冗余且极简的 JavaScrip...

    1 年前
  • Material Design 教程之 Navigation Drawer 详解

    在 Material Design 设计风格中,Navigation Drawer 是一个非常常见的 UI 元素,可以让用户通过侧边栏来快速浏览和访问应用中的不同部分,比如设置、帮助、帐号等。

    1 年前
  • SSE 技术在移动端 H5 应用中的应用场景和实践

    随着移动互联网的普及和发展,移动端 H5 应用的开发越来越受到关注,而 SSE 技术作为移动端 H5 应用的一种常用实现方式,也越来越得到了广泛的应用。 本文将介绍 SSE 技术在移动端 H5 应用中...

    1 年前
  • RESTful API 的 API 文档生成方法

    RESTful API 是现代 Web 开发中经常使用的一种 API 设计风格。它的特点包括资源关注、统一接口等,使得 API 更加易于理解和维护。对于 API 的使用者来说,清晰的文档是使用 API...

    1 年前
  • PWA 开发中如何处理离线访问

    随着 PWA 技术的不断发展,越来越多的网站开始采用 PWA 技术来提供更好的用户体验。PWA 技术可以帮助网站实现离线访问,让用户即使在没有网络连接的情况下也能够访问网站。

    1 年前
  • Babel 编译 ES5 时遇到的问题及解决方法

    在前端开发过程中,Babel 是一个非常流行的工具,用于将 ES6+ 的代码转换成浏览器兼容的 ES5 代码。然而在实际使用中,我们常常会遇到一些问题。本文将针对常见的问题和解决方法进行详细介绍,旨在...

    1 年前
  • TypeScript 中的类型修饰符技巧

    在 TypeScript 中,类型修饰符是指那些用来限制变量或函数的类型的关键字,例如 readonly、public、protected 等关键字。这些类型修饰符可以帮助开发者更好地控制代码的行为,...

    1 年前
  • Docker 部署 MongoDB 集群及常见问题解决详解

    前言 MongoDB 是一款流行的 NoSQL 数据库,它支持水平扩展,可以很好地满足大量数据存储和高并发读写的需求。而 Docker 是一款流行的容器化技术,它具备环境隔离、易于部署、可移植性等优点...

    1 年前
  • Hapi框架开发中解决跨域问题的几种方案

    跨域问题是前端开发中的一个常见问题。在使用Hapi框架开发web应用时,同样会遇到这个问题。本文将介绍几种解决Hapi框架开发中跨域问题的方案,包括Hapi官方支持的跨域插件以及自定义中间件方法,希望...

    1 年前
  • ES9 中全新的正则表达式的功能

    正则表达式是在许多编程语言中都存在的一种强大的字符串处理方式。在 JavaScript 中,正则表达式一直是非常重要的一部分,用于匹配、替换、搜索等操作。随着 ES9 的到来,正则表达式又迎来了全新的...

    1 年前
  • ECMAScript 2019 中的 Array.prototype.slice:取数组部分元素

    ECMAScript 2019 中的 Array.prototype.slice:取数组部分元素 Array.prototype.slice() 是 JavaScript 中最常用的数组方法之一,用于...

    1 年前
  • 使用 PM2 进行性能监控的教程指南

    前言 对于前端开发者而言,了解并掌握性能监控是非常必要的。而对于 Node.js 服务端开发者而言,使用 PM2 进行性能监控则是一种非常常见的方式。本篇文章将介绍如何使用 PM2 进行性能监控,旨在...

    1 年前
  • redux-saga 进阶 —— 处理复杂场景和异常

    随着前端应用的复杂性不断增加,单纯地使用 Redux 这样的状态管理库已经不能满足需求了,所以出现了 Redux-Saga 这样的中间件,用于处理异步逻辑和复杂场景。

    1 年前
  • 在 Promise 中如何处理多个请求返回的数据

    在 Promise 中如何处理多个请求返回的数据 随着前端技术的不断发展,现在常常需要在前端进行多个异步请求,并在所有请求都完成后再进行统一处理。在这种情况下,我们可以使用 Promise.all()...

    1 年前
  • 如何在 Custom Elements 中处理用户输入

    在 Web 开发中,我们经常需要在页面上添加自定义的 HTML 元素。Web Components 的出现为这种需求提供了新的解决方案:Custom Elements。

    1 年前
  • Angular 中如何利用 MatTooltip 提示信息

    MatTooltip 是 Angular Material 中的一个组件,可以在用户鼠标悬停在某个元素上时,以浮动框的形式展示提示信息。在前端开发中,我们经常需要给用户提供一些额外的信息来提示他们如何...

    1 年前

相关推荐

    暂无文章