【JavaScript】Function的祖传方法call与apply

在这里插入图片描述

引言

内容速递

看了本文您能了解到的知识!

在本篇文章中,将带你了解什么是call和applycall和apply的用途、如何手写callapply以及callapply的使用场景。

1、什么是call和apply

call()apply()JavaScript中的两个内置方法,用于调用函数并指定函数中的this值。

两者的区别是:call()方法的语法和作用与apply()方法类似,只有一个区别,就是call()方法接受的是一个参数列表,而apply()方法接受的是一个包含多个参数的数组

1.1、call

MDN给的解释:Function.prototype.call()

**call()**方法使用一个指定的this 值和单独给出的一个或多个参数来调用一个函数。

1.2、apply

MDN给的解释:Function.prototype.apply()

apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。

需要注意的是,使用callapply方法改变函数的this指向后,函数会立即执行,并返回执行结果。

2、call和apply的语法介绍

2.1、call语法

语法:

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

参数:

  • thisArg:可选的。在 function 函数运行时使用的 this 值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,原始值会被包装。

  • arg1, arg2, …:指定的参数列表。

返回值:

使用调用者提供的 this 值和参数调用该函数的返回值。若该方法没有返回值,则返回 undefined

2.1、apply语法

语法:

function.apply(thisArg, [argsArray])

参数:

  • thisArg:在 func 函数运行时使用的 this 值。请注意,this 可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,原始值会被包装。

  • argsArray: 可选。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 nullundefined,则表示不需要传入任何参数。从 ECMAScript 5 开始可以使用类数组对象。

返回值:

调用有指定 this 值和参数的函数的结果。

3、call与apply的用法

3.1、call的用法

一个函数继承另外一个函数的属性

代码:

function Guizimo(name, age) {
  this.name = name
  this.age = age
}

function Zimo(name, age) {
  Guizimo.call(this, name, age)
  this.sex = 'man'
}

console.log(new Zimo('guizimo', '24'))
// Zimo{name: 'guizimo', age: '24', sex: 'man'}

代码解析:

  1. Guizimo中定义了nameage两个属性。

  2. Zimo中使用call,让Zimo继承了Guizimo的属性

  3. new一个Zimo,拥有Guizimo中定义的属性。

3.2、apply的用法

合并两个数组

代码:

const array = ['a', 'b'];
const elements = [0, 1, 2];

array.push.apply(array, elements);

console.log(array);
// ["a", "b", 0, 1, 2]

代码解析:

  1. 定义了两个数组arrayelements

  2. arraypush上使用apply,将elements数组传入。

  3. array拥有elements的元素。

4、手写call与apply

4.1、手写call

思路:

  1. 处理好传入的this和参数。

  2. 创建一个临时属性接收当前this

  3. 将参数作为属性置入创建的的临时属性中,并执行得到结果。

  4. 清理掉刚创建的临时属性。

  5. 返回结果

普通版

/**
 * 自定义实现call
 * @returns {*|void}
 */
Function.prototype.myCall = function () {
  // 判断this的指向
  if (typeof this !== 'function') {
    throw new Error('type error')
  }
  // 处理参数,拿到传入的this与参数
  const args = Array.from(arguments)
  // 如果传入的this为null时,指向全局的window对象
  const newThis = args.shift() || window
  // 创建一个临时属性接受this,注意唯一性,可能会覆盖原有的属性
  newThis.fn = this
  // 将参数作为属性置入
  const res = newThis.fn(...args)
  // 删除属性,防止污染
  delete newThis.fn
  // 返回
  return res
}

进阶

想了一下这个还是可以改进的。在使用newThis.fn = this的时候,这个fn需要保持唯一性,不覆盖外部传入的属性,因此可以使用Symbol,注意使用Symbol之后就不再使用newThis.fn,而是newThis[fn]

进阶版

/**
 * 自定义实现call,优化版
 * @returns {*}
 */
Function.prototype.myCallPlus = function () {
  // 判断this的指向
  if (typeof this !== 'function') {
    throw new Error('type error')
  }
  // 处理参数,拿到传入的this与参数
  const args = Array.from(arguments)
  // 如果传入的this为null时,指向全局的window对象
  const newThis = args.shift() || window
  // 创建一个唯一的key
  let fn = Symbol()
  // 接收临时this
  newThis[fn] = this
  // 将参数作为属性置入
  const res = newThis[fn](...args)
  // 删除属性,防止污染
  delete newThis[fn]
  // 返回
  return res
}

4.2、手写apply

思路:

call几乎一样,区别是在参数的处理

  1. 处理好传入的this和参数。
  2. 创建一个临时属性接收当前this
  3. 将参数作为属性置入创建的的临时属性中,并执行得到结果。
  4. 清理掉刚创建的临时属性。
  5. 返回结果。

普通版

/**
 * 自定义实现apply
 * @param thisArg
 * @param args
 * @returns {*|void}
 */
Function.prototype.myApply = function (thisArg, args) {
  // 判断this的指向
  if (typeof this !== 'function') {
    throw new Error('type error')
  }
  // 处理外部传递过来的this,如果this为null时,指向全局的window对象
  const newThis = thisArg || window
  // 创建一个Null对象,用作返回的载体
  let res = null
  // 创建一个临时属性接受this,注意唯一性,可能会覆盖原有的属性
  newThis.fn = this
  // 将参数作为属性置入
  res = newThis.fn(...args)
  // 删除属性,防止污染
  delete newThis.fn
  // 返回
  return res
}

进阶版

/**
 * 自定义实现apply,进阶版
 * @param thisArg
 * @param args
 * @returns {*}
 */
Function.prototype.myApplyPlus = function (thisArg, args) {
  // 判断this的指向
  if (typeof this !== 'function') {
    throw new Error('type error')
  }
  // 处理外部传递过来的this,如果this为null时,指向全局的window对象
  const newThis = thisArg || window
  // 创建一个唯一的key
  const fn = Symbol()
  // 接收临时this
  newThis[fn] = this
  // 传入参数,调用新属性
  const res = newThis[fn](...args)
  // 删除属性,防止污染
  delete newThis[fn]
  // 返回
  return res
}

5、使用场景

5.1 call的使用场景

1、调用父构造函数

在子构造函数中可以使用call调用父构造函数,这种方式可以实现继承,这种使用子构造方法之后,都会拥有父构造函数的属性。

// 父构造方法
function Father (name, age) {
  this.name = name
  this.age = age
}

// 子构造方法
function Child(name, age) {
  Father.call(this, name, age)
  this.sex = 'man'
}

console.log(new Child('guizimo', '24'))
// Child {name: 'guizimo', age: '24', sex: 'man'}

2、调用匿名函数

创建了一个匿名函数,通过callobj作为属性给到匿名函数使用。

const obj = {
  name: 'guizimo',
  age: '24'
};  // 此处;不可省略

(function (id) {
  this.print = function () {
    console.log(`id : ${id} ${this.name} : ${this.age}`);
  };
  this.print();
}).call(obj, 1);

// id : 1 guizimo : 24

这里我遇到了一个小插曲:

有一个报错:Uncaught TypeError: {(intermediate value)(intermediate value)} is not a function

原因是在匿名函数使用之前,需要加上;

3、设置指定的上下文this

给某个函数指定上下文this,这是call比较通常的用法

const obj = {
  name: 'guizimo',
  description: 'a good boy'
};

function fn() {
  let reply = [this.name, 'is', this.description].join(' ');
  console.log(reply);
}

fn.call(obj);
// guizimo is a good boy

4、使用call不携带this参数

在使用call的时候不给到指定的this参数,这时this的值为全局对象

var name = 'guizimo'  // 注意这里使用 var 来声明

function fn() {
  console.log(`${this.name} is a good boy`)
}

fn.call()
// guizimo is a good boy

注意

在严格模式下,this的值将会是undefined。在调用call的时候,会给到报错。

5.2、apply的使用场景

1合并数组

使用apply可以将push改造为类似concat的效果,但是不会创建一个新的数组,而是在原数组上直接合并。

const array = ['a', 'b'];
const elements = [0, 1, 2];

array.push.myApply(array, elements);

console.log(array);
// ["a", "b", 0, 1, 2]

2、兼容max和min的限制

apply有一个好处就是可以避免循环。

const numbers = [5, 6, 2, 3, 7];

let max = Math.max.apply(null, numbers); // 等同与 Math.max(5, 6, 2, 3, 7)
let min = Math.min.apply(null, numbers); // 等同与 Math.min(5, 6, 2, 3, 7)

看起来使用了apply的收益还是不大,当我们使用Math.max和Math.min是会出现一个问题

JavaScript引擎参数长度上限: 65536)。

为了避免这个问题,可以将数组切块后循环传入目标方法。

function minOfArray(arr) {
  let min = Infinity;
  let QUANTUM = 32768;

  for (let i = 0, len = arr.length; i < len; i += QUANTUM) {
    const submin = Math.min.apply(null, arr.slice(i, Math.min(i + QUANTUM, len)));
    min = Math.min(submin, min);
  }

  return min;
}

let min = minOfArray([5, 6, 2, 3, 7]);

博客说明与致谢

文章所涉及的部分资料来自互联网整理,其中包含自己个人的总结和看法,分享的目的在于共建社区和巩固自己。

引用的资料如有侵权,请联系本人删除!

感谢勤劳的自己,个人博客,GitHub,公众号【归子莫】,小程序【子莫说】

如果你感觉对你有帮助的话,不妨给我点赞鼓励一下,好文记得收藏哟!

幸好我在,感谢你来!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

天翎MyApps低代码平台唯品会金牌客服管理系统

项目痛点&#xff1a; 作为一家知名的创新大型电商&#xff0c;唯品会秉承“传承品质生活&#xff0c;提升幸福体验”的企业使命。基于客服铁军锻造项目&#xff0c;实现基于金牌案例的提交、评审、积分&#xff0c;学习功能。 项目中的晋升机制、案例产生学习机制、双激励机制…

STM32之按键驱动的使用和自定义(MultiButton)

原始Github地址 Github地址 修改后 调整内容 将宏定义转换成配置结构体 头文件 #ifndef _MULTI_BUTTON_H_ #define _MULTI_BUTTON_H_#include "stdint.h" #include "string.h"//According to your need to modify the constants. //#define TICKS_IN…

HarmonyOS/OpenHarmony应用开发-Stage模型UIAbility组件使用(四)

UIAbility组件与UI的数据同步 基于HarmonyOS的应用模型&#xff0c;可以通过以下两种方式来实现UIAbility组件与UI之间的数据同步。 1.EventHub&#xff1a;基于发布订阅模式来实现&#xff0c;事件需要先订阅后发布&#xff0c;订阅者收到消息后进行处理。 2.globalThis&…

MySQL日常操作记录

1.查看MySQL版本 select version();2.快速复制表结构&#xff0c;不包含相关主键及约束 create table user_test as select * from user where 12;3.uuid select uuid(),uuid_short();4.替换uuid()里的’-‘为’’ select replace(uuid(),-,);5.md5摘要 select md5(uuid()…

HBase(一)HBase v2.2 高可用多节点搭建

最近刚刚完成了HBase相关的一个项目,作为项目的技术负责人,完成了大部分的项目部署,特性调研工作,以此系列文章作为上一阶段工作的总结. 前言 其实目前就大多数做应用的情况来讲,我们并不需要去自己搭建一套HBase的集群,现有的很多云厂商提供的服务已经极大的方便日常的应用使…

rce题目

<?php include "flag.php"; highlight_file(__FILE__); if(isset($_GET[HECTF])) { if (; preg_replace(/[^\W]\((?R)?\)/, NULL, $_GET[HECTF])) { if (!preg_match(/pos|high|op|na|info|dec|hex|oct|pi/i, $_GET[HECTF])) { eval(…

NSSCTF刷web(2)

[NISACTF 2022]bingdundun~ bingdundun处感觉像文件包含,改upload为index 发现确实,猜测会补一个后缀.php 那常规文件包含都不行了,这里还有一个文件上传的功能,考虑phar协议 <?php$phar new Phar("test.phar"); $phar->startBuffering(); $phar->setStu…

C++入门学习(2)

思维导图&#xff1a; 一&#xff0c;缺省参数 如何理解缺省参数呢&#xff1f;简单来说&#xff0c;缺省参数就是一个会找备胎的参数&#xff01;为什么这样子说呢&#xff1f;来看一个缺省参数就知道了&#xff01;代码如下&#xff1a; #include<iostream> using std…

算法与数据结构-排序

文章目录 一、如何分析一个排序算法1.1 排序算法的执行效率1.1.1 最好情况、最坏情况、平均情况时间复杂度1.1.1.1 最好、最坏情况分析1.1.1.2 平均情况分析 1.1.2 时间复杂度的系数、常数 、低阶1.1.3 比较次数和交换&#xff08;或移动&#xff09;次数 1.2 排序算法的内存消…

Go语言之重要数组类型map(映射)类型

通过切片&#xff0c;我们可以动态灵活存储管理学生姓名、年龄等信息&#xff0c;比如 names : []string{"张三","李四","王五"} ages : []int{23,24,25} fmt.Println(names) fmt.Println(ages)但是如果我想获取张三的年龄&#xff0c;这是一个…

C# 同构字符串

205 同构字符串 给定两个字符串 s 和 t &#xff0c;判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t &#xff0c;那么这两个字符串是同构的。 每个出现的字符都应当映射到另一个字符&#xff0c;同时不改变字符的顺序。不同字符不能映射到同一个字符…

详细介绍MATLAB中的图论算法

MATLAB是一种功能强大的编程语言和环境,提供了许多用于图论算法的工具和函数。图论是研究图及其属性和关系的数学分支,广泛应用于计算机科学、网络分析、社交网络分析等领域。在MATLAB中,我们可以使用图论算法来解决各种问题,如最短路径问题、最小生成树问题、最大流问题等…

国产MCU-CW32F030开发学习-BH1750模块

国产MCU-CW32F030开发学习-BH1750模块 硬件平台 CW32_48F大学计划板CW32_IOT_EVA物联网开发评估套件BH1750数字型光照强度传感器 BH1750 BH1750是一款数字型光照强度传感器&#xff0c;能够获取周围环境的光照强度。其测量范围在0~65535 lx。lx勒克斯&#xff0c;是光照强…

【电路效应】信号处理和通信系统模型中的模拟电路效应研究(SimulinkMatlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码、Simulink仿真实现 &#x1f4a5;1 概述 在信号处理和通信系统模型中&#xff0c;模拟电路效应研究是指考虑到实际电路的特性对信号进行建模和分析的过程。模拟电路效应…

Kubernetes—集群故障排查

一、用 Kubectl 调试 Kubernetes 节点 准备开始 你必须拥有一个 Kubernetes 的集群&#xff0c;同时你必须配置 kubectl 命令行工具与你的集群通信。 建议在至少有两个不作为控制平面主机的节点的集群上运行本教程。 你的 Kubernetes 服务器版本必须不低于版本 1.2. 要获知版…

C#基础--委托

C#基础–委托 C#基础–委托 简单说它就是一个能把方法当参数传递的对象&#xff0c;而且还知道怎么调用这个方法&#xff0c;同时也是粒度更小的“接口”&#xff08;约束了指向方法的签名&#xff09; 一、什么是委托&#xff0c;委托的本质是什么&#xff1f; 跟方法有点类似…

云迁移第二波热潮来袭,你准备好了吗?

最近&#xff0c;云迁移再次被频繁提及&#xff0c;企业对云迁移的需求量有回升趋势&#xff0c;究其根本&#xff0c;主要有以下原因&#xff1a; 企业数字化进程加速&#xff0c;本地上云需求强劲 根据《2021中国企业上云指数洞察报告》&#xff0c;我国实体经济上云渗透率…

深入理解Java虚拟机(二)Java内存区域与内存溢出异常

一、前言 对于Java程序员来说&#xff0c;在虚拟机自动内存管理机制的帮助下&#xff0c;不再需要为每一个new操作去写配对的delete/free代码&#xff0c;不容易出现内存泄漏和内存溢出问题&#xff0c;看起来由虚拟机管理内存一切都很美好。不过&#xff0c;也正是因为Java程序…

kafka接收外部接口的数据,并实现转发

目录 一、什么是kafka 二、kafka接收外部接口数据 三、kafka收到数据后转发 四、kafka总结 一、什么是kafka Kafka是一种分布式流式处理平台&#xff0c;最初由LinkedIn开发。它设计用于高吞吐量、低延迟的数据处理&#xff0c;能够处理大规模的实时数据流。Kafka采用发布…

关系型数据库设计规则

目录 1.1 表、记录、字段 1.2 表的关联关系 1.2.1 一对一关联&#xff08;one-to-one&#xff09; 1.2.2 一对多关系&#xff08;one-to-many&#xff09; 1.2.3 多对多&#xff08;many-to-many&#xff09; 1.2.4 自我引用&#xff08;Self reference&#xff09; 关系…