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

详解JavaScript中this指向

this 原理

this 是一个指针型变量,它指向当前函数的运行环境。

1.内存的数据结构

var obj = { foo: 5 };

img

img

2.函数

var obj = { foo: function () {} };

引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。

img

由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。

var f = function () {};
var obj = { f: f };// 单独执行
f();// obj 环境执行
obj.f();

3.环境变量

var f = function () {console.log(x);
};

函数体里面使用了变量x。该变量由运行环境提供。

上面代码,函数体内的this.x就是当前运行环境的 x

var f = function () {console.log(this.x);
};var x = 1;
var obj = {f: f,x: 2,
};// 单独执行
f();// obj 环境执行
obj.f();

上面代码中,函数f在全局环境执行,this.x指向全局环境的x

img

obj环境执行,this.x指向obj.x

img

this 的指向问题

在不同的场景中调用同一个函数,this 的指向也可能会发生变化,但是它永远指向其所在函数的真实调用者(谁调用就指向谁);如果没有调用者,this 就指向全局对象 window。

全局上下文

非严格模式和严格模式中 this 都是指向顶层对象(浏览器中是window

this === window;
console.log(this === window);
("use strict");
this === window;
console.log(this === window);
this.name = "若川";
console.log(this.name);

函数上下文

1.普通函数调用模式

​ 非严格模式下,默认绑定的 this 指向window

​ 严格模式下,默认绑定的 this 指向undefined

// 非严格模式
var name = "window";
var doSth = function () {console.log(this.name);
};
doSth(); // 'window'
// 非严格模式
let name2 = "window2";
let doSth2 = function () {console.log(this === window);console.log(this.name2);
};
doSth2(); // true, undefined

let没有给顶层对象中(浏览器是 window)添加属性,window.name2和window.doSth2都是undefined

// 严格模式
"use strict";
var name = "window";
var doSth = function () {console.log(typeof this === "undefined");console.log(this.name);
};
doSth(); // true,// 报错,因为this是undefined

回调函数,其实也是普通函数调用模式。

  var name = "The Window";var object = {name : "My Object",getNameFunc : function(){console.log(this, "ssss"); //这个this是objectreturn function(){return this.name;};}};alert(object.getNameFunc()());
//相当于
var f = object.getNameFunc()//返回一个匿名函数
f();//此时调用者是window,所以this会指向window
解决:
getNameFunc : function(){var that = this;return function(){return that.name;     //更改匿名函数中的this};
}

匿名函数的执行环境具有全局性,所以匿名函数 this 指向 window

2.箭头函数调用模式

先看箭头函数和普通函数的重要区别:

1、没有自己的thissuperargumentsnew.target绑定。

​ 也就是说无法通过callapplybind绑定箭头函数的this

2、不能使用new来调用。 3、没有原型对象。 4、不可以改变this的绑定。 5、形参名称不能重复。

var name = "window";
var student = {name: "若川",doSth: function () {onsole.log(this, "ssss");var arrowDoSth = () => {console.log(this.name);};arrowDoSth();},arrowDoSth2: () => {console.log(this.name);},
};
student.doSth(); // '若川'
student.arrowDoSth2(); // 'window'

箭头函数外的 this 是缓存的该箭头函数上层的普通函数的 this。如果没有普通函数,则是全局对象(浏览器中则是 window)。

因此我们可以修改外层函数 this 指向达到间接修改箭头函数 this 的目的。

function fn() {return () => {console.log(this.name);};
}
let obj1 = {name: "听风是风",
};
let obj2 = {name: "时间跳跃",
};
fn.call(obj1)(); // fn this指向obj1,箭头函数this也指向obj1
fn.call(obj2)(); //fn this 指向obj2,箭头函数this也指向obj2

3.构造函数调用模式

new 操作符调用时,this指向生成的新对象。

function Student(name) {this.name = name;console.log(this);
}
var result = new Student("若川");

4.call,apply 和 bind 调用模式

​ 通过 call、apply 以及 bind 方法改变 this 的指向,也称为显式绑定

let obj1 = {name: "听风是风",
};
let obj2 = {name: "时间跳跃",
};
let obj3 = {name: "echo",
};
var name = "行星飞行";function fn() {console.log(this.name);
}
fn(); //行星飞行
fn.call(obj1); //听风是风
fn.apply(obj2); //时间跳跃
fn.bind(obj3)(); //echo

​ 指向参数提供的是 null 或者 undefined,那么 this 将指向全局对象。

let obj1 = {name: "听风是风",
};
let obj2 = {name: "时间跳跃",
};
var name = "行星飞行";function fn() {console.log(this.name);
}
fn.call(undefined); //行星飞行
fn.apply(null); //行星飞行
fn.bind(undefined)(); //行星飞行

call、apply 与 bind 有什么区别?

1.call、apply 与 bind 都用于改变 this 绑定,但 call、apply 在改变 this 指向的同时还会执行函数,而 bind 在改变 this 后是返回一个全新的绑定函数,这也是为什么上方例子中 bind 后还加了一对括号 ()的原因。

2.bind属于硬绑定,返回的 this 指向无法再次通过 bind、apply 或 call 修改;call 与 apply 的绑定只适用当前调用,调用完就没了,下次要用还得再次绑。

3.call与 apply 功能完全相同,唯一不同的是 call 方法传递函数调用形参是以散列形式,而 apply 方法的形参是一个数组。在传参的情况下,call 的性能要高于 apply,因为 apply 在执行时还要多一步解析数组。

描述二:

let obj1 = {name: "听风是风",
};
let obj2 = {name: "时间跳跃",
};
var name = "行星飞行";function fn() {console.log(this.name);
}
fn.call(obj1); //听风是风
fn(); //行星飞行
fn.apply(obj2); //时间跳跃
fn(); //行星飞行
let boundFn = fn.bind(obj1); //听风是风
boundFn.call(obj2); //听风是风
boundFn.apply(obj2); //听风是风
boundFn.bind(obj2)(); //听风是风

描述三:

let obj = {name: "听风是风",
};function fn(age, describe) {console.log(`我是${this.name},我的年龄是${age},我非常${describe}!`);
}
fn.call(obj, "26", "帅"); //我是听风是风,我的年龄是26,我非常帅
fn.apply(obj, ["26", "帅"]); //我是听风是风,我的年龄是26,我非常帅

5.对象中的函数(方法)调用模式

​ 如果函数调用时,前面存在调用它的对象,那么 this 就会隐式绑定到这个对象上

如果函数调用前存在多个对象,this 指向距离调用自己最近的对象

function fn() {console.log(this.name);
}
let obj = {name: "行星飞行",func: fn,
};
let obj1 = {name: "听风是风",o: obj,
};
obj1.o.func(); //行星飞行

​ 隐式丢失

​ 最常见的就是作为参数传递以及变量赋值

// 参数传递
var name = "行星飞行";
let obj = {name: "听风是风",fn: function () {console.log(this.name);},
};function fn1(param) {param();
}
fn1(obj.fn); //行星飞行
// 变量赋值
var name = "行星飞行";
let obj = {name: "听风是风",fn: function () {console.log(this.name);},
};
let fn1 = obj.fn;
fn1(); //行星飞行

6.原型链中的调用模式

是指向生成的新对象。 如果该对象继承自其它对象。同样会通过原型链查找。

function Student(name) {this.name = name;
}
var s1 = new Student("若川");
Student.prototype.doSth = function () {console.log(this.name);
};
s1.doSth();

生成的新对象是 Student:{name: ‘若川’}

7.DOM事件处理函数调用

addEventerListener、attachEvent、onclick指向绑定事件的元素

<button class="button">onclick</button>
<ul class="list"><li>1</li><li>2</li><li>3</li>
</ul>
<script>var button = document.querySelector('button');button.onclick = function(ev){console.log(this);console.log(this === ev.currentTarget); // true}var list = document.querySelector('.list');list.addEventListener('click', function(ev){console.log(this === list); // trueconsole.log(this === ev.currentTarget); // true   //当前绑定事件的元素console.log(this);console.log(ev.target);       //当前触发事件的元素}, false);
</script>

一些浏览器,比如IE6~IE8下使用attachEventthis指向是window

8.内涵事件处理函数调用

<buttonclass="btn1"onclick="console.log(this === document.querySelector('.btn1'))"
>点我呀
</button>
<button onclick="console.log((function(){return this})());">再点我呀</button>

9.立即执行函数,谁调用 this 就是谁,因为是普通函数,是由 window 调用的

var myObject = {foo: "bar",func: function () {var self = this;console.log(this.foo); //barconsole.log(self.foo); //bar(function () {console.log(this.foo); //undefinedconsole.log(self.foo); //bar})();},
};
// 第二个
window.number = 2;
var obj = {number: 3,db1: (function () {console.log(this);this.number *= 4;return function () {console.log(this);this.number *= 5;};})(),
};
var db1 = obj.db1;
db1(); //自执行函数里的回调函数由window调用
obj.db1(); //自执行函数里的回调函数由obj调用
console.log(obj.number); // 15
console.log(window.number); // 40

myObject.func();

this 绑定优先级:

​ 显式绑定 > 隐式绑定 > 默认绑定

​ new 绑定 > 隐式绑定 > 默认绑定

相关文章:

详解JavaScript中this指向

this 原理 this 是一个指针型变量&#xff0c;它指向当前函数的运行环境。 1.内存的数据结构 var obj { foo: 5 };2.函数 var obj { foo: function () {} };引擎会将函数单独保存在内存中&#xff0c;然后再将函数的地址赋值给foo属性的value属性。 由于函数是一个单独的…...

c语言之在函数中传递指针

c语言中定义一个函数&#xff0c;如果说是形参一个数组&#xff0c;这个数组在编译后会变成一个指针变量 比如下面的代码例子 #include<stdio.h> void ff(char a[]) {a[1]r;a[4]r;printf("%d\n",a); }int main() {char a[]"peogeam";ff(a);printf(…...

vue2 插槽(默认插槽 slot 、具名插槽 v-slot 、作用域插槽 slot-scope -- 插槽传值 )

插槽&#xff1a;用于在子组件的指定位置插入指定内容&#xff0c;类似在电梯里挂的若干广告显示屏&#xff0c;可以给指定的位置传入指定的广告 单插槽&#xff08;匿名/默认插槽&#xff09; 父组件中&#xff08; 此时的 &#xff09; <Child><template><p…...

(第79天)单机转 RAC:19C 单机 到 19C RAC

前言 单机转 RAC 分为两种: 同版本迁移:可以使用 RMAN 或者 ADG 方式升级迁移:建议使用数据泵 或者 XTTS 方式升级迁移使用数据泵的方式与 (第72天)数据泵升级:11GR2 到 19C 步骤基本一致,这里不作演示,只演示使用 ADG 来进行同版本迁移。 升级前准备 本次测试尽量按…...

Spring Cloud微服务Actuator和Vue

目录 前言一、引入Actuator依赖二、暴露Actuator端点1. 配置文件2. 监控端点 三、自定义健康检查自定义健康检查类 四、vue前端代码五、监控器的优势六、监控指标的可视化1. Grafana2. Prometheus 七、安全性考虑安全配置示例 八、总结 前言 随着微服务架构的流行&#xff0c;…...

Iterator对象功能学习

package config;import java.util.Iterator; import java.util.Properties; import java.util.Set;/*** 这个类演示了如何使用Properties类来存储和访问键值对。* Properties类继承自Hashtable&#xff0c;因此它可以用来存储键值对数据&#xff0c;且支持同步。*/ public clas…...

Linux的一些基本指令

​​​​​​​ 目录 前言&#xff1a; 1.以指令的形式登录 2.ls指令 语法&#xff1a; 功能&#xff1a; 常用选项&#xff1a; 3.pwd指令 4.cd指令 4.1 绝对路径与相对路径 4.2 cd .与cd ..&#xff08;注意cd后先空格&#xff0c;然后两个点是连一起的&#xff0…...

【tips】Git使用指南

文章目录 一、Git介绍1. 什么是Git2.Git对比SVN3.Git安装 二.Git常用命令1. git config2. 初始化本地库3. 工作区、暂存区和版本库4. git add5. git commit6. git reset 与 git revertgit resetgit revert 三. Git 分支1.初识分支2.创建分支3.切换分支4.合并分支5.删除分支 四.…...

【字节序】

字节序 高字节&#xff08;低字节&#xff09;高地址&#xff08;低地址&#xff09;大端模式&#xff08;小端模式&#xff09; 高字节&#xff08;低字节&#xff09; 一个16位(双字节)的数据&#xff0c;比如0xAABB&#xff0c;那么高位字节就是0xAA&#xff0c;低位是0xBB …...

数据结构(五)——树森林

5.4 树和森林 5.4.1 树的存储结构 树的存储1&#xff1a;双亲表示法 用数组顺序存储各结点&#xff0c;每个结点中保存数据元素、指向双亲结点(父结点)的“指针” #define MAX_TREE_SIZE 100// 树的结点 typedef struct{ElemType data;int parent; }PTNode;// 树的类型 type…...

vscode配置c/c++调试环境

本文记录win平台使用vscode远程连接ubuntu server服务器下&#xff0c;如何配置c/c调试环境。 过程 1. 服务器配置编译环境 这里的前置条件是vscode已经能够连接到服务器&#xff0c;第一步安装编译构建套件&#xff08;gcc、g、make、链接器等&#xff09;和调试器&#xf…...

食品输送带的材质

食品输送带的材质&#xff1a;确保安全与卫生的关键选择 在食品生产和加工过程中&#xff0c;食品输送带扮演着至关重要的角色。它负责将原材料、半成品和成品在各个环节之间进行有效传输&#xff0c;确保生产流程的顺畅进行。然而&#xff0c;在食品行业中&#xff0c;输送带…...

普通用户权限运行Docker

普通用户权限运行Docker 安装Docker Docker的安装比较简单&#xff0c;在Docker官网已经给出了具体的方案&#xff0c;可以直接使用apt安装 # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/…...

7.Java并发编程—掌握线程池的标准创建方式和优雅关闭技巧,提升任务调度效率

文章目录 线程池的标准创建方式线程池参数1.核心线程(corePoolSize)2.最大线程数(maximumPoolSize)3.阻塞队列(BlockingQueue) 向线程提交任务的两种方式1.execute()1.1.案例-execute()向线程池提交任务 2.submit()2.1.submit(Callable<T> task)2.2.案例-submit()向线程池…...

从边缘设备丰富你的 Elasticsearch 文档

作者&#xff1a;David Pilato 我们在之前的文章中已经了解了如何丰富 Elasticsearch 本身和 Logstash 中的数据。 但如果我们可以从边缘设备中做到这一点呢&#xff1f; 这将减少 Elasticsearch 要做的工作。 让我们看看如何从具有代理处理器的 Elastic 代理中执行此操作。 E…...

day29|leetcode|C++|491. 非递减子序列|46. 全排列|47. 全排列 II

Leetcode 491. 非递减子序列 链接&#xff1a;491. 非递减子序列 thought: 设 stack 中最后一个值的位置为 last。如果 stack 为空&#xff0c;则 last -1。 设当前正在处理的位置为 pos。如果在 nums 的子区间 [last1, pos) 中&#xff0c;存在和 nums[pos] 相同的值&…...

[Java、Android面试]_12_java访问修饰符、抽象类和接口

文章目录 1. java访问修饰符2. 抽象类和接口2.1 抽象类2.2 接口2.3 抽象类和接口的区别 本人今年参加了很多面试&#xff0c;也有幸拿到了一些大厂的offer&#xff0c;整理了众多面试资料&#xff0c;后续还会分享众多面试资料。 整理成了面试系列&#xff0c;由于时间有限&…...

Linux:Prometheus的源码包安装及操作(2)

环境介绍 三台centos 7系统&#xff0c;运行内存都2G 1.prometheus监控服务器&#xff1a;192.168.6.1 主机名&#xff1a;pm 2.grafana展示服务器:192.168.6.2 主机名&#xff1a;gr 3.被监控服务器&#xff1a;192.168.6.3 …...

MongoDB聚合运算符:$integral

文章目录 语法使用举例 $integral聚合运算符只能用在$setWindowField阶段&#xff0c;返回曲线下面积的近似值&#xff0c;该曲线是使用梯形规则计算的&#xff0c;其中每组相邻文档使用以下公式形成一个梯形&#xff1a; $setWindowFields阶段中用于积分间隔的sortBy字段值$i…...

手撕算法-买卖股票的最佳时机 II(买卖多次)

描述 分析 使用动态规划。dp[i][0] 代表 第i天没有股票的最大利润dp[i][1] 代表 第i天持有股票的最大利润 状态转移方程为&#xff1a;dp[i][0] max(dp[i-1][0], dp[i-1][1] prices[i]); // 前一天没有股票&#xff0c;和前一天有股票今天卖掉的最大值dp[i][1] max(dp[i-1…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...