JavaScript 中的 this 到底指向谁? 今天彻底把它征服!!!

概述

我们知道,JS 中的 this 指向问题,一直是一个经久不衰的重点和考点。那么它到底难不难呢?怎么说呢?它也难也不难,你要是不把它理清楚,随意变化一下就能把你绊倒;但是你要是把他理清楚了以后,无论它如何变换也难不倒你!那这个 this 里面到底有什么道道呢?今天我们就一起把它彻底征服!

this 到底指向谁之 this 绑定规则

咱们不弯弯绕绕了,直接开宗明义,this 的绑定规则一共有四种,如下

  1. 默认绑定
  2. 隐式绑定
  3. 显式绑定
  4. new 绑定

那这 4 种绑定分别是什么意思呢?文字略显苍白,接下来我们直接通过代码来看一下这 4 种绑定是什么样的情况,直接明了!

测试代码,后文用于说明这 4 种绑定方式的测试都基于本段代码

function Foo() {console.log(this)
}var obj1 = {name: 'obj1',age: 13, foo: Foo
}
var obj = {name: 'obj',obj1: obj1
}function Person(name) {console.log(this) this.name = name 
}

1. 默认绑定:指向window,严格模式下指向undefined

Foo() // window

2. 隐式绑定,指向调用对象(不管多少层调用,始终指向最后发起调用的对象,也就是最后一层)

obj1.foo()  // obj1
obj.obj1.foo()  // 指向 obj1 ,而不是 obj

3. new 绑定  :指向构造函数

var p = new Person()	// 16行输出 Person {}

4. 显式绑定 — call/apply/bind:绑定谁就是谁

Foo.call(obj) // obj
Foo.apply(obj1) // obj1
Foo.bind(obj)() // obj

绑定的优先级比较:默认绑定 < 隐式绑定 < apply/call < bind < new

// I)   new > bind
var bindFn = Foo.bind('abc')
new bindFn()  // 打印的是Foo{} 而不是 String:{ 'abc' },说明new > bind
// II)  bind > apply/call
bindFn.call(123)  // String:{ 'abc' } 而不是 Number { 123 },说明 bind > call 
// III) apply/call > 隐式绑定
obj1.foo.apply(111) // Number { 111 } 而不是 ojb1, 说明 apply/call > 隐式绑定
// IV)  隐式 > 默认 
obj1.foo() // ojb1 而不是 window ,说明 隐式 > 默认

this规则之外的特殊情况

var obj2 = {name: 'obj2'
}

1. 显式绑定的this传入null/undefined,使用默认绑定规则(非严格模式下)

Foo.call(null) 	// window

2. 间接函数引用,采用默认规则

(obj1.foo = obj2.foo)() // window

3. 要注意的是,在严格模式中,null 就是 null,undefined 就是 undefined

'use strict'
function Foo() {console.log(this)
}
Foo(null) // null
Foo(undefined) // undefined

箭头函数的用法和规则

1. 不会绑定this,arguments等属性

2. 不能作为构造函数来使用,因为其没有原型

3. 箭头函数没有自己的this,内部的this需按照作用域链一步步的向上查找

this指向练习

1. 例1

var name = 'window'
var person1 = {name: 'person1',foo1: function () {console.log(this.name)},foo2: () => console.log(this.name),// 注意这里有两个 this 要分析foo3: function () { return function () { console.log(this.name)}},// 这里也有两个 this 要分析(虽然只存在一个)foo4: function () {return () => {console.log(this.name)}}
}var person2 = { name: 'person2' }person1.foo1(); // person1
person1.foo1.call(person2); // person2person1.foo2(); // window
person1.foo2.call(person2); // windowperson1.foo3()(); // window
person1.foo3.call(person2)(); // window
person1.foo3().call(person2); // person2person1.foo4()(); // person1
person1.foo4.call(person2)(); // person2
person1.foo4().call(person2); // person1

分析

  • 注意函数调用时,是普通函数还是箭头函数,是独立调用还是其他
  • person1.foo1() 隐式绑定指向 person1 ;
  • person1.foo1().call(person2) 显式绑定指向 person2;
  • person1.foo2() 箭头函数无自己的 this,到上层作用域查找指向 window;
  • person1.foo2.call(person2) 同上,注意,箭头函数无自己的 this,所以绑定无效!
  • person1.foo3()() 回调函数独立调用指向 window
  • person1.foo3.call(person2)() 同上
  • person1.foo3().call(person2) 显式绑定指向 person2
  • person1.foo4()() 箭头函数无自己的 this,到上层作用域查找指向 person1
  • person1.foo4.call(person2)() 箭头函数无自己的 this,到上层作用域查找指向 person2
  • person1.foo4().call(person2) 箭头函数绑定无效,到上层作用域查找指向 person1

2. 例2

var name = 'window'
function Person (name) {this.name = namethis.foo1 = function () {console.log(this.name)},this.foo2 = () => console.log(this.name),this.foo3 = function () {return function () {console.log(this.name)}},this.foo4 = function () {return () => {console.log(this.name)}}
}
var person1 = new Person('person1')
var person2 = new Person('person2')person1.foo1() // person1
person1.foo1.call(person2) // person2person1.foo2() // person1
person1.foo2.call(person2) // person1person1.foo3()() // window
person1.foo3.call(person2)() // window
person1.foo3().call(person2) // person2person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1

分析

  • person1.foo1() 隐式绑定指向 person1
  • person1.foo1.call(person2) 显式绑定指向 person2
  • person1.foo2() 上层作用域中查找指向 person1
  • person1.foo2.call(person2) 同上指向 person1
  • person1.foo3()() 默认绑定指向 window
  • person1.foo3.call(person2)() 同上指向 window
  • person1.foo3().call(person2) 显式绑定指向 person2
  • person1.foo4()() 上层作用域中查找指向 person1
  • person1.foo4.call(person2)() 上层作用域中查找指向 person2
  • person1.foo4().call(person2) 上层作用域中查找指向 person1

3. 例3

var name = 'window'
function Person (name) {this.name = namethis.obj = {name: 'obj',foo1: function () {return function () {console.log(this.name)}},foo2: function () {return () => {console.log(this.name)}}}
}
var person1 = new Person('person1')
var person2 = new Person('person2')person1.obj.foo1()() // window
person1.obj.foo1.call(person2)() // window
person1.obj.foo1().call(person2) // person2person1.obj.foo2()() // obj
person1.obj.foo2.call(person2)() // person2
person1.obj.foo2().call(person2) // obj

分析

  • person1.obj.foo1()() 默认绑定指向window
  • person1.obj.foo1.call(person2)() 同上指向 window
  • person1.obj.foo1().call(person2) 显式绑定指向 person2
  • person1.obj.foo2()() 上层作用域中查找指向 obj
  • person1.obj.foo2.call(person2)() 上层作用域中查找指向 person2
  • person1.obj.foo2().call(person2) 上层作用域中查找指向 obj

4. 例4

var length = 10
function fn() {console.log(this.length)
}var obj = {length: 5,method: function(fn) {fn()arguments[0]()}
}obj.method(fn, 1)// 10
// 2
  • 分析:第一次执行fn(),this指向window对象,输出10。第二次执行arguments[0],相当于arguments调用方法,this指向arguments,而这里传了两个参数,故输出arguments长度为2。

总结

本篇文章详细讲述了 JS 中 this 的指向问题,相信大家只要仔细阅读,并将例题琢磨透,以后再遇到 this 指向的问题一定会所向披靡,战无不胜!!亦菲,彦祖们,我们下次见~

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

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

相关文章

VUE+TS使用elementUI的el-checkbox双重v-for循环做勾选

html部分 <template><div class"hello"><el-form :model"elForm"> <!-- cities对象数组形式 --><el-form-item v-for"(item, topIndex) in cities" :key"topIndex"> <!--item.checked 是每一个item…

最新游戏陪玩语音聊天系统3.0商业升级独立版本源码+搭建教程

首发价值29800元的最新商业版游戏陪玩语音聊天系统3.0商业升级独立版本源码。 下 载 地 址 &#xff1a; runruncode.com/php/19748.html 1. 新增人气店员轮播功能。 2. UI界面优化&#xff0c;包括游戏图标展示和分类展示的改进。 3. 增加动态礼物打赏功能。 4. 新增礼…

SQL底层执行过程

MySQL 的查询流程 客户端请求连接器 负责与客户端的通信,是半双工模式&#xff08;半双工(Half Duplex)数据传输指数据可以在一个信号载体的两个方向上传输,但是不能同时传输。&#xff09;&#xff0c;验证请求用户的账户和密码是否正确&#xff0c;③如果用户的账户和密码验…

Codigger数据篇(下):数据安全的全方位保障

在数字化浪潮中&#xff0c;数据已成为现代企业的核心财富。Codigger作为领先的数据服务平台&#xff0c;深知数据安全对于用户的重要性&#xff0c;因此在深挖数据价值的同时&#xff0c;我们始终坚守数据安全防线。 一、双重加密技术保障 Codigger平台运用先进的加密通信和…

MATLAB初学者入门(22)—— 哈希算法

哈希算法在计算机科学中广泛用于数据管理、安全、错误检测等多种应用。在MATLAB中&#xff0c;可以通过内置函数或自定义函数来实现哈希算法&#xff0c;以便于数据的快速检索、唯一性验证和数据完整性检查。 案例分析&#xff1a;使用哈希算法快速检索数据 假设我们有一个大型…

vue2.7与vue2.6、vue3的区别

官网链接&#xff1a;https://v2.cn.vuejs.org/v2/guide/migration-vue-2-7.html -组合式与选项式 选项式&#xff1a;export default { 各种选项关键字名&#xff0c;都定好了&#xff0c;无需引入&#xff0c;配对放置即可}

RabbitMQ Transport indicated EOF 总结

Rabbitmq报错 pika.exceptions.IncompatibleProtocolError StreamLostError (‘Transport indicated EOF‘,) 网上的答案是端口写错了&#xff0c;产生此报错的原因是我将port写成了15672&#xff0c;15672是rabbitmq管理页面的端口 port 15672rabbitmq需要通过端口5672连…

stm32单片机开发一、中断之外部中断实验

stm32单片机的外部中断和定时器中断、ADC中断等都由stm32的内核中的NVIC模块控制&#xff0c;stm32的中断有很多中&#xff0c;比如供电不足中断&#xff0c;当供电不足时&#xff0c;会产生的一种中断&#xff0c;这么多中断如果都接在CPU上&#xff0c;或者说CPU去处理&#…

Leetcode—2639. 查询网格图中每一列的宽度【简单】

2024每日刷题&#xff08;121&#xff09; Leetcode—2639. 查询网格图中每一列的宽度 实现代码 class Solution { public:int func(int num) {if(num 0) {return 1;}int len 0;while(num ! 0) {len;num / 10;}return len;}vector<int> findColumnWidth(vector<ve…

[开发|JAVA] asdf安装adoptium openJDK

编写原因 asdf使用install命令安装adoptium openJDK&#xff0c;会因为网络的原因导致安装失败&#xff0c;本篇文章介绍在adoptium官方下载压缩包安装openJDK。 openjdk下载地址 Eclipse Temurin 安装 以temurin-17.0.99为例演示安装 在temurin-17.0.99安装包所在地址打…

如何测试响应式网站

我们每天通过多种设备访问互联网。移动电话&#xff0c;台式机/笔记本电脑&#xff0c;平板电脑&#xff0c;平板电脑…我们所掌握的设备数量已经增长为天文数字。作为消费者&#xff0c;体验很棒。我们可以随时随地在任何设备上自由访问互联网。但对于Web开发人员&#xff0c;…

双非二本找工作前的准备day15

学习目标&#xff1a; 每天复习代码随想录上的题目1-2道算法&#xff08;时间充足可以继续&#xff09; 今日碎碎念&#xff1a; 1&#xff09;双非本真难受... 力扣刷题 算法 力扣20&#xff1a;20. 有效的括号 class Solution {public boolean isValid(String s) {//这里…

类加载子系统之类的生命周期(待完善)

0、前言 文中大量图片来源于 B站 黑马程序员 0.1、类加载子系统在 JVM 中的位置 类加载器负责的事情是&#xff1a;加载、链接、解析 0.2、与类的生命周期相关的虚拟机参数 参数描述-XX:TraceClassLoading打印出加载且初始化的类 1、类的生命周期 堆上的变量在分配空间的时…

【C++STL详解(三)】------vector的介绍与使用

目录 前言 一、关于数组 二、vector的介绍 三、vector的使用 Ⅰ、默认成员函数 1.构造函数 2.赋值重载 3.析构函数 Ⅱ、容量 1.size(&#xff09; 2.capacity() 3.empty() 4.resize() 5.reserve() Ⅲ、遍历操作 1.迭代器 begin() end()&#xff08;正向迭代器…

ubuntu18源码安装postgresql15.2数据库

由于官方的源只能安装到pg10这个版本&#xff0c;整了好一会没有成功就改为源码安装了。 下载源代码源码并解压 wget https://ftp.postgresql.org/pub/source/v15.2/postgresql-15.2.tar.gztar -xf postgresql-15.2.tar.gz cd postgresql-15.2/ 安装C相关开发库和编译工具 ap…

Java解决找出字符串中第一个匹配项的下标

Java解决找出字符串中第一个匹配项的下标 01 题目 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 示…

网络扫描技术

网络扫描技术是信息安全领域中的一项重要技术&#xff0c;它旨在发现网络中的设备、服务、漏洞和配置情况&#xff0c;为安全评估、渗透测试或恶意攻击做准备。以下是几种主要的网络扫描技术&#xff1a; PING扫射&#xff08;Ping sweep&#xff09;&#xff1a; 通过发送ICMP…

element 分页切换时:current-page无效 页数不会跟着一起切换

问题回溯&#xff1a;使用el-pagination组件 选择切换当前分页 页数为2 问题结果&#xff1a;el-pagination组件 当前页切换失败 一直都是 1&#xff0c;接口传参分页数据是2&#xff0c;打印当前分页也是2 解决方案1&#xff1a;使用 current-page参数 .sync 修饰符 解决方案2…

安装nuxt3环境

安装nuxt3&#xff0c;按照nuxt官网&#xff0c;node版本需在18以上 执行 npx nuxilatest init ‘xxx’终端控制台可能会报错&#xff1a; ERROR Error: Failed to download template from registry: Failed to download https://raw.githubusercontent.com/nuxt/starter/temp…

rust将json字符串直接转为map对象或者hashmap对象

有些时候我们还真的不清楚返回的json数据里面到底有哪些数据&#xff0c;数据类型是什么等&#xff0c;这个时候就可以使用批处理的方式将json字符串转为一个对象&#xff0c;然后通过这个对象的get方法来获取json里面的数据。 pub async fn test_json(&self) {let json_st…