(十六)call、apply、bind介绍、区别和实现

news2024/11/17 13:44:35

函数中的this指向:

函数中的this指向是在函数被调用的时候确定的,也就是执行上下文被创建时确定的。在一个执行上下文中,this由调用者提供,由调用函数的方式来决定。

类数组对象arguments:

arguments只在函数(除了箭头函数)中存在的类数组参数对象,储存了我们传入的所有参数。

一、call

  • call(this, 参1, 参2, ...),第一个参数为this,后面是函数的一系列参数
  • 当第一个this参数为null、undefind时,默认this指向window
  • 函数立即调用
  • 原理:实际就是把函数放到call传入的第一个参数上,然后再调用该函数。
  • 实现:
/**
 * 手写call
 * @param {*} context
 * @param  {...any} args
 * @returns
 */
Function.prototype.mycall = function (context, ...args) {
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }
  //context不传,默认window
  let _this = context || window;

  //假如原来的context上存在fn属性会产生冲突,暂存一下。
  let temp = null;
  if (_this.fn) {
    temp = _this.fn;
  }

  _this.fn = this;
  // 调用函数
  let res = _this.fn(...args);

  if (temp) {
    //恢复_this对象上的原来的fn属性
    _this.fn = temp;
  } else {
    //删除_this对象上的fn属性
    delete _this.fn;
  }

  return res;

// 测试
let num = 1;
let obj = {
  num: 2,
  fn: "this is obj.fn",
};
function test(a, b, c, d) {
  let num = 1;
  console.log(this.num, "test参数", a, b, c, d);
}
test(4, 3, 2, 1);
// 调用myCall函数
test.mycall(obj, 4, 3, 2, 1);

// 检查obj本身的fn是否被修改
console.log(obj.fn);
};

在这里插入图片描述

注意:这个 undefind,是由于我使用 let 声明的变量。var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性。let 命令、const 命令、class 命令声明的全局变量,不属于顶层对象的属性,而是在一般声明环境 declsEnv 中。

从ES6开始,全局变量和顶层对象的属性开始分离,这意味着使用let和const声明的全局变量不再属于顶层对象的属性,如window对象。这是为了提供更好的模块化和封装,避免全局命名空间的污染。

二、apply

  • applyl(this, arr),第一个参数为this,第二个参数是一个参数数组
  • 当第一个this参数为null、undefind时,默认this指向window
  • 函数立即调用
  • 原理:和call类似,区别就是参数不同,call方法接受的参数是一个参数列表,而apply接受的是一个包含多个参数的数组。
  • 实现:
// 和myCall的不同之处1:参数
Function.prototype.myApply=function(context){
  	if(typeof this!== 'function'){
        throw new TypeError('type error')
    }
  	console.log("myApply", arguments, context, this);
  	let _this = context || window;

  	let temp = null;
  	if (_this.fn) {
    	temp = _this.fn;
  	}
  	_this.fn = this;
  	let res;
  	//   判断是否存在第二个参数
  	if (arguments[1]) {
    	res = _this.fn(...arguments[1]);
  	} else {
    	res = _this.fn();
  	}
 // 删除context对象上的fn属性
  	if (temp) {
    	_this.fn = temp;
  	} else {
    	delete _this.fn;
  	}
 	 return res;
}

// 测试
let num = 1;
let obj = {
  num: 2,
  fn: "this is obj.fn",
};
function test(a, b, c, d) {
  let num = 1;
  console.log(this.num, "test参数", a, b, c, d);
}
test(4, 3, 2, 1);
// 调用myCall函数
test.myApply(obj, [4, 3, 2, 1]);

// 检查obj本身的fn是否被修改
console.log(obj.fn);

结果:
在这里插入图片描述

三、bind

  • bind(this, 参1, 参2, ...),第一个参数为this,后面是函数的一系列参数
  • 当第一个this参数为null、undefind时,默认this指向window
  • bind方法的返回值是函数,不会立即调用
  • 原理:(1)bind返回的函数作为构造函数使用的时候,bind绑定的this会失效,到那时参数有效。(2)如何判断bind 是正常使用还是当构造函数,根据this。当为构造函数时,this指向实例对象(this的prototype在该构造函数上)
  • 实现:
Function.prototype.myBind = function (_this) {
  if (typeof this !== "function") {
    throw new TypeError("_this must be a function");
  }
  //获取参数
  let args0 = [...arguments].slice(1);
  //保存this,如果作为构造函数使用,此时this会指向实例
  let that = this;
  let context = _this||window;
  return function Fn(...args) {
    // 如果是new的形式来使用绑定函数的
    if (this instanceof Fn) {
      return new that(...args0, ...args);
    } else {
      return that.call(context , ...args0, ...args);
    }
  };
};

//测试:
function Point(x, y) {
  this.x = x;
  this.y = y;
}

// 情况1:正常调用bind函数
let testObj = {};
let MyPoint = Point.myBind(testObj, 0);
MyPoint(1);
console.log(testObj);
//情况2:bind返回的函数作为构造函数
let newObj = new MyPoint(2);
console.log(newObj);

结果:
在这里插入图片描述

四、区别:

1、相同点:
(1) call、apply、bind 都有改变this指向的作用。
(2)都可以给参数传参
2、不同点:
(1)call、bind 的第二个、后续参数是多个;而apply接收的第二参数是数组。
(2)call、apply会立即执行参数;而bind不会立即执行,得再调用才能执行。

参考:
1、https://blog.csdn.net/weixin_51472145/article/details/132566180
2、https://blog.csdn.net/weixin_40856652/article/details/124293144?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171348813116800178533534%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=171348813116800178533534&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-124293144-null-null.142v100pc_search_result_base1&utm_term=call%E3%80%81apply%E5%92%8Cbind%E7%9A%84%E5%8C%BA%E5%88%AB&spm=1018.2226.3001.4187
3、https://blog.csdn.net/sinat_41904410/article/details/104396112

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

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

相关文章

消息队列选型(RabbitMq、RocketMq、Kafaka)

文章目录 前言RabbitMq优点缺点 RocketMq优点缺点 Kafaka优点缺点 总结 前言 当引入消息队列时,常见的选择包括ActiveMQ、Kafka、RabbitMQ和RocketMQ。然而,近年来,ActiveMQ的活跃度已经下降,很多公司已经不再使用这款消息队列中…

DBeaver导入sql文件

DBeaver导入sql文件 下载数据库 数据库下载地址: https://www.begtut.com/mysql/mysql-sample-database.html数据库导入 获取sql文件中创建的数据库的名称,创建一个同名的数据库。 输入数据库的名称,设置字符集和排序规则 数据库创建完…

基于Springboot的人职匹配推荐系统

基于SpringbootVue的人职匹配推荐系统的设计与实现 开发语言:Java数据库:MySQL技术:SpringbootMybatis工具:IDEA、Maven、Navicat 系统展示 用户登录 首页 企业信息 岗位信息 新闻资讯 后台管理 用户管理 企业信息管理 岗位信…

【Linux高性能服务器编程】——高性能服务器框架

hello !大家好呀! 欢迎大家来到我的Linux高性能服务器编程系列之高性能服务器框架介绍,在这篇文章中,你将会学习到高效的创建自己的高性能服务器,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解&…

LeetCode:组合求和III之回溯法

题目 题目链接 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。题目图解 ** ** cpp代码 class …

大模型改变了NLP的游戏规则了吗

NLP已经死了吗? 自从 ChatGPT 横空出世以来,自然语言处理(Natural Language Processing,NLP) 研究领域就出现了一种消极的声音,认为大模型技术导致 NLP “死了”。在某乎上就有一条热门问答,大…

03-为啥大模型LLM还没能完全替代你?

1 不具备记忆能力的 它是零状态的,我们平常在使用一些大模型产品,尤其在使用他们的API的时候,我们会发现那你和它对话,尤其是多轮对话的时候,经过一些轮次后,这些记忆就消失了,因为它也记不住那…

Python 开发实现登陆和注册模块

Python 开发实现登陆和注册模块 一、案例介绍 本例设计一个用户登录和注册模块,使用Tkinter框架构建界面,主要用到画布、文本框、按钮等组件。涉及知识点:Python Tkinter界面编程、pickle数据存储。本例实现了基本的用户登录和注册互动界面…

纹理合成在AI去衣技术中的关键作用

随着人工智能技术的飞速发展,图像处理和计算机视觉领域取得了显著的进步。其中,AI去衣技术作为图像处理的一个分支,近年来引起了广泛关注。在AI去衣技术中,纹理合成发挥着至关重要的作用,它不仅能够保证图像的真实性&a…

YOLO算法改进Backbone系列之MogaNet:

卷积神经网络(ConvNets)一直是计算机视觉的首选方法。受灵长类视觉系统的启发,卷积层可以对具有区域密集连接和平移等方差约束的观测图像的邻域相关性进行编码。通过交错分层,ConvNets获得了被动增加的感受野,并善于识…

掼蛋比赛中的违规及处罚

一、越序违规及处罚 1、越序抓牌:抢先抓其他选手应抓的牌。 (1)越序抓牌但并没有看到的,一经发现须马上退回。 (2)越序抓牌已经看到的但是没有插入手牌中的,除马上退回外,可由裁判员…

OpenHarmony实战开发-文件上传下载性能提升指导。

概述 在开发应用时,要实现高效的客户端跟服务器之间数据交换,文件传输的性能是至关重要的。一个数据交换性能较低的应用会导致其在加载过程中耗费较长时间,在很多的场景造成页面卡顿,极大的影响了用户体验。相反,一个…

【进程地址空间】地址空间理解存在原因 | 深入理解页表写时拷贝虚拟地址

目录 地址空间深入理解 划分区域 理解地址空间 地址空间存在的意义 意义1 意义2 意义3 理解页表和写时拷贝 页表 写时拷贝 OS识别错误 理解虚拟地址 fork解释 上篇我们简单的学习了进程地址空间/页表/物理地址/虚拟地址/写时拷贝等概念。本篇深入理解下。 地址空…

分数求和(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;double a 0, b 1, result1 2, sum 0;int i 0;//循环运算&#xff1b;for (i 1; i <…

人工智能大模型培训老师叶梓 探索知识库问答中的查询图生成:处理多跳复杂问题的新方法

在人工智能领域&#xff0c;基于知识库的问答&#xff08;KBQA&#xff09;技术正变得越来越重要。它使得机器能够理解自然语言问题&#xff0c;并从结构化的知识库中检索答案。然而&#xff0c;面对多跳复杂问题&#xff0c;传统的KBQA方法往往力不从心。近期&#xff0c;研究…

Threejs绘制传送带

接下来会做一个MES场景下的数字孪生&#xff0c;所以开始做车间相关的模型&#xff0c;不过还是尽量少用建模&#xff0c;纯代码实现&#xff0c;因为一方面可以动态使用&#xff0c;可以调节长度和宽度等&#xff0c; 下面这节就做一个简单的传送带&#xff0c;这是所有车间都…

C++心决之类和对象详解(中篇)(封装入门二阶)

目录 1.类的6个默认成员函数 2. 构造函数 2.1 概念 2.2 特性 3.析构函数 3.1 概念 3.2 特性 4. 拷贝构造函数 4.1 概念 4.2 特征 5.赋值运算符重载 5.1 运算符重载 5.2 赋值运算符重载 5.3 前置和后置重载 7.const成员 8.取地址及const取地址操作符重载 1.类的…

Win 进入桌面黑屏,只有鼠标

大家好&#xff0c;我叫秋意零。 今天&#xff0c;遇到一个同事电脑进入桌面黑屏&#xff0c;只有鼠标。经过询问沟通&#xff0c;说是 Windows 突然进行了自动更新&#xff0c;更新之后桌面就黑了屏。经过查询是一个桌面进程没启动才会导致桌面黑屏。首先分两种情况&#xff0…

【linux】软件工具安装 + vim 和 gcc 使用(上)

目录 1. linux 安装软件途径 2. rzsz 命令 3. vim 和 gcc 使用 a. vim的基本概念 b. 命令模式下的指令 c. 底行模式下的指令 1. linux 安装软件途径 源代码安装rpm安装 -- linux安装包yum安装&#xff08;最好&#xff0c;可以解决安装源&#xff0c;安装版本&#xff0…

ArrayList与顺序表(1)

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x…