深入剖析JavaScript中的this(下)

五、事件处理函数的this

5.1 事件绑定

<button id="btn">点击我</button>function handleClick(e) {console.log(this);// <button id="btn">点击我</button>
}document.getElementById('btn').addEventListener('click', handleClick, false); 
// <button id="btn">点击我</button>document.getElementById('btn').onclick = handleClick; 
// <button id="btn">点击我</button>

根据上述代码我们可以得出:当通过事件绑定来给DOM元素添加事件,事件将被绑定为当前DOM对象。

5.2 内联事件

<button onclick="handleClick()" id="btn1">点击我</button>
<button onclick="console.log(this)" id="btn2">点击我</button>function handleClick(e) {console.log(this); // window
}

图片

根据上述代码我们可以得出:点击btn1打印window对象,this指向window;点击btn2打印当前DOM对象,this指向当前DOM对象。

浏览器有三种添加事件监听的方式。
直接在标签内写 οnclick=“fn”,此时this指向window;

在js中 el.οnclick=fn,此时this指向el;

在js中用 el.attachEvent或者el.addEventListener(),此时this指向el。

六、定时器中的this

先看下面的两段代码,看看有何不同之处?

function foo() {setTimeout(function() {console.log(this); // window}, 1000)
}foo();
var name = "前端技术营";
var obj = {name: "张三",foo: function() {console.log(this.name); // 张三setTimeout(function() {console.log(this.name); // 前端技术营}, 1000)}
}obj.foo();

通过这两段代码发现,函数 foo 内部this指向为调用它的对象,即为obj 。而定时器中的this指向为 window。

如果没有特殊指向,setInterval和setTimeout的回调函数中this的指向都是window。这是因为JS的定时器方法是定义在window下的,但是平时很多场景下,都需要修改this的指向,那么有什么办法让定时器中的this跟包裹它的函数绑定为同一个对象呢?

(1)利用闭包,在外部函数中将this存为一个变量,回调函数中使用该变量,而不是直接使用this。

var name = "前端技术营";var obj = {name: "张三",foo: function() {console.log(this.name); // 张三var that = this;setTimeout(function() {console.log(that.name); // 张三}, 1000)}
}obj.foo();

(2)使用bind实现。

var name = "前端技术营";var obj = {name: "张三",foo: function() {console.log(this.name); // 张三setTimeout(function() {console.log(this.name); // 张三}.bind(this), 1000)}
}obj.foo();

七、箭头函数中的this

在使用普通函数之前对于函数的this绑定,需要根据这个函数如何被调用来确定其内部this的绑定对象。而且常常因为调用链的数量或者是找不到其真正的调用者对 this 的指向模糊不清。在箭头函数出现后其内部的 this 指向不需要再依靠调用的方式来确定。

箭头函数不绑定 this ,它只会从作用域链的上一层继承 this。箭头函数中的this是定义函数的时候绑定,而不是在执行函数的时候绑定。箭头函数中,this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

看下面代码来对比普通函数与箭头函数中的this绑定。

var obj = {foo: function() {console.log(this); // obj},bar: () => {console.log(this); // window}
}obj.foo();
obj.bar();

上述代码中,同样是通过 obj. 方法调用一个函数,但是函数内部this绑定确是不同,只因一个数普通函数一个是箭头函数。

箭头函数中的this继承于作用域链上一层对应的执行上下文中的this。

var name = '前端技术营';
var obj = {name: '张三',foo: () => {console.log(this.name); // 前端技术营}
}
obj.foo();

this是继承于作用域链上一层执行上下文的this,这里的箭头函数中的this.name,箭头函数本身与foo平级是key:value的形式,也就是箭头函数本身所在的对象为obj,而obj的上一层执行上下文就是window,因此这里的this.name实际上表示的是window.name,因此输出的是"前端技术营"。

var obj = {foo: function() {console.log(this); // objvar bar = () => {console.log(this); // obj}bar();}
}
obj.foo();

在普通函数中,bar 执行时内部this被绑定为全局对象,因为它是作为独立函数调用。但是在箭头函数中呢,它却绑定为 obj 。跟父级函数中的 this 绑定为同一对象。

var obj = {foo: () => {console.log(this); // windowvar bar = () => {console.log(this); // window}bar();}
}
obj.foo();

这个时候怎么又指向了window了呢?当我们找bar函数中的this绑定时,就会去找foo函数中的this绑定。因为它是继承于它的。这时 foo 函数也是箭头函数,此时foo中的this绑定为window而不是调用它的obj对象。因此 bar函数中的this绑定也为全局对象window。

箭头函数的 this 是从当前箭头函数逐级向上查找 this,如果找到了,则作为自己的 this 指向,如果没有则继续向上查找。而父级的 this 是可变的,所以箭头函数的 this 也可跟随父级而改变。

var name = "前端技术营";function Foo(val) {let bar = new Object();bar.name = val;bar.getVal = () => {console.log(this);};return bar;
}
let baz = new Foo("李四");
baz.getVal(); // Foo {}

八、return中的this

先看一段代码:

function Foo() {  this.name = '张三';  return {};  
}
var obj = new Foo();  
console.log(obj.name); //undefined

再看一段代码:

function Foo() {  this.name = '张三';  return function(){};
}
var obj = new Foo();  
console.log(obj.name); //undefined

再看一段代码:

function Foo() {  this.name = '张三';  return '1';
}
var obj = new Foo();  
console.log(obj.name); // 张三

再看一段代码:

function Foo() {  this.name = '张三';  return undefined;
}
var obj = new Foo();  
console.log(obj.name); // 张三

通过上面的四段代码会发现什么?如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象,那么this还是指向函数的实例。

还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。

function Foo() {  this.name = '张三';  return null;
}
var obj = new Foo();  
console.log(obj.name); // 张三

九、被忽略的this

规则总有例外,这里也一样。如果你把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,应用的是默认绑定规则。

var name = '张三';function foo() {// 张三,this指向windowconsole.log(this.name); 
}
var obj = {name: '李四'
}
foo.call(null);

什么情况下会传入null呢?

function foo(a, b) {console.log("a:" + a + ", b:" + b);
}
// 把数组 “展开” 成参数
foo.apply(null, [2, 3]);// 使用 bind() 进行柯里化 (预先设置一些参数)
var bar = foo.bind(null, 2);
bar(3);

如果函数不关心 this 的话,你仍然需要传入一个占位值,这时 null 肯定是一个不错的选择,如果某个函数确实使用了 this,默认规则把 this 绑定到全局对象,这将导致不可预计的后果,比如修改全局对象。

十、this绑定优先级

显示绑定:通过使用.call()、.apply()或者.bind()等方法来明确指定函数内部的this值。这样就能将函数与特定的上下文进行绑定。

隐式绑定:当 函数引用 有 上下文对象 时,如 obj.foo() 的调用方式,foo() 内的 this 指向 obj。也就是说,谁调用函数,函数内的 this 就指向谁(无论是普通对象、还是全局对象),this 永远指向最后调用它的那个对象(不考虑箭头函数)。

默认绑定:表现为一个独立函数的直接调用,函数调用时无任何调用前缀的情景。

new绑定:new绑定就是通过new一个构造函数的方式进行绑定,this会指向被new出来的那个对象。

我们首先来看下隐式绑定和显示绑定哪个优先级更高。

function foo() {console.log(this.name)
}
var obj1 = {name: '张三',foo: foo
}
var obj2 = {name: '李四',foo: foo
}obj1.foo(); // 张三
obj2.foo(); // 李四obj1.foo.call(obj2); // 李四
obj2.foo.call(obj1); // 张三

这段到面可以看到,显示绑定优先级比隐式绑定更高。

现在我们需要搞清楚 new 绑定和隐式绑定的优先级谁高。

function foo(name) {this.name = name
}
var obj1 = {foo: foo
}
var obj2 = {}obj1.foo('张三');
console.log(obj1.name); // 张三obj1.foo.call(obj2, '李四');
console.log(obj2.name); // 李四var bar = new obj1.foo('王五');
console.log(obj1.name); // 张三
console.log(bar.name); // 王五

这段到面可以看到 new 绑定比隐式绑定优先高,但是 new 绑定和显示绑定谁的优先级高呢?

function foo(name) {this.name = name
}
var obj1 = {}var bar = foo.bind(obj1);
bar('张三');
console.log(obj1.name); // 张三var baz = new bar('李四');
console.log(obj1.name); // 张三
console.log(baz.name); // 李四

new 修改了硬绑定调用 bar() 中的 this。

this 存在很多使用场景,当多个场景同时出现时,就要根据优先级来判断 this 的指向。优先级:new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定。

总结

  1. 定义在全局作用域中的普通函数中的this指向window对象: 作为函数调用,作为内部函数调用
    setTimeout,setInterval。

  2. 事件处理函数中的this指向触发事件的标签元素 DOM对象绑定事件。

  3. 构造函数中的this指向当前正在创建的对象(严格模式下必须使用new)作为构造函数去调用,即new的时候,指向构造函数创建的那个实例 。

  4. 对象方法中的this指向:在使用对象方法调用方式时,this被自然绑定到该对象。

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

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

相关文章

CSS基础:4种简单选择器的详解

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。大专生&#xff0c;2年时间从1800到月入过万&#xff0c;工作5年买房。 分享成长心得。 261篇原创内容-公众号 后台回复“前端工具”可获取开发工具&#xff0c;持续更新中 后台回复“前端基础…

MySQL客户端安装并配置免密登录

最近在写脚本时需要向MySQL数据库中存储数据&#xff0c;且脚本运行的服务器与MySQL服务器不是同一台服务器&#xff0c;而且需要保证MySQL密码的安全性&#xff0c;不能在脚本中暴露&#xff0c;所以就需要在服务器上安装MySQL客户端&#xff0c;并配置免密登录。 一、虚拟机…

Git安装教程(图文安装)

Git Bash是git(版本管理器)中提供的一个命令行工具&#xff0c;外观类似于Windows系统内置的cmd命令行工具。 可以将Git Bash看作是一个终端模拟器&#xff0c;它提供了类似于Linux和Unix系统下Bash Shell环境的功能。通过Git Bash&#xff0c;用户可以在Windows系统中运行基于…

Vue项目登录页实现获取短信验证码的功能

之前我们写过不需要调后端接口就获取验证码的方法,具体看《无需后端接口,用原生js轻松实现验证码》这个文章。现在我们管理后台有个需求,就是登录页面需要获取验证码,用户可以输入验证码后进行登录。效果如下,当我点击获取验证码后能获取短信验证码: 这里在用户点击获取…

Linux 线程:线程同步、生产者消费者模型

目录 一、死锁 二、条件变量实现线程同步 1、为什么需要线程同步 2、条件变量、同步、竞态条件 3、条件变量函数&#xff1a;初始化 销毁 等待 唤醒 4、实现简单的多线程程序 不唤醒则一直等待 实现线程同步 三、生产者消费者 1、借助超市模型理解 2、优点 四、基于…

数字乡村创新实践探索:科技赋能农业现代化与乡村治理体系现代化同步推进

随着信息技术的飞速发展&#xff0c;数字乡村作为乡村振兴的重要战略方向&#xff0c;正日益成为推动农业现代化和乡村治理体系现代化的关键力量。科技赋能下的数字乡村&#xff0c;不仅提高了农业生产的效率和品质&#xff0c;也为乡村治理带来了新的机遇和挑战。本文旨在探讨…

Linux 环境下 Redis基础配置及开机自启

Linux 环境下 Redis基础配置及开机自启 linux环境安装redis<redis-6.0.5.tar.gz> 1-redis基本安装配置 解压 获取到tar包后&#xff0c;解压到相关目录&#xff0c;一般是将redis目录放在usr/local/redis目录下&#xff0c;可以使用-C指定到解压下目录 tar -zvxf re…

Java数据结构栈

栈&#xff08;Stack&#xff09; 概念 栈是一种先进后出的数据结构。 栈的使用 import java.util.Stack; public class Test {public static void main(String[] args) {Stack<Integer> s new Stack();s.push(1);s.push(2);s.push(3);s.push(4);System.out.println(s…

3. python练习题3-自由落体

3. python练习题3-自由落体 【目录】 文章目录 3. python练习题3-自由落体1. 目标任务2. 解题思路3. 知识回顾-%占位符格式化处理3.1 概述3.2 占位符的多种用法3.3 格式化操作符辅助指令3.4 将整数和浮点数格式化为字符串 4. 解题思路4.1 球第1次下落4.2 球第2次下落 5. 最终代…

day60 动态规划part17

这两题看了自己写的笔记还不懂的话&#xff0c;看看这个up的思路就行&#xff1a; https://space.bilibili.com/111062940/search/video?keyword%E5%9B%9E%E6%96%87 647. 回文子串 中等 提示 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。 回…

学习【RabbitMQ入门】这一篇就够了

目录 1. RabbitMQ入门1-1. 同步调用1-2. 异步调用1-3. MQ技术选型1-4. RabbitMQ介绍消息模式 1-5. SpringAMQPBasic QueueWork QueueFanout ExchangeDirect ExchangeTopic Exchange消息转换器 1. RabbitMQ入门 1-1. 同步调用 优势&#xff1a; 时效性强&#xff0c;等待到结…

Windows 2008虚拟机安装、安装VM Tools、快照和链接克隆、添加硬盘修改格式为GPT

一、安装vmware workstation软件 VMware workstation的安装介质&#xff0c;获取路径&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1AUAw_--yjZAUPbsR7StOJQ 提取码&#xff1a;umz1 所在目录&#xff1a;\vmware\VMware workstation 15.1.0 1.找到百度网盘中vmwa…

Mysql的基本命令

1 服务相关命令 命令描述systemctl status mysql查看MySQL服务的状态systemctl stop mysql停止MySQL服务systemctl start mysql启动MySQL服务systemctl restart mysql重启MySQL服务ps -ef | grep mysql查看mysql的进程mysql -uroot -hlocalhost -p123456登录MySQLhelp显示MySQ…

8.list容器的使用

文章目录 list容器1.构造函数代码工程运行结果 2.赋值和交换代码工程运行结果 3.大小操作代码工程运行结果 4.插入和删除代码工程运行结果 5.数据存取工程代码运行结果 6.反转和排序代码工程运行结果 list容器 1.构造函数 /*1.默认构造-无参构造*/ /*2.通过区间的方式进行构造…

java-网络编程socket-聊天室-03

完整版代码 java -聊天室的代码: 用于存放聊天室的项目的代码和思路导图https://gitee.com/to-uphold-justice-for-others/java---code-for-chat-rooms.git 多线程并发问题 多线程的并发问题主要出现在当一个程序涉及多个线程同时运行时&#xff0c;这些线程可能会同时访问共…

Java:接口应用(Clonable 接口和深拷贝)

目录 1.引例2.Object中clone方法的实现3.Cloneable接口讲解4.深拷贝和浅拷贝4.1浅拷贝4.2深拷贝 1.引例 Java 中内置了一些很有用的接口, Clonable 就是其中之一. Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法。必…

精密电阻阻值表和电容容值表

前面2张是电阻阻值表&#xff08;E-96/0603/1%&#xff09; 常见贴片电容的容值表

解决windows下Qt Creator显示界面过大的问题

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;QT❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 问题描述 解决方法 1、右击此电脑--->属性 2、点击高级系统设置--->点击环境变量 3、 找到系…

【美团笔试题汇总】2023-08-26-美团春秋招笔试题-三语言题解(CPP/Python/Java)

&#x1f36d; 大家好这里是KK爱Coding &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新小米近期的春秋招笔试题汇总&#xff5e; &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f…

想要安装ssh?

SSH&#xff08;Secure Shell&#xff09;是一种加密的网络协议&#xff0c;用于在不安全的网络上安全地进行远程登录和执行命令。它通过加密通信和身份验证机制&#xff0c;确保用户和系统之间的通信是安全的。 SSH协议的主要功能包括&#xff1a; 加密通信&#xff1a;SSH使…