掌握apply和call,解密JavaScript的this指向

news2024/12/26 12:05:51

在这里插入图片描述

文章目录

  • 一、介绍apply和call方法
    • 1.1 简述apply和call方法的作用
    • 1.2 apply和call方法的共同点与不同点
  • 二、深入理解apply方法
    • 2.1 apply方法的语法和参数介绍
    • 2.2 apply方法的使用示例
    • 2.3 apply方法的应用场景
  • 三、深入理解call方法
    • 3.1 call方法的语法和参数介绍
    • 3.2 call方法的使用示例
    • 3.3 call方法的应用场景
  • 四、总结
    • 4.1 两种方法的优劣比较
    • 4.2 如何在项目中合理使用apply和call方法

一、介绍apply和call方法

1.1 简述apply和call方法的作用

apply和call是JavaScript中的两个方法,它们都可以用来调用函数,改变函数中的this指向。函数中的this指向通常指向调用函数的对象,但是在使用apply和call方法调用函数时,可以手动修改函数中的this指向,从而修改函数的执行环境。apply和call的作用类似,但它们的参数列表有所不同。

apply方法的参数是一个数组,该数组中的元素作为参数传递给函数调用。这样可以方便地传递多个参数,也可以使用数组的切片方法来动态地传递参数,达到动态定义参数的效果。

call方法的参数则比较自由,可以传递多个参数,每个参数都逐一传入函数。它不仅能改变函数this指向,还能方便地传递多个参数,执行函数。

总之,apply和call方法可以灵活地改变函数this指向,使其指向指定的对象,可以使用传递多个参数,执行函数。这样可以让程序更加灵活和高效,提高程序的可维护性和可重用性。

1.2 apply和call方法的共同点与不同点

好的,以下是一份表格,总结了apply和call方法的共同点与不同点。

apply方法call方法
语法func.apply(thisArg, [argsArray])func.call(thisArg, arg1, arg2)
参数参数以数组的形式传入参数逐一传入
改变this可以修改函数中的this,指向指定对象可以修改函数中的this,指向指定对象
参数传递方便传递数组或动态参数列表方便传递多个参数
适用对象可以使用在各种对象上,不仅仅是函数对象只能使用在函数对象上

通过表格可以清晰地看出apply和call方法的区别和共同点。两种方法都能够改变函数中的this指向,使其指向指定对象,但它们的参数列表不同。apply方法的参数是一个数组,call方法的参数则比较自由,可以逐一传递多个参数。

二、深入理解apply方法

2.1 apply方法的语法和参数介绍

apply方法的语法比较简单,它是函数对象的一个方法,函数对象直接调用apply方法即可。

apply方法的具体语法如下:

func.apply(thisArg, [argsArray])

其中:

  • func:目标函数对象
  • thisArg:被指定的this对象
  • argsArray:被指定的参数列表,以数组的形式传入

apply方法的核心是通过第一个参数thisArg来指定函数的执行环境。通常情况下,函数的this指向的是调用它的对象。但是使用apply方法时,可以手动命名thisArg的值,从而修改函数的执行环境。argsArray参数可以是任何类型的数组,且其元素顺序与函数形参一一对应。如果函数不需要参数,则需要传递一个空数组或者不传递这个参数。

下面是一个示例,说明apply方法的用法:

function addNums(x, y) {
   return x + y;
}

var numbers = [5, 10];
var sum = addNums.apply(null, numbers);

console.log(sum); //15

在这个示例中,addNums函数接收两个参数并返回它们的和。我们创建了一个数字数组[5, 10],然后使用apply方法将它们作为参数传递给了addNums函数。由于这个示例是在浏览器环境下运行的,因此我们传入了null作为this值。输出结果是15,因为addNums(5, 10)的结果为15

2.2 apply方法的使用示例

下面是一些常见的apply方法的使用示例:

1. 将一个数组添加到另一个数组的尾部

var array1 = [1, 2, 3];
var array2 = [4, 5, 6];

Array.prototype.push.apply(array1, array2);

console.log(array1); // [1, 2, 3, 4, 5, 6]

在这个示例中,我们使用apply方法将一个数组添加到了另一个数组的尾部。使用Array.prototype.push.apply(array1, array2)的语法实现了这一操作,等同于array1.push(4, 5, 6)。由于apply方法需要一个数组作为第二个参数,因此我们使用了[4, 5, 6]来代表这三个数字。

2. 获取数组中的最大值和最小值

var numbers = [5, 10, 2, 9];

var maxNum = Math.max.apply(null, numbers);
var minNum = Math.min.apply(null, numbers);

console.log(maxNum); // 10
console.log(minNum); // 2

在这个示例中,我们使用apply方法来获取数组中的最大值和最小值。虽然Math对象本身没有提供数组做为参数的max和min方法,但是通过传递数组到apply方法中,我们可以实现这样的功能。Math.max和Math.min方法可以接收任意数量的数字参数,因此通过apply方法可以将数值数组拆分为多个数字参数,然后将它们传递给这两个方法。

3. 继承父类的属性和方法

function Person(name, age) {
  this.name = name;
  this.age = age;
}

function Employee(name, age, salary) {
  Person.apply(this, [name, age]);
  this.salary = salary;
}

var john = new Employee('John', 25, 5000);

console.log(john.name); // "John"
console.log(john.age); // 25
console.log(john.salary); // 5000

在这个示例中,我们定义了两个构造函数PersonEmployeeEmployee继承了Person的属性和方法。在Employee函数中使用apply方法调用了Person函数,并将this(即新的Employee实例)作为第一个参数传递给了apply,以实现对Person属性和方法的继承。在本例中,Employee函数还添加了自己的属性salary,以便更好的实现对员工的描述。

这些示例表明,在JavaScript中,apply方法是非常有用的,可以通过传递数组参数来利用不同的API和函数。在这些示例中,我们使用apply方法改变了函数的执行环境,通过第一个参数手动命名thisArg的值,使得被调用函数运行在指定对象的上下文中,这使得函数更加动态和易于重用。

2.3 apply方法的应用场景

apply方法的应用场景比较多,主要是以下几个方面:

1. 改变函数中的this指向

apply方法最常见的用途是改变函数中的this指向,使其指向一个指定的对象。这样可以在函数内部操作该对象,提高代码的灵活性和复用性。例如,我们可以将一个对象添加到另一个对象中:

var person = {
  name: "Tom"
};

function sayHello(age) {
  console.log(`Hello! My name is ${this.name} and I'm ${age} years old.`);
}
sayHello.apply(person, [25]); // Hello! My name is Tom and I'm 25 years old.

在这个示例中,我们使用apply方法将person对象作为函数sayHello的thisArg值。因此,函数中的this指向了person对象。通过传递[25]作为argsArray参数,我们将age参数传递给了该函数。

2. 模拟继承和多态

apply方法还可以用于模拟继承和多态,可以让一个函数继承另一个函数的属性和方法。例如:

function Animal(name) {
  this.name = name;
}

function Dog(name) {
  Animal.apply(this, arguments);
}

Dog.prototype.bark = function() {
  console.log(`Woof! My name is ${this.name}.`);
};

var myDog = new Dog("Rocky");
myDog.bark(); // Woof! My name is Rocky.

在这个示例中,我们使用apply方法将Animal函数的属性和方法赋给了Dog函数。因此,Dog函数得以继承Animal函数。然后我们定义了bark方法,在其中使用this对象访问Dog对象的属性。

3. 动态地传递函数参数

apply方法还可以用于动态地传递函数参数,在调用函数时使用argsArray参数传递一个数组参数。这样可以通过数组来动态地传递参数,使得代码更具灵活性。

function sum(a, b, c) {
  return a + b + c;
}

var numbers = [2, 4, 6];
console.log(sum.apply(null, numbers)); // 12

在这个示例中,我们向sum函数传递了一个数组[2, 4, 6]作为参数。apply方法将数组拆开,并将每个元素作为单独的参数传递给了函数。

总之,apply方法可以应用于很多场景,总的来说,它的主要作用是改变函数中的执行环境,使其运行在指定的对象中。apply方法可以让函数更加动态和高效,提高代码的可维护性和可重用性。

三、深入理解call方法

3.1 call方法的语法和参数介绍

call方法也是函数对象的一个方法,和apply方法类似,它可以改变函数的执行环境并手动指定函数中的this值。call方法的语法如下:

func.call(thisArg, arg1, arg2, ...)

其中:

  • func:目标函数对象
  • thisArg:被指定的this对象
  • arg1arg2、……:被指定的参数列表,逐一传入

与apply方法不同的是,call方法的参数必须一个一个手动传入,没有像argsArray参数那样可以一次传递多个参数的方式。但是call方法相较于apply方法的优点是能够更清晰地了解哪个参数是什么,从而更好地控制函数。

下面是一个示例,说明call方法的用法:

function addNums(x, y) {
   return x + y;
}

var sum = addNums.call(null, 5, 10);

console.log(sum); //15

在这个示例中,与apply相似,addNums函数接收两个参数并返回它们的和。我们直接使用call方法将5和10这两个参数作为参数传递给addNums函数,而不是使用数组作为参数像apply方法一样。由于这个示例是在浏览器环境下运行的,因此我们传入了null作为this值。输出结果是15,因为addNums(5, 10)的结果为15

3.2 call方法的使用示例

下面是一些常见的call方法的使用示例:

1. 调用父类的构造函数

在使用原型继承时,为了实现子类可以继承父类的属性和方法,通常需要在子类内部调用父类的构造函数。使用call方法可以很容易地实现这一点:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

function Employee(name, age, salary) {
  Person.call(this, name, age);
  this.salary = salary;
}

在这个示例中,我们定义了两个构造函数Person和Employee,Employee继承了Person的属性。在Employee函数内部使用了call方法调用了Person函数,将this对象,也就是新的Employee实例,作为第一个参数传递给了call方法,从而实现了对Person属性的继承。

2. 利用Math对象的方法

Math对象的一些方法也可以使用call方法来执行,使它们能够应用于数组等数据结构上。例如:

var numbers = [5, 10, 2, 9];

var maxNum = Math.max.call(null, ...numbers);
var minNum = Math.min.call(null, ...numbers);

console.log(maxNum); // 10
console.log(minNum); // 2

在这个示例中,我们使用call方法调用了Math对象的max和min方法。由于这些方法不接受数组作为参数,因此我们使用了扩展运算符将数组中的值展开,以便向函数中逐一传递这些值。

3. 使用某个对象的方法对另一个对象进行操作

有时候,我们希望可以使用一个对象的方法来操作另一个对象。例如:

var person = {
  name: "Tom",
  introduce: function() {
    console.log(`Hello! My name is ${this.name}.`);
  }
};

var student = {
  name: "Jerry"
};

person.introduce.call(student); // Hello! My name is Jerry.

在这个示例中,我们定义了两个对象:personstudentperson有一个introduce方法,它可以打印出相应的问候语,而student没有这个方法。我们可以使用call方法将personintroduce方法作为student的一个方法来使用。

这些示例表明,call方法也是非常有用的,可以通过传递单个参数来使用不同的API和函数。在这些示例中,我们使用call方法改变了函数的执行环境,手动指定了函数中的this值,使得被调用函数运行在指定对象的上下文中,这使得代码更加动态和易于重用。

3.3 call方法的应用场景

call方法的应用场景与apply方法类似,主要是以下几个方面:

1. 改变函数中的this指向

和apply方法一样,call方法最常见的用途是改变函数中的this指向,使其指向一个指定的对象。这样可以在函数内部操作该对象,提高代码的灵活性和复用性。例如,我们可以将一个对象添加到另一个对象中:

var person = {
  name: "Tom"
};

function sayHello(age) {
  console.log(`Hello! My name is ${this.name} and I'm ${age} years old.`);
}
sayHello.call(person, 25); // Hello! My name is Tom and I'm 25 years old.

在这个示例中,我们使用call方法将person对象作为函数sayHello的this值。因此,函数中的this指向了person对象。通过直接在call方法中传递25,我们将age参数传递给了该函数。

2. 间接调用函数

有时候,我们需要间接调用一个函数,例如从一个对象发出信号来触发函数,或者使用setTimeout方法执行一个函数。在这些情况下,我们可以使用call方法间接调用函数,如下所示:

var person = {
  name: "Tom",
  sayHi: function() {
    console.log(`Hi! My name is ${this.name}.`);
  }
};

var sayHiFunc = person.sayHi;

setTimeout(function() {
  sayHiFunc.call(person);
}, 1000); // Hi! My name is Tom. (1秒后输出)

在这个示例中,我们定义了一个对象person和一个函数sayHi。在将sayHi赋值给变量sayHiFunc时,我们断开了函数和person对象之间的联系,使sayHi的this值变为了全局对象。为了同时使该函数使用person对象作为this值,我们在一个setTimeout函数中使用call方法来修改this值。

3. 借用其他对象的方法

使用call方法,可以借用另一个对象的方法来操作当前对象,从而实现代码的复用。例如:

var person = {
  name: "Tom"
};

var student = {
  name: "Jerry",
  sayHi: function() {
    console.log(`Hi! My name is ${this.name}.`);
  }
};

student.sayHi.call(person); // Hi! My name is Tom.

在这个示例中,我们使用call方法调用了student对象的sayHi方法。由于sayHi中使用了this关键字,使用call方法借用该方法时,我们指定了person对象为this值,使sayHi方法能够操作person对象,达到一定程度上的代码复用。

这些示例表明,call方法也是非常有用的,可以通过传递单个参数来使用不同的API和函数。它的主要作用是改变函数中的执行环境,使其运行在我们指定的对象上下文中。call方法可以让函数更加灵活和高效,提高代码的可维护性和可重用性。

四、总结

4.1 两种方法的优劣比较

下面是apply和call两种方法的优劣比较,使用表格进行展示:

特点applycall
参数格式接收数组作为参数,使用argsArray传递需要将参数一个个手动传入
this指向第一个参数指定this值第一个参数指定this值
传参方式argsArray参数可以一次传递多个参数,可以使用数组的方法操作参数参数必须一个个手动传入,不能一次传递多个参数
性能相较于call方法,在传递多个参数时可能稍有不便比call方法略快一些,性能更高一些
适用场景适用于参数数量不确定的情况,特别是对数组或类似数组对象操作时适用于参数个数少、确定的情况,特别是需要精确控制参数传递时
代码可读性对实现依赖数组的API操作,代码可读性好参数显式传递,代码可读性稍差
错误提示对参数数量或数组长度没有进行检查,出错时不够明确参数必须逐个传递,出错时更有可能提示错误
应用场景举例取得数组最大值/最小值调用父类的构造函数,间接调用函数,借用其他对象的方法等

需要注意的是,两种方法都有各自擅长的领域和局限性,选择应用的方法要根据具体情况和需求来判断,而无法简单地提出哪一个方法更好。

4.2 如何在项目中合理使用apply和call方法

在项目中,使用apply和call方法能使代码更加灵活、高效、易于维护,而且能够提高代码的可重用性。下面介绍在项目中的两种合理使用场景:

1. 对数组或类似数组对象进行操作

使用apply或call方法可以使对数组或类似数组对象的操作更加方便,例如:

var arr = [1, 2, 3, 4, 5];

// 使用apply方法取得最大值
var maxNum = Math.max.apply(null, arr);
console.log(maxNum); // 5

// 使用call方法取得最小值
var minNum = Math.min.call(null, ...arr);
console.log(minNum); // 1

通过使用apply方法和call方法,我们可以直接将数组作为参数传递,而不需要手动遍历数组来处理。这不仅提高了代码的可读性,而且可以提高代码的执行效率。

2. 处理JavaScript中this指向的问题

在JavaScript中,this的指向相当灵活,但有时候需要对它进行手动控制,这时候就需要使用apply方法或call方法。例如:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

function Employee(name, age, salary) {
  Person.call(this, name, age);
  this.salary = salary;
}

在这个例子中,Employee函数继承了Person函数的属性,并使用call方法将Person的this值指定为Employee。这样,在Employee函数中就可以使用Person的属性和方法了,达到代码重用的效果。

总的来说,使用apply和call方法可以帮助我们解决很多问题,使代码更加精简、高效、可读性强,从而提高代码质量和维护性。然而,在实际操作过程中,我们也需要注意使用场景,避免不必要的性能问题。

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

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

相关文章

MySQL:库的操作和表的操作(内含MySQL数据类型讲解)

进入数据库的数据目录 cd var/lib/mysql 库的操作 创建数据库 创建数据库的本质是创建目录。 创建数据库时有两个编码集:1.数据库编码集(存储时使用)2.进行字段比对读取时使用的编码方式) 语法: CREATE DATABASE …

SpringBoot+Prometheus采集Metrics指标数据

简介 本文介绍在springboot3.x里配置启用系统指标监控功能,来监视系统各项Metrics指标,从而实时了解应用的运行健康状态,通过promtheus服务提供数据收集与指标可视化方案; Metrics指标 metrics指标表示应用程序代码运行中提供的…

【JavaWeb】前端之CSS基础认知

目录 前言 1、CSS基本语法规范 2、向HTML中引入CSS的方式 2.1、内部样式 2.2、外部样式 2.3、内联样式 3、CSS选择器 3.1、基础选择器 3.1.1、标签选择器 3.1.2、类选择器 3.1.3、id选择器 3.1.4、通配符选择器 3.2、复合选择器 3.2.1、后代选择器 4、CSS常用元素…

leetcode514. 自由之路(java)

自由之路 leetcode514. 自由之路题目描述解题思路代码演示 动态规划专题 leetcode514. 自由之路 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/freedom-trail 题目描述 电子游戏“辐射4”中,任务 “通向自由…

深入浅出设计模式 - 工厂模式

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌ Java知识图谱点击链接:体系化学习Java(Java面试专题) ​ 💕💕 感兴趣的同学可…

目标检测舰船数据集整合

一、光学数据集 1、 DIOR 数据集(已下载yolo版本)(论文中提到过) “DIOR”是一个用于光学遥感图像目标检测的大规模基准数据集。数据集包含23463个图像和192472个实例,涵盖20个对象类。这20个对象类是飞机、机场、棒球场、篮球场、桥梁、烟囱…

CSS 边框盒子

文章目录 边框盒子 边框盒子 提示:这里可以添加本文要记录的大概内容: 默认的盒子类型是内容盒子:content-box 在内容盒子中,width和height是内容尺寸 盒子整体的尺寸内容的尺寸两个边框两个内边距 css3引入了新的盒子类型&…

Linux——对动静态库的优缺点和底层剖析对比

目录 静态库底层剖析: 所以静态库的优缺点总结: 动态库底层剖析: 所以使用动态库的优缺点剖析: 静态库底层剖析: 如上图:file1.exe被加载到了内存,成为了进程,因为代码中都有pri…

【Docker】docker安装配置Jenkins

docker 安装 Jenkins #拉镜像 docker pull jenkins/jenkins#创建卷(volume) docker volume create jenkins_home#制作容器并启动 docker run -d \ -p 8080:8080 \ -p 50000:50000 \ -v jenkins_home:/var/jenkins_home \ -v /usr/lib/jvm/java-8-openjdk-amd64:/usr/local/java…

IMX6ULL系统移植篇-镜像烧写方法

一. 烧录镜像简介 本文我们就来学习:windows 系统下烧录镜像的方法。 如何使用 NXP 官方提供的 MfgTool 工具通过 USB OTG 口来 烧写系统。 二. windows下烧录镜像 1. 烧录镜像前准备工作 (1)从开发板上拔下 SD卡。 (2…

SpringBoot中如何优雅的统一全局返回格式与处理系统异常?

在领域驱动设计(DDD)中,接口层主要负责处理与外部系统的交互,包括接收用户或外部系统的请求,调用应用层服务处理请求,以及将处理结果返回给请求方。 我发现一些代码中,接口的返回值类型众多&am…

chatgpt赋能python:Python语言单词大全:从入门到精通

Python语言单词大全:从入门到精通 Python是一种高级编程语言,适用于各种应用领域,如数据科学、机器学习、网站开发和自动化脚本编写等。Python语言有其内置的语法和功能,我们将在本文中逐一介绍和解释。 1. Python中最基本的单词…

K8S集群安全之安全机制

1. 安全机制说明: Kubernetes作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server是集群内部各个组件通信的中介,也是外部控制的入口。所以Kubernetes的安全机制基本就是围绕保护APIServer来设计的。Kubernetes使…

小程序实现双列布局

目录 1 双列布局2 采用流式布局3 采用网格布局总结 小程序中双列布局是一个常见的效果,比如在电商小程序的商品推荐,效果如下: 1 双列布局 双列布局的话特点是随着数据的增多,我们会出现偶数或者奇数的问题,如果是偶…

【Data Studio 2.1.0连接openGauss2.1.0】

Data Studio 2.1.0连接openGauss2.1.0 一、实验环境说明二、配置客户端接入方式三、Data Studio2.1.0连接openGauss数据库 一、实验环境说明 openGauss2.1.0版本Data Studio 2.1.0 版本 二、配置客户端接入方式 【以下操作是omm用户进行】 修改配置文件pg_hba.conf&#xf…

12--Gradle进阶 - Gradle任务的类型

12--Gradle进阶 - Gradle任务的类型 任务类型 前面我们定义的task 都是DefaultTask 类型的,如果要完成某些具体的操作完全需要我们自己去编写gradle 脚本,势必有些麻烦,那有没有一些现成的任务类型可以使用呢?有的,Gradle 官网给出…

C. Tenzing and Balls - dp

分析: 补题。当时不明白的点是如何快速查询相同元素的下标,可以用last[a[i]]表示与a[i]相同的最近的一个数的下标,可以遍历数组的同时实现下标的查询和变化,不断通过遍历来更新last[a[i]]的值,即last[a[i]]i。 然后是状…

衡石bi的几种跳转方式

衡石bi的几种跳转方式 通过点击表格的单元格跳转(带参) 点击首页的表格里任意一行的单元格跳转到明细页的企业相关数据 这两个数据集都是有一个info_uuid的主键,我们知道每个控件都可以设置交互的方式进行跳转应用内的仪表盘。 点击首页里要设置跳转的控件右上角的…

maven基础教程

一、安装moven 1、下载maven包 首先到maven官网下载安装包,解压到本地目录,然后配置环境变量。 maven下载地址:https://maven.apache.org/download.cgi 2、配置环境变量 然后打开环境变量,添加 MAVEN_HOME 系统变量&#xff…

JVM-jvisualvm性能监控可视化工具使用与eden-s0-s1分配分析

目录 第一步:安装jvisualvm 第二步:安装VisualvmGc插件 方式一:jvisualvm工具直接下载安装 方式二:去官网下载导入安装 总结 第三步:idea安装VisualvM Launcher插件 第四步:演示young中eden、s0、s1垃…