当前位置: 首页 > news >正文

说一下this,实现apply、call

理解this

在ES5中,this的指向始终坚持一个原理:“this永远指向最后调用它的那个对象”,切记这句话。下面看几个例子。

例一

var obj = {name: 'zhangsan',say: function() {console.log(this.name);}
}obj.say() // zhangsan 

最基本的使用,函数say是obj调用的,函数中的this指向obj,所以打印的是zhangsan

例二

var name = 'lisi'
var obj = {name: 'zhangsan',say: function() {console.log(this.name);}
}var func = obj.say
func() // lisi 

把say赋给变量func,然后调用func,前面没有调用对象,那么此时的调用对象就是全局对象window,因此this指向window,所以打印lisi

例三

var name = 'lisi'
var obj = {name: 'zhangsan',say: function() {console.log(this.name);}
}window.obj.say() // zhangsan 

this永远指向最后调用它的那个对象,这里虽然前面加了个window,但是最后调用者还是obj,因此this指向obj。

可以看出:this的指向并不是在创建的时候就可以确定的。在ES5中,this永远指向最后调用它的那个对象

例四

var name = 'lisi'var obj = {name: 'zhangsan',say: function() {console.log(this.name); // zhangsanfunc()function func() {console.log(this.name); // lisi}},}obj.say() 

say的调用者是obj,say函数内部的this就指向obj,因此第一次打印zhangsan。再调用func,此时func的调用者是window,func函数内部的this就指向window,因此第二次打印lisi

如何改变this指向

改变this指向有以下几种方法:

  • 使用ES6的箭头函数
  • 把this存一下然后调用:_this=this
  • 使用apply、call、bind改变this指向

箭头函数

ES6中的箭头函数可以避免ES5中很多this的坑。箭头函数中的this始终指向函数定义时的this,而非执行时。箭头函数中没有this绑定,必须通过查找作用域链来决定其值。如果箭头函数被非箭头函数包含,则this绑定的是最近一次非箭头函数的this

例五

var name = 'lisi'var obj = {name: 'zhangsan',func1: function() {console.log(this.name);},func2: function() {setTimeout(function() {this.func1()}, 1000)}
}obj.func2() // this.func1 is not a function 

如上代码,调用func2报错,因为最终调用setTimeout的对象是window,函数里的this指向window,而window中并没有func1,所以报错。

这里补充个知识点:匿名函数的this永远指向window。this永远指向最后调用它的那个对象。我们来找找最后调用匿名函数的对象是哪个,很尴尬,匿名函数没有名字,所以是没有办法被其他对象调用的,所以只能在window下执行。因此匿名函数的this始终指向window。

var name = 'lisi'var obj = {name: 'zhangsan',func1: function() {console.log(this.name);},func2: function() {// 使用箭头函数setTimeout(() => {this.func1()}, 1000)}
}obj.func2() // zhangsan 

使用箭头函数即可解决问题,此时this就是func2函数中的this,而func2是obj调用的,因此this实际指向obj。

_this=this

简单把this存一下即可

例六

var name = 'lisi'var obj = {name: 'zhangsan',func1: function() {console.log(this.name);},func2: function() {// this存一下在匿名函数里使用var _this = thissetTimeout(function() {_this.func1()}, 1000)}
}obj.func2() // zhangsan 

apply、call

apply和call都是用来改变this指向的,作用相同,用法稍微有点不同。

MDN中apply定义如下:

apply() 方法调用一个函数, 其具有一个指定的this值,以及作为一个数组(或类似数组的对象)提供的参数

用法:

func.apply(thisArg, ArrayArg) 

call定义如下:

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

用法:

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

详细用法见MDN。

例七

var name = 'lisi'var obj = {name: 'zhangsan',say: function(arg) {console.log(this.name, arg);}
}var func1 = obj.say // lisi 1
func1(1) 

此时this指向window,打印lisi 1。如果我们现在仍想要this指向obj,那么就可通过apply改变this:

var name = 'lisi'var obj = {name: 'zhangsan',say: function(arg) {console.log(this.name, arg);}
}var func1 = obj.say // zhangsan 2
func1.apply(obj, [2]) 

通过apply改变this指向,指向obj。然后通过一个数组传入调用时的参数。这就是apply用法的简单实例。call用法与其类似,只不过不是通过数组传参,而是需要显式传入多个参数。

var name = 'lisi'var obj = {name: 'zhangsan',say: function(arg) {console.log(this.name, arg);}
}var func1 = obj.say // zhangsan 3
func1.call(obj, 3) 

手动实现apply、call

理解了this、this指向、apply、call后我们可以思考一下如何手动实现apply、call。

手动实现apply

Function.prototype._apply = function(context) {// context是要绑定的this// this是调用的函数即func1if (typeof this !== "function") {throw new Error('只能在函数上调用')return}context = context || windowlet result = null// 把需要执行的函数赋给context,由context调用即可context.fn = thisif (arguments[1]) {result = context.fn(...arguments[1])} else {result = context.fn()}delete context.fnreturn result
}var name = 'lisi'var obj = {name: 'zhangsan',say: function(arg) {console.log(this.name, arg);}
}var func1 = obj.say // zhangsan 1
func1._apply(obj, [1]) 

总结步骤如下:

  • 判断调用对象是否为函数,即使是定义在函数的原型上的,但是可能出现使用 call 等方式调用的情况。* 判断传入上下文对象是否存在,如果不存在,则设置为 window 。* 将函数作为上下文对象的一个属性。* 判断参数值是否传入* 使用上下文对象来调用这个方法,并保存返回结果。* 删除刚才新增的属性* 返回结果手动实现call

Function.prototype._call = function(context) {// context是要绑定的this// this是调用的函数即func1if (typeof this !== "function") {throw new Error('只能在函数上调用')return}context = context || windowconst arg = Array.from(arguments).splice(1)let result = null// 把需要执行的函数赋给context,由context调用即可context.fn = thisresult = context.fn(...arg)delete context.fnreturn result
}var name = 'lisi'var obj = {name: 'zhangsan',say: function(arg) {console.log(this.name, arg);}
}var func1 = obj.say // zhangsan 2
func1._call(obj, 2) 

最后

为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

相关文章:

说一下this,实现apply、call

理解this 在ES5中,this的指向始终坚持一个原理:“this永远指向最后调用它的那个对象”,切记这句话。下面看几个例子。 例一 var obj {name: zhangsan,say: function() {console.log(this.name);} }obj.say() // zhangsan 最基本的使用&am…...

华为OD机试真题Python实现【总最快检测效率】真题+解题思路+代码(20222023)

总最快检测效率 题目 在系统、网络均正常情况下,组织核酸采样员和志愿者对人群进行核酸检测筛查。 每名采样员的效率不同,采样效率为N人/小时。 由于外界变化,采样员的效率会以M人/小时为粒度发生变化,M 为采样效率浮动粒度, M=N*10%,输入保证N*10%的结果为整数。 采样…...

【历史上的今天】2 月 23 日:Enigma 密码机申请专利;戴尔电脑创始人出生;Mellanox 收购 EZchip

整理 | 王启隆 透过「历史上的今天」,从过去看未来,从现在亦可以改变未来。 今天是 2023 年 2 月 23 日,在 2006 年的今天,都灵冬奥会自由式滑雪男子空中技巧决赛在意大利都灵萨奥兹杜尔克斯滑雪场举行。中国选手韩晓鹏战胜众多好…...

新手入门吉他推荐,第一把吉他从这十款选绝不踩雷!初学者吉他选购指南【新手必看】

一、新手购琴注意事项: 1、预算范围 一把合适的吉他对于初学者来说会拥有一个很好的音乐启蒙。选一款性价比高,做工材料、音质和手感相对较好的吉他自然不会是一件吃亏的事。**初学者第一把琴的预算,我觉得最低标准也是要在500元起&#xf…...

XSS注入进阶练习篇(三) XSS原型链污染

XSS原型链污染1.原型链的概念1.1 构造函数的缺点1.2 prototype 属性的作用1.3 原型链1.4 constructor属性1.5 prototype和__proto__2. 原型链污染2.1 原型链污染是什么?2.2 原型链污染的条件2.3 原型连污染实例2.3.1 hackit 20182.3.2 challenge-04223.总结1.原型链…...

【Java基础 下】 025 -- 阶段项目(斗地主)

目录 斗地主 一、斗地主游戏1 -- 准洗发(控制台版) 1、准备牌 2、洗牌 3、发牌 4、看牌 二、斗地主游戏2 -- 给牌排序①(利用序号进行排序) 2、洗牌 3、发牌 4、看牌 三、斗地主游戏2 -- 给牌排序②(给每一张牌计算价值…...

华为OD机试真题Python实现【矩阵最值】真题+解题思路+代码(20222023)

题目 给定一个仅包含0和1的n*n二维矩阵 请计算二维矩阵的最大值 计算规则如下 每行元素按下标顺序组成一个二进制数(下标越大约排在低位), 二进制数的值就是该行的值,矩阵各行之和为矩阵的值允许通过向左或向右整体循环移动每个元素来改变元素在行中的位置 比如 [1,0,1,1,1]…...

TypeScript笔记(三)

前言 上一篇文章我们主要介绍了TypeScript的基本类型boolean、number、string、void、null和undefine,还介绍了任意类型any和联合类型,这篇文章我们将会了解对象类型Interface和数组的相关知识。 对象的类型——接口 在TypeScript中,我们使…...

C++(41)-低版本升级到VS2019项目时遇到的问题(2)

1.错误码:MSB8066 代码为3 QT 项目老版本升级到新版本造成的, 1.重新加载项目: 扩展->QT VS tools->Open QT project files-> 2.添加QT模块:QT Project-Settings -> QT Modules2.无法打开QT的头文件 3.…...

git 实战应用

基本使用1.1、使用git想要让 git 对一个目录进行版本控制需要一下步骤:进入要管理的文件夹执行初始化命令git init查看目录下的文件状态git status管理指定文件// 添加指定文件 git add ***.txt// 添加未被管理的所有文件 git add .生成版本git commit -m 描述信息提…...

Linux重启命令shutdown与reboot

在linux命令中reboot是重新启动,shutdown -r now是立即停止然后重新启动,都说他们两个是一样的,其实是有一定的区别的。 shutdown 命令可以安全地关闭或重启Linux系统,它在系统关闭之前给系统上的所有登录用户提示一条警告信息。…...

华为OD机试真题 用 C++ 实现 - 静态扫描最优成本

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…...

拿下宁王、迪王的湖南裕能,还能“狂飙”多远?

文|智能相对论作者|Kinki近日,磷酸铁锂正极材料龙头湖南裕能正式登陆A股,上市当天市值超过了400亿元,投资者中一签可赚1.49万元,可谓近年低迷的资本市场中一支“大肉签”。不过在 “开门红”之后,湖南裕能的股价便一路…...

STM32FreeRTOS - 按键实现任务挂起和恢复

STM32f103C8T6 FreeRTOS - 按键实现任务挂起和恢复,按键按下时,LED任务执行,led闪烁,当led任务挂起,Led停止闪烁。1.STM32CubeMX 创建任务1.1配置GPIO按键配置外部中断触发GPIO绿灯,红灯配置输出模式1.2配置…...

华为OD机试真题Python实现【判断牌型】真题+解题思路+代码(20222023)

判断牌型 题目 五张牌每张牌由牌大小和花色组成 牌大小2~10 J Q K A 花色四种 红桃 黑桃 梅花 方块 四种花色之一 判断牌型 牌型一 同花顺 同一花色的顺子 如红桃 2 红桃 3 红桃 4 红桃 5 红桃 6牌型二 四条 四张相同数字+单张 红桃 A 黑桃 A 梅花 A 方块 A 加黑桃 A牌型三 葫…...

Kafka(7):生产者详解

1 消息发送 1.1 Kafka Java客户端数据生产流程解析 1 首先要构造一个 ProducerRecord 对象,该对象可以声明主题Topic、分区Partition、键 Key以及值 Value,主题和值是必须要声明的,分区和键可以不用指定。 2 调用send() 方法进行消息发送。 3 因为消息要到网络上进行传输…...

FPGA纯verilog代码实现H.264/AVC视频解码,提供工程源码和技术支持

目录1、前言2、硬件H.264/AVC视频解码优势3、vivado工程设计架构4、代码架构分析5、vivado仿真6、福利:工程代码的获取1、前言 本设计是一种verilog代码实现的低功耗H.264/AVC解码器(baseline ),硬件ASIC设计,不使用任何GPP/DSP等内核&#…...

通俗神经网络

经典的全连接神经网络 经典的全连接神经网络来包含四层网络:输入层、两个隐含层和输出层,将手写数字识别任务通过全连接神经网络表示,如 图3 所示。 图3:手写数字识别任务的全连接神经网络结构输入层:将数据输入给神经…...

网络工程(一) 简单的配置

网络工程 简单的配置 需求 两台交换机 两台路由器 两台PC AR1配置静态路由 system-view [HUAWEI]sysname ar1 [ar1]interface g 0/0/0 [ar1-G…0/0/0]ip address 192.168.2.1 24 [ar1-G…0/0/0]quit [ar1]interface g 0/0/1 [ar1-G…0/0/1]ip address 192.168.3.1 24 [ar1-G…...

深度剖析数据在内存中的存储(上)

目录 1. 数据类型介绍 1.1 类型的基本归类 2. 整形在内存中的存储 2.1 原码、反码、补码 2.2 大小端介绍 2.3 一道小题 本章重点 1. 数据类型详细介绍 2. 整形在内存中的存储:原码、反码、补码 3. 大小端字节序介绍及判断 4. 浮点型在内存中的存储解析 正文…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...