JavaScrip 中的 this, bind, call apply 简述

news2025/1/10 1:27:29

JavaScrip 中的 this, bind, call & apply 简述

this 是一个比较特殊的东西,基本上可以理解成 this 的指向是就近调用的指向,因此 this 在 JS 中也是一个比较令人困惑的知识点。

之前绕过 this 的方法基本上采用 arrow function,因为 arrow function 不包含对 this, arguments, 或者 super 的绑定,因此使用 arrow function 时,this 的指向更容易判断一些,不过最近感觉也是时候细究一下 this, call, applybind 了。

this 的指向问题

上面说了,this 的指向采用的是就近调用原则,以下面的代码为例:

const person = {
  name: 'John',
  greet() {
    console.log(this);
    console.log('Hello ' + this.name);
  },
};

正常情况下,greet 中的 this 指向应该指代的是 person,在下面这种调用的情况下是正确的:

person.greet();

在这里插入图片描述

这个情况下,greet 是通过 person 这个对象去调用的,因此 greet 绑定的环境是 person 对象本身。

不过换一种方式调用,就会有不同的结果:

const { greet } = person;
greet();

这时候的 greet 指向就变成了 global,或者在严格模式下变为 undefined:

在这里插入图片描述

在这里插入图片描述

这是因为 greet 被解构出来了,在这个时候调用它的是 global 对象下,换言之,这个时候的 this 就会自动绑定成 global 对象。

再换一种调用的方法:

const user = {
  name: 'user',
};

user.greet = person.greet;

user.greet();

这个时候的 greet 因为是被 user 调用的,因此绑定的对象就是 user:

在这里插入图片描述

大多数情况下 this 的问题不是很大,不过有的时候用到 callback 时就会是一个比较麻烦的事情,如下面模拟一个挂载 callback,并且实现延后执行的情况:

const example = {
  addEventListener(cb) {
    this.cb = cb;
  },
  click() {
    this.cb();
  },
};

example.addEventListener(person.greet);
example.click();

这时候调用 greet 中的指向就变成了 example

在这里插入图片描述

这种方法也是很多时候 behind the scene 的实现,因此 this 的指向是相当不清晰的。

在一些情况下,这也就是 bind, callapply 可以帮忙的地方。

bind

bind 会创建一个新的函数,同时 bind 会将 this 绑定成第一个传过去的参数。

遥想当初使用 React 的 class based component 时,在没有使用 arrow function 的时候,都是使用 bind 去执行的:

class ExplainBindingsComponent extends Component {
  constructor() {
    super();

    this.onClickMe = this.onClickMe.bind(this);
  }

  onClickMe() {
    console.log(this);
  }
}

这就是为了将方法中的 this 绑定到当前类中,使其不随从就近原则。这里的第一个参数就是想要绑定的对象,在无所谓第绑定的对象时可以用 null,使用 this 指代的是当前对象,也可以明确的使用一个已经实例化的对象。

上面的几个案例使用 bind 绑定的改写方法为:

// 'use strict';

const person = {
  name: 'John',
  greet() {
    console.log(this);
    console.log('Hello ' + this.name);
  },
};

const { greet } = person;
greet();
greet.bind(person)();

const user = {
  name: 'user',
};

user.greet = person.greet.bind(person);

user.greet();

const example = {
  addEventListener(cb) {
    this.cb = cb;
  },
  click() {
    this.cb();
  },
};

example.addEventListener(person.greet.bind(person));
example.click();

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这样都可以通过 bindthis 绑定到固定的对象上,使其不受就近原则的影响。另外,bind 也可以接受其他的参数,并将其传到调用的函数中,如:

const person = {
  name: 'John',
  greet(from) {
    console.log(this);
    console.log('Hello ' + this.name + ' from ' + from);
  },
};

const { greet } = person;
// greet();
greet.bind(person, 'Sam')();

在一些情况下,使用 bind 也可以简化代码,如下面的代码可以实现一段基础的加减乘除的操作:

const math = {
  accumulated: 0,
  add(num) {
    const oldNum = this.accumulated;
    this.accumulated += num;
    console.log(`${oldNum} + ${num} = ${this.accumulated}`);
  },
  substract(num) {
    const oldNum = this.accumulated;
    this.accumulated -= num;
    console.log(`${oldNum} - ${num} = ${this.accumulated}`);
  },
  multiply(num) {
    const oldNum = this.accumulated;
    this.accumulated *= num;
    console.log(`${oldNum} * ${num} = ${this.accumulated}`);
  },
  divide(num) {
    const oldNum = this.accumulated;
    this.accumulated /= num;
    console.log(`${oldNum} / ${num} = ${this.accumulated}`);
  },
};

math.add(10);
math.substract(5);
math.multiply(2);
math.divide(5);

在这里插入图片描述

这种情况下,可以使用 bind 去简化操作:

const math = {
  accumulated: 0,
  calculation(operation, num) {
    let oldNum = this.accumulated;
    if (operation === '+') {
      this.accumulated += num;
    } else if (operation === '-') {
      this.accumulated -= num;
    } else if (operation === '*') {
      this.accumulated *= num;
    } else if (operation === '/') {
      this.accumulated /= num;
    }
    console.log(`${oldNum} ${operation} ${num} = ${this.accumulated}`);
  },
};

const { calculation } = math;
calculation.bind(math, '+', 10)();
calculation.bind(math, '-', 5)();
calculation.bind(math, '*', 2)();
calculation.bind(math, '/', 5)();

最后的运行结果也是一致的:

在这里插入图片描述

不是说一定要用 bind 去执行,也可以用其他的方式使得代码更加清晰可读,并提高复用性

call & apply

callapply 两个不会创建一个新的函数,但是它们的用法和 bind 相似,第一个参数都是 this 的指向,只不过 call 可以传递无限多的参数,而 apply 会接受一个 array like 的结构作为第二个参数:

call(thisArg, arg1, /* …, */ argN);

apply(thisArg, argsArray);

根据 mdn 所说,通常情况下,在不涉及到 constructed 的情况下,bindcall 是可以被视作效果相同的:

You can generally see const boundFn = fn.bind(thisArg, arg1, arg2) as being equivalent to const boundFn = (...restArgs) => fn.call(thisArg, arg1, arg2, ...restArgs) for the effect when it’s called (but not when boundFn is constructed).

reference

  • Function.prototype.bind()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/538877.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

35、Adaptive Forms(3)Data Model

文章目录 35、Adaptive Forms(3)Data Model配置JDBC安装mysql-connector-j.jar在数据库中创建数据创建Data Model 35、Adaptive Forms(3)Data Model 在使用Mysql数据之前还需要在AEM中创建DataModel映射数据库的数据结构。也需要…

07-架构2023版-centos+docker部署nacos

1、创建nacos的数据库配置 1.1、创建数据库 1.2、创建用户nacos并授权数据库 参考 06 mysql 创建账户部分内容 1.3、执行数据库脚本 导入官方nacos sql语句。如果是历史有历史数据, 从历史数据sql导入 2、创建 bridge 网络并指定 IP 区间 2.1、创建自定义网络…

选择排序:20年前在谭浩强《c语言程序设计上》学习过(43)

小朋友们好,大朋友们好! 我是猫妹,一名爱上Python编程的小学生。 和猫妹学Python,一起趣味学编程。 今日主题 什么是选择排序? 用Python写段代码,实现插入排序。 选择排序 选择排序是一种简单的排序算…

Contrastive Learning

https://towardsdatascience.com/understanding-contrastive-learning-d5b19fd96607 The Beginner’s Guide to Contrastive Learning Contrastive Representation Learning | LilLog

Visual Studio 怎样设置断点并调试

1-普通断点 设置普通断点的方法很简单,就是在代码行的左边栏灰色区域点击或者把光标放在某代码行,按下 F9。 2-调试 按F5开始调试,按F11是逐行调试,F10是逐过程调试,shiftF5是停止调试。 3-高级调试 见链接&#x…

学系统集成项目管理工程师(中项)系列22b_信息化知识(中)

1. 信息化 1.1. 产品信息化 1.1.1. 信息化的基础 1.2. 企业信息化 1.2.1. 国民经济信息化的基础 1.3. 产业信息化 1.3.1. 【21下选08】 1.4. 国民经济信息化 1.5. 社会生活信息化 1.6. 【22上选08】 1.7. 基本内涵 1.7.1. 信息化的主体是全体社会成员,包…

日语文法PPT截图16-30

16句子的构成 单句 谓语只出现了一次 重句 两个谓语 并且两个谓语是对应的关系 由多个在意义上有差距的小句共同构成的句子 叫复文 用一个句子去修饰一个名词 也是个很典型的复文 被名词化了的句子充当小句,这也是复文的一种情况 引用形式的复文 对于这种复文的…

Java-API简读_java.util.RandomAccess接口(基于JDK1.8)(不涉及源码)

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://blog.csdn.net/m0_69908381/article/details/130727525 出自【进步*于辰的博客】 其实我的【Java-API】专栏内的博文对大家来说意义是不大的。…

史上最详细的SQL盲注入门教程

测试同学在做安全测试时,相信多少都会遇到SQL注入与盲注的漏洞,那么今天我们就来分享下SQL注入与盲注的相关知识,希望对大家有所启发。 一、SQL盲注的定义 SQL注入,简单理解,也就是将用户输的的内容当代码执行了&…

Docker的使用 (1.安装docker)

安装docker前非常重要的准备工作 请保证你的centos 操作系统为至少为7版本请关闭linux的防火墙虚拟机联网,安装好yum工具 安装docker # 设置docker镜像源 yum-config-manager \--add-repo \https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.reposed…

Aqua 启动管道完整性扫描以检测恶意软件

云原生安全提供商 Aqua Security 已宣布添加管道完整性扫描,以防止软件供应链攻击并确保 CI/CD 管道完整性。 在 eBPF 技术的支持下,Aqua 的管道完整性扫描器实时检测并阻止可疑行为和恶意软件,防止代码篡改并应对软件构建过程中的威胁。 该…

Spring Boot异常处理

目录 Spring Boot异常处理 介绍 拦截器VS 过滤器 自定义异常页面 自定义异常页面 代码实战 需求 代码实现 创建MyErrorController类来模拟异常错误 完成测试 全局异常 说明 全局异常-应用实例 创建GlobalExceptionHandler.java 创建对应的视图地址global.html 完成…

Docker的使用 (1.什么是docker)

前言 这个系列是我自己学习使用docker的记录和分享,作为一名开发人员,你需要了解这个东西并且学会它的简单使用,但是作为一名开发而不是运维,不要花过多的时间去深究它的原理,而是把它当作一个工具即可 docker Docke…

新的RA Group勒索软件针对美国组织进行双重勒索攻击

一个名为“RA Group”的新勒索软件组织针对美国和韩国的制药、保险、财富管理和制造公司。 新的勒索软件行动始于 2023 年 4 月,当时他们在暗网上启动了一个数据泄露站点,以发布受害者的详细信息和被盗数据,采用了大多数勒索软件团伙使用的典…

1.2 Kubernetes架构组件

1.2 Kubernetes架构组件 学习Kubernetes的架构组件,也就是说我们要搭建一个Kubernetes集群里面应该有哪些组成部分,里面具体会有哪些组件。如图 组建一个Kubernetes的集群至少需要包含两个部分,即Master和node,Master即主控节点&…

5月16日|5月17日 6H|时间轴复盘

目录 5月16日 5月17日 5月16日 7:50 起床 7:50-8:25 洗漱到教室吃饭🥚🥚🥛 8:25-8:55 扇贝单词*105 【30min】 8:55- 20:10-21:04 做阅读真题 21:04-21:49 订正答案 糟糕的一天 记忆完全失踪 🆘 5月17日 7:00 起床 7:00-7:30 起床洗漱到教室 7:30-8:00…

1.3 Kubernetes核心概念

1.3 Kubernetes核心概念 在Kubernetes中有三个核心的概念,即Pod、Controller、Service。整个过程可以理解为通过Service统一入口进行访问,由Controller去创建Pod进行部署。 1.3.1 Pod Pod是在Kubernetes中最小的部署单元,如图 Pod的特点 …

免费可用 ChatGPT 网页版

前言 ChatGPT(全名:Chat Generative Pre-trained Transformer),美国OpenAI 研发的聊天机器人程序 ,于2022年11月30日发布 。ChatGPT是人工智能技术驱动的自然语言处理工具,它能够通过理解和学习人类的语言来…

2.5 Java开发环境搭建

2.5 Java开发环境搭建 一、JDK下载与安装 我们主要下载安装JDK8,有些人认为现在已经更新到了JDK12以及更高的版本,无论JDK12、13还是更高的版本,在企业中用的并不是很多,而且每次更新只是增加了一些新特性,我们只需要…

PDF大文件批量去除水印,又一个省心小妙招

PDF大文件批量去除水印,又一个省心小妙招 适用场景:本教程适合批量去除文件量较大的PDF文档内的图片水印。 使用软件:Python; 需安装第三方库:PIL,fitz,pymupdf pip install PIL pip install…