简单易懂的JavaScript中的this指针

文章目录

  • 默认绑定 / 隐式绑定
  • 如何调整this
    • 1.用变量固定this
    • 2.箭头函数
    • 2.bind
    • 3.call/apply(一次性)

默认绑定 / 隐式绑定

要找this指针指向谁,我们首先要做的是:找到一个明确的对象,这个对象调用了含有this指针的函数,则:
谁调用函数,this指向谁。

而如果找不到,this 默认指向全局对象(浏览器中是 windowNode.js 中是 global)。
以下面几个例子说明一下:

const obj = {name: 'Alice',Hello: function() {console.log(this.name);}
};obj.Hello(); // 输出 "Alice" - this 指向 obj

这个例子中的this指针所属函数为Hello,谁调用了Hello()?是obj,所以this.name就是要在obj中找到name


const obj = {name: 'Alice',Hello: function() {console.log(name);}
};const obj1 = {};
obj1.name = 'Joan';
obj1.Hello = obj.Hello; 
obj1.Hello();// 输出 "Joan" - this 指向 obj1

这里调用Hello的对象成了obj1,自然地,name就需要找obj1中的了。


function test() {console.log(this.x);
}var obj = {};
obj.x = 1;
obj.m = test;obj.m(); // 1

在这个例子中,this指针所属函数为test, test赋值m,此时mtest指向同一个函数,那么obj调用m()就等同于调用test()this.x就是要在obj中找到那个x。其实和上一个例子本质上大差不差。

要是我们换一种方法呢?


function test() {console.log(this.x);
}
test();// 非严格模式:可能输出 undefined// 严格模式:报错(Cannot read property 'x' of undefined)

这个时候我们会发现找不到谁调用了test,这就遵循默认绑定规则,this 会默认绑定到全局对象(非严格模式)或 undefined(严格模式)。
当然,这是因为我们把test绑定到了全局,并且非要在全局没有x的情况下找出一个x。
那么如果我们不找那个x呢?


function test() {console.log(this);
}
test(); // 输出 window 对象

此时,在非严格模式下会输出window,在严格模式下,this 还是undefined
这是删掉.x的情况,但这样改显然不符合这个函数原先的想法。那么如果我们依然把this指向全局,并且在全局中给他一个x呢?


let x = 1;
function test() {console.log(this.x);
}
test(); // 1

这时候就会输出全局中的x。


let x = 1;
function test() {return this.x;
}
console.log(test());  //1

当然换一种方式也是一样的。


function test() {return this;
}
function test1(func) {func();
}test1(test())  //window

在这个例子中,test函数以参数的形式被调用,有没有那么一个明确的对象调用了test呢?我们找不到这个调用者,所以this指针指向window


其他情况:

除了上面的情况,还有一些情况可以拎出来单独说一下,我们回到第一个例子,给它额外加个定时器:

const obj = {name: 'Alice',Hello: function() {setTimeout(function(){    console.log(this.name);})}
};obj.Hello(); // undefined(非严格模式)或 报错(严格模式)

现在我们的this所属函数是setTimeout,但是是谁调用了它呢?我们找不到了。也就是说这种情况下,this 会丢失原来的绑定,默认指向全局对象。而全局中找不到name

定时器中的回调函数的this指针总是指向window对象


如何调整this

出现上面的this 丢失原来的绑定的情况不是我们希望看到的,那么有没有什么办法能按照原本的想法输出呢?

1.用变量固定this

const obj = {name: 'Alice',Hello: function() {let that = this;setTimeout(function(){    console.log(that.name);})}
};obj.Hello(); // "Alice"

我们用一个额外的变量先把this固定下来,下面再利用这个变量进行操作就可以了。

2.箭头函数

箭头函数没有自己的 this,它会捕获所在上下文的 this 值。所以我们可以这么写:

const obj = {name: 'Alice',Hello: function() {setTimeout(() => {    console.log(this.name); // this 继承 Hello 的 this(即 obj)}, 0);}
};
obj.Hello(); // 输出 "Alice"

可以想象箭头函数就是把它所在的那个函数趟平了setTimeout现在不是一个拦路石了,它变成了单纯的“一条路”,那我们直接往上走就好,现在this通往的函数就是Hello

2.bind

如果不方便用变量固定this,箭头函数也不太合适呢?
比如这种情况:

class Person {constructor(){this.name = "Alice";}getName(){return this.name;}
}let change = {name: "Joan"
}let a = new Person()
change.getName = a.getName
console.log(change.getName())  //"Joan"

这种情况下,如果我们想要this.name始终为"Alice",就可以使用bind了,bind的意义指定一个this的指定对象,可以在于返回一个始终指向该对象的值。

class Person {constructor(){this.name = "Alice";this.getName = this.getName.bind(this);}getName(){return this.name;}
}let change = {name: "Joan"
}let a = new Person()
change.getName = a.getName
console.log(change.getName()) //"Alice"

我们添加了一句代码:

this.getName = this.getName.bind(this);

为了便于理解,我们可以把它看成

function1 = function.bind(this)

意思就是,我给你一个function1 ,这个方法是我把function和一个this指向绑定到了一起,从今往后你在任何地方调用function1 ,它的指向永远固定,而回到情境中,function1是this.getName,function也是this.getName,也就是说,我们给this.getName重新赋值了一下。现在它永远指向Alice了。

为了加深理解,我们可以多看一个例子:

function Hello() {console.log(this.name);
}const person = { name: 'Bob' };const BobHello = Hello.bind(person);
BobHello();         //  输出"Bob"

这个会容易理解一点是不是?

3.call/apply(一次性)

既然提到了bind,就不得不提一下call/apply了。他们的作用相似,但有一些微妙的差别,比方说bind并不会立即执行,但这两个会,并且他们是一次性的,不能接收,如下:

function Hello() {console.log(this.name);
}const person = { name: 'Bob' };Hello.call(preson);  //  输出"Bob"
Hello.apply(preson); //  输出"Bob"
const BobHello = greet.bind(person);
BobHello();         //  输出"Bob"

而call和apply的区别如下:

  1. apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入
    当参数是动态数组时,apply 比 call 更方便。
    改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
function fn(...args){console.log(this,args);
}
let obj = {myname:"张三"
}fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window

当第一个参数为null、undefined的时候,默认指向window(在浏览器中)

fn.apply(null,[1,2]); // this指向window
fn.apply(undefined,[1,2]); // this指向window
  1. call方法的第一个参数也是this的指向,后面传入的是一个参数列表(逐个传入)
    call 通常更快,因为 apply 需要处理数组。
    跟apply一样,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
function fn(...args){console.log(this,args);
}
let obj = {myname:"张三"
}fn.call(obj,1,2); // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // this指向window

同样的,当第一个参数为null、undefined的时候,默认指向window(在浏览器中)

fn.call(null,[1,2]); // this指向window
fn.call(undefined,[1,2]); // this指向window

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

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

相关文章

Spring MVC数据绑定和响应 你了解多少?

数据绑定的概念 在程序运行时,Spring MVC接收到客户端的请求后,会根据客户端请求的参数和请求头等数据信息,将参数以特定的方式转换并绑定到处理器的形参中。Spring MVC中将请求消息数据与处理器的形参建立连接的过程就是Spring MVC的数据绑…

BMS工具箱用来执行贝叶斯模型平均(BMA)计算模块

贝叶斯模型平均(Bayesian Model Averaging,BMA)是一种用于处理模型不确定性的统计方法,通过结合多个模型的预测结果来提高预测的准确性和鲁棒性。在 MATLAB 中,可以使用专门的工具箱(如 BMS 工具箱&#xf…

Java内存马的检测与发现

【网络安全】Java内存马的检测与发现 一、Java内存马的现象二、检测思路三、重点关注类四、检测方法1. 检查方法(FindShell)2. 检查方法(sa-jdi)3. 检查方法(arthas-boot)4. 检查方法(cop.jar&a…

ISP有感自发

一、黑电平 由于传感器,即便在无光的情况下,依然会产生微小的暗电流,这些暗电流可能是噪点会影响后期的调试。因此,我们便将这些电流处理为0,成为纯黑的颜色。可以在源头消除这些误差。 如何矫正黑电平: …

数字信号处理-大实验1.1

MATLAB仿真实验目录 验证实验:常见离散信号产生和实现验证实验:离散系统的时域分析应用实验:语音信号的基音周期(频率)测定 目录 一、常见离散信号产生和实现 1.1 实验目的 1.2 实验要求与内容 1.3 实验…

【SSL证书系列】https双向认证中客户端认证的原理

HTTPS双向认证(也称为双向SSL/TLS认证)是一种增强安全性的机制,其中客户端和服务器都需要验证彼此的数字证书,以确保双方身份的真实性。以下是其核心原理和步骤的详细解析: 一、双向认证的核心目标 双向身份验证&#…

Linux系统编程——fork函数的使用方法

在 Linux 系统编程 中,fork() 函数是创建新进程的关键系统调用。fork() 在当前进程(父进程)中创建一个几乎完全相同的子进程。子进程和父进程从调用 fork() 的位置继续执行,但它们是两个独立的进程,每个进程都有自己的…

LLMs之ChatGPT:《Connecting GitHub to ChatGPT deep research》翻译与解读

LLMs之ChatGPT:《Connecting GitHub to ChatGPT deep research》翻译与解读 导读:这篇OpenAI帮助文档全面介绍了将GitHub连接到ChatGPT进行深度代码研究的方法、优势和注意事项。通过连接GitHub,用户可以充分利用ChatGPT强大的代码理解和生成…

flutter 视频通话flutter_webrtc

flutter 比较热门的库 flutter_webrtc | Flutter package agora_rtc_engine | Flutter package 我使用的是flutter_webrtc 下面是官方推荐的demo库 GitHub - flutter-webrtc/flutter-webrtc-demo: Demo for flutter-webrtc 其中 https://demo.cloudwebrtc.com:8086/ 已经停…

同设备访问php的多个接口会有先后等待问题

同设备访问php的多个接口会有先后等待问题 这个现象的核心原因通常与 PHP 的 Session 锁机制 有关,即使两个接口表面上无关联,也可能因共享 Session 导致请求排队。以下是详细分析: 关键背景:PHP 的 Session 锁机制 PHP 的 Sessi…

【免杀】C2免杀技术(三)shellcode加密

前言 shellcode加密是shellcode混淆的一种手段。shellcode混淆手段有多种:加密(编码)、偏移量混淆、UUID混淆、IPv4混淆、MAC混淆等。 随着杀毒软件的不断进化,其检测方式早已超越传统的静态特征分析。现代杀软往往会在受控的虚…

【论文阅读】Dip-based Deep Embedded Clustering with k-Estimation

摘要 近年来,聚类与深度学习的结合受到了广泛关注。无监督神经网络,如自编码器,能够自主学习数据集中的关键结构。这一思想可以与聚类目标结合,实现对相关特征的自动学习。然而,这类方法通常基于 k-means 框架,因此继承了诸如聚类呈球形分布等各种假设。另一项常见假设(…

.NET8关于ORM的一次思考

文章目录 前言一、思路二、实现ODBC>SqlHelper.cs三、数据对象实体化四、SQL生成SqlBuilder.cs五、参数注入 SqlParameters.cs六、反射 SqlOrm.cs七、自定义数据查询八、总结 前言 琢磨着在.NET8找一个ORM,对比了最新的框架和性能。 框架批量操作性能SQL控制粒…

CVE-2025-31258 macOS远程视图服务沙箱逃逸漏洞PoC已公开

苹果公司近日针对macOS系统中新披露的CVE-2025-31258漏洞发布补丁,该漏洞可能允许恶意应用程序突破沙箱限制,获取未授权的系统资源访问权限。在安全研究员Seo Hyun-gyu公开概念验证(PoC)利用代码后,该漏洞已在macOS Se…

21.第二阶段x64游戏实战-分析采集物偏移

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 上一个内容:20.第二阶段x64游戏实战-代码实现遍历周围 上一个内容里把遍历周围的npc和玩家…

2025年全新 GPT 4.5 AI 大模型 国内免费调用

一、中转账号注册 第一步:打开宙流AI中转站,网站地址如下: 宙流AI中转站 按照上图中的操作步骤,通过邮箱进行账号注册,注册完毕后,网站初始会分配0.4刀的免费额度,获取额度后,即可…

基于事件驱动和策略模式的差异化处理方案

一、支付成功后事件驱动 1、支付成功事件 /*** 支付成功事件** author ronshi* date 2025/5/12 14:40*/ Getter Setter public class PaymentSuccessEvent extends ApplicationEvent {private static final long serialVersionUID 1L;private ProductOrderDO productOrderDO;…

简述Web和HTTP

目录 HTTP概述 非持续连接和持续连接 非持续连接 持续连接 HTTP报文格式 HTTP请求报文 HTTP响应报文 用户与服务器的交互:cookie Web缓存 条件GET方法 Web 即万维网,是一个基于超文本和 HTTP 协议的全球性信息系统,通过浏览器访问…

基于STM32的LCD信号波形和FFT频谱显示

一、项目准备 主要利用LCD驱动中的画点和画连线函数,驱动是正点原子给我写好了的画点和画线的函数等些相关函数 void LCD_Draw_Circle(u16 x0,u16 y0,u8 r); //画圆 void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2); //画线 二、画波形图函数实…

Go 语言即时通讯系统开发日志-day1:从简单消息收发 Demo 起步

Go语言即时通讯系统开发日志day1,主要模拟实现的一个简单的发送消息和接受消息的小demo,因为也才刚学习go语言的语法,对go的json、net/http库了解不多,所以了解了一下go语言的encoding/json库和net/http库,以及websock…