JavaScript中的this指向绑定规则(超全)

news2024/11/25 23:30:21

JavaScript中的this指向绑定规则(超全)

1.1 为什么需要this?

为什么需要this?

在常见的编程语言中,几乎都有this这个关键字(Objective-C中使用的是self),但是在JavaScript中的this和常见的面向对象语言中的this不太一样

  • 常见面向对象的编程语言中,比如Java,C++,Swift、Dart等等一系列语言中,this通常只会出现在方法
  • 也就是你需要有一个类,类中的方法(特别是实例方法)中,this代表的是当前调用对象

但是JavaScript中的this更加灵活无论是它出现的位置还是它代表的含义

我们来看一下编写一个obj的对象,有this和没有this的区别

image-20241122185112728

从上面我们可以看出:this的作用就是提高的代码的复用性,不用随着对象名字的改变而改变,直接this指向当前对象即可

1.2 目前掌握两个this的判断方法

1.2.1 以默认的方式调用一个函数,this指向window

  1. 代码示例:
  function foo(name,age) {
        console.log(arguments)
        console.log(this)
      }
  
  foo("123","hahah","xiix")//默认调用,没有对象引用
  
  function sayHello(name) {
    console.log(this)
  }
  1. 结果分析

image-20241122185717441

  1. 思考题:下述代码fn调用的this指向什么?
    var obj = {
      name:"why",
      running:function() {
        console.log(this)
        console.log(this===obj)
      },
      eating:function() {
        console.log("eaintg~",this.name)
      },
      eaing:function() {
        console.log("studying~",this.name)
      }
    }
    //题目一
    obj.running()
    var fn = obj.running
    fn()

答案:默认调用,指向window对象

image-20241122190132320

    function bar() {
          console.log(this)
    }
      var obj = {
        name:"why",
        "bar":bar
      }
      obj.bar()//谁调用它,对象就会指向哪个

答案:指向obj对象,被obj对象调用

image-20241122190309008

1.2.2 通过对象调用,this指向调用的对象

  1. 代码案例
 var obj = {
      name:"why",
      running:function() {
        console.log(this)//指向的就是obj
        console.log(this===obj)
      },
      eating:function() {
        console.log(this)
      },
      eaing:function() {
        console.log(this)
      }
    }

    obj.running()

结果分析:被obj对象调用,所以指向obj这个对象

image-20241122190447415

  1. 思考题:下述代码调用的this指向什么?
    function bar() {
          console.log(this)
    }
      var obj = {
        name:"why",
        "bar":bar
      }
      obj.bar()//谁调用它,对象就会指向哪个

答案:指向obj对象,被obj对象调用

image-20241122190309008

1.3 this到底指向什么呢?

我们先来看一个令人困惑的问题:

  • 定义一个函数,我们采用三种不同的方式对它就行调用,它产生了三中不同的效果

这样的案例可以给我们什么样的启示呢?

  1. 函数在调用时,JavaScript会默认给this绑定一个值
  2. this的绑定和定义的位置(编写的位置)没有关系
  3. this绑定和调用的方式以及调用的位置有关系
  4. this是在运行时被绑定的

image-20241122193140652

1.4 this的绑定规则

  • 绑定规则一:默认绑定;
  • 绑定规则二:隐式绑定;
  • 绑定规则三:显示绑定
  • 绑定规则四:new绑定

1.4.1 规则一:默认绑定

什么情况下使用默认绑定呢?独立函数调用

独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用

  //1.定义函数
  function foo() {
    console.log("foo:",this)
  }
  
  foo()//默认调用,this指向window
  
  //2.函数定义在对象中,但是独立调用
  var obj = {
    name:"why",
    bar:function() {
      console.log("bar:",this)
    }
  }
  var baz = obj.bar
  baz()//独立函数调用

//3.高阶函数
function test(fn) {
  fn()
}
test(obj.bar)

严格模式下,独立函数调用的函数中的this指向的是undefined

   "use strict"
  //1.定义函数
  function foo() {
    console.log("foo:",this)
  }
  
  foo()//默认调用,this指向window
  
  //2.函数定义在对象中,但是独立调用
  var obj = {
    name:"why",
    bar:function() {
      console.log("bar:",this)
    }
  }
  var baz = obj.bar
  baz()//独立函数调用

1.4.2 隐式绑定

另外一种比较常见的调用方式就是通过某个对象进行调用的

  • 也就是它的调用位置中,是通过某个对象发起的函数调用
  //你不知道的JavaScript(上中下)
    function foo() {
      console.log("foo函数:",this)
    }
    
    var message = "Hello World"
    var obj = {
      bar:foo
    }
    obj.bar()
    

image-20241122195424498

偷偷的把这个this绑定到这个对象上了

1.4.3 new绑定

JavaScript中函数可以当做一个类的构造函数来使用,也就是new关键字

使用new关键字来调用函数时,会执行如下的操作

  1. 创建一个全新的对象
  2. 这个对象会被执行prototype连接
  3. 这个新对象会绑定到函数调用的这个this上(this绑定在这个步骤完成
  4. 如果函数没有返回其他对象,表达式辉返回这个新对象
   /*
    1. 创建新的空对象
    2.将this指向这个空对象
    3.执行函数体中的代码
    4.没有显示返回这个非空对象时,默认返回这个对象
    */
    function foo() {
      console.log("foo函数:",this)
      this.name = "why"
    }
  
    new foo()
  

image-20241122200405676

1.4.4 this的绑定规则四-显示绑定

隐式绑定有一个前提条件

  • 必须在调用的对象内部有一个对函数的引用(比如一个属性)
  • 如果没有这样的引用,在进行调用时,会报找不到该函数的错误
  • 正是通过这个引用,间接将this绑定到了这个对象上

如果我们不希望在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?

  • JavaScript所有的函数都可以使用call和apply方法

    • 第一个参数是相同的,要求传入一个对象
      • 这个对象的作用是什么呢?就是给this准备的
      • 在调用这个函数时,会将this绑定到这个传入的对象中
    • 后面的参数,apply为数组,call为参数列表

    image-20241122210225518

  • 因为上面的过程,我们明确的绑定了this指定的对象,所以称之为显示绑定

 var obj = {
    name:"why"
  }
  
  function foo() {
    console.log("foo函数:",this)
  }
  //执行函数,并且函数中的this指向obj这个对象
  obj.foo = foo//先把这个函数放到obj里面
  obj.foo()
  //这个绑定方法比较麻烦,需要在对象里面添加一个属性,然后在把属性赋值给当前对象,在调用,太麻烦了

  //直接执行函数,并且强制this就是obj对象
  foo.call(obj)
  foo.call(123)
  foo.call("abc")
  foo.call(undefined)//指向window

image-20241122201603877

 
  //call/apply
  function foo(name,age,height) {
    console.log("foo函数被调用:",this)
  }
  //()调用
  foo("why",18,1.88)//window
  //apply
  //第一个参数:绑定this
  //第二个参数:传入额外的实参,以数组的形式
  foo.apply("apply",["kobe",30,1.98])//String
  //call
  //第一个参数:绑定this
  //参数列表:后续的参数是以我们多参数来传递的,会作为我们的实参
  foo.call("call","james",25,2,2.05)

  

image-20241122210020892

如果我们希望this总是绑定到一个对象上,可以怎么做呢?

  • 使用bind方法,bind()方法创建一个新的绑定函数(bound function,BF);

1.4.5 call、apply、bind

  • 通过call或者apply绑定this对象

显示绑定后,this就会明确的指向绑定的对象

function foo() {
console.log(this);
}

foo.call(window);//window
foo.call({name:"why"});
foo.call(123); //Number对象,存放时123

如果我们希望一个函数总是显示的绑定到一个对象上,可以怎么做呢

  1. 使用bind方法,bind()方法创建一个新的绑定函数(boud function,BF)
  2. 绑定函数是一个 exotic function object(怪异函数对象,ECMAScript 2015中的术语)
  3. 在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用
  function foo() {
    console.log("foo:",this)
  }
  var obj = {name:"why"}

  //需求:调用foo时,总是绑定到obj对象身上(不希望obj对象身上有我们的函数)
  var bar = foo.bind(obj)//其实在执行这行foo.bind(obj)时,生成了一个新的函数,原有的foo函数还是指向window,bar函数指向这个被绑定的对象
  bar()//this指向的是obj
  bar()
 function foo() {
    console.log("foo:",this)
  }
  var obj = {name:"why"}

  //需求:调用foo时,总是绑定到obj对象身上(不希望obj对象身上有我们的函数)
  var bar = foo.bind(obj)//其实在执行这行foo.bind(obj)时,生成了一个新的函数,原有的foo函数还是指向window,bar函数指向这个被绑定的对象
  bar()//this指向的是obj
  bar()

  //2.bind函数的其他参数(了解)
  var bar = foo.bind(obj,"kobe",18,1.88)
  bar("james")

1.5 内置函数的调用

有些时候,我们辉调用一些JavaScript的内置函数,或者一些第三方库中的内置函数

  • 这些内置函数要求我们传入内外一个函数
  • 我们自己并不会显示的调用这些函数,而且JavaScript内部或者第三方库会帮助我们执行
  • 这些函数中的this是如何绑定的呢?

setTimeout\数组中的forEach、div的点击

    //1.定时器
    setTimeout(function() {
        console.log("定时器函数:",this)  //默认指向Window
    },10000)

自动调用,this指向window

image-20241124181432615

内置函数(第三方库):根据一些经验,Vue传入生命周期函数
 //2.按钮的点击监听
    var btnE1 = document.querySelector("button")
    btnE1.onclick = function() {
      console.log("btn的点击:",this)
    }
    btnE1.addEventListener("click:",function(){
      console.log("btn的点击:",this)
    })

image-20241124182105226

  //forEach
    var names = ["abc","cba","nba"]
    names.forEach(function(item){
      console.log("foeEach:",this)
    })

image-20241124182417693

image-20241124182422186

  //forEach
    var names = ["abc","cba","nba"]
    names.forEach(function(item){
      console.log("foeEach:",this)
    },"aaaaaa")  //显示绑定

image-20241124182557559

1.6 this绑定的优先级比较

学了四条规则,接下来开发中我们只需要去查找函数的调用应用了哪条规则即可,但是如果一个函数调用的位置应用了多条规则,优先级谁更高?

  1. 默认规则的优先级是最低的

毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this

  1. 显示绑定优先级高于隐式绑定绑定

代码测试

  1. new绑定的优先级高于隐式绑定

代码测试

  1. new绑定的优先级高于bind
  • new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
  • new绑定可以和bind一起使用,new绑定优先级更高
  • 代码测试

优先级顺序

new
bind
/apply/call
隐式绑定
默认绑定
bind的优先级高于apply

1.7 this规则之外-忽略显示绑定

上述的规则已经足以应付平时的开发,但是总有一些语法,超出了我们的规则之外。(神话故事和动漫中总是有类似这样的人物)

  1. 情况一:如果在显示绑定中,我们传入一个null或者undefined,那么这个显示绑定会被忽略,使用默认规则:
  function foo() {
    console.log("foo:",this)
  }
  
  foo.apply("abc")
  foo.apply(null)
  foo.apply(undefined)

image-20241124200434888

在严格模式下,绑定的使我们基本数据类型,就不是一个对象

  "use strict"
  function foo() {
    console.log("foo:",this)
  }
  
  foo.apply("abc")
  foo.apply(null)
  foo.apply(undefined)

image-20241124200610556

1.7.1 间接函数的引用

创建一个函数的间接引用,这种情况使用默认绑定规则

  • 赋值(obj2.foo = obj1.foo()的结果是foo函数
  • foo函数被直接调用,那么是默认绑定

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

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

相关文章

Vision Transformer(VIT模型)

【11.1 Vision Transformer(vit)网络详解-哔哩哔哩】 https://b23.tv/BgsYImJ 工作流程: ①将输入的图像进行patch的划分 ②Linear Projection of Flatted patches,将patch拉平并进行线性映射生成token ③生成CLS token(用向量有效地表示整…

2024年11月最新 Alfred 5 Powerpack (MACOS)下载

在现代数字化办公中,我们常常被繁杂的任务所包围,而时间的高效利用成为一项核心需求。Alfred 5 Powerpack 是一款专为 macOS 用户打造的高效工作流工具,以其强大的定制化功能和流畅的用户体验,成为众多效率爱好者的首选。 点击链…

C#里怎么样检测文件的属性?

C#里怎么样检测文件的属性? 对于文件来说,在C#里有一种快速的方法来检查文件的属性。 比如文件是否已经压缩, 文件是否加密, 文件是否是目录等等。 属性有下面这么多: 例子演示如下: /** C# Program to View the Information of the File*/ using System; using Syste…

网络安全,文明上网(4)掌握网络安全技术

前言 在数字化时代,个人信息和企业数据的安全变得尤为重要。为了有效保护这些宝贵资产,掌握一系列网络安全技术是关键。 核心技术及实施方式 1. 网络监控与过滤系统: 这些系统构成了网络防御体系的基石,它们负责监控网络通信&…

Vue 项目中如何使用FullCalendar 时间段选择插件(类似会议室预定、课程表)

本文中是基于VUEelementui项目中实现的前后端分离的前端功能部分: 插件的官方文档:FullCalendar 1.安装对应依赖(统一安装版本为6.15) npm install --save fullcalendar/core6.15 npm install --save fullcalendar/daygrid6.…

Oracle SQL优化③——表的连接方式

前言 表(结果集)与表(结果集)之间的连接方式非常重要,如果CBO选择了错误的连接方式,本来几秒就能出结果的SQL可能执行一天都执行不完。如果想要快速定位超大型SQL性能问题,就必须深入理解表连接…

小程序25- iconfont 字体图标的使用

项目中使用到图标,一般由公司设计进行设计,设计好后上传到阿里巴巴矢量图标库 日常开发过程中,也可以通过 iconfont 图标库下载使用自带的图标 补充:使用 iconfont 图标库报错:Failed to load font 操作步骤&#xff…

Java基于SpringBoot+Vue的藏区特产销售平台

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…

selinux及防火墙

selinux说明 SELinux 是 Security-Enhanced Linux 的缩写,意思是安全强化的 linux 。 SELinux 主要由美国国家安全局( NSA )开发,当初开发的目的是为了避免资源的误用。 httpd进程标签(/usr/share/nginx/html &#…

详细探索xinput1_3.dll:功能、问题与xinput1_3.dll丢失的解决方案

本文旨在深入探讨xinput1_3.dll这一动态链接库文件。首先介绍其在计算机系统中的功能和作用,特别是在游戏和输入设备交互方面的重要性。然后分析在使用过程中可能出现的诸如文件丢失、版本不兼容等问题,并提出相应的解决方案,包括重新安装相关…

生成对抗网络模拟缺失数据,辅助PAMAP2数据集仿真实验

PAMAP2数据集是一个包含丰富身体活动信息的数据集,它为我们提供了一个理想的平台来开发和测试HAR模型。本文将从数据集的基本介绍开始,逐步引导大家通过数据分割、预处理、模型训练,到最终的性能评估,在接下来的章节中&#xff0c…

IEC61850读服务器目录命令——GetServerDirectory介绍

IEC61850标准中的GetServerDirectory命令是变电站自动化系统中非常重要的一个功能,它主要用于读取服务器的目录信息,特别是服务器的逻辑设备节点(LDevice)信息。以下是对GetServerDirectory命令的详细介绍。 目录 一、命令功能 …

基于CNN+RNNs(LSTM, GRU)的红点位置检测(pytorch)

1 项目背景 需要在图片精确识别三跟红线所在的位置,并输出这三个像素的位置。 其中,每跟红线占据不止一个像素,并且像素颜色也并不是饱和度和亮度极高的红黑配色,每个红线放大后可能是这样的。 而我们的目标是精确输出每个红点的…

前端:JavaScript (学习笔记)【2】

目录 一,数组的使用 1,数组的创建 [ ] 2,数组的元素和长度 3,数组的遍历方式 4,数组的常用方法 二,JavaScript中的对象 1,常用对象 (1)String和java中的Stri…

全面解析多种mfc140u.dll丢失的解决方法,五种方法详细解决

当你满心期待地打开某个常用软件,却突然弹出一个错误框,提示“mfc140u.dll丢失”,那一刻,你的好心情可能瞬间消失。这种情况在很多电脑用户的使用过程中都可能出现。无论是游戏玩家还是办公族,面对这个问题都可能不知所…

STM32总体架构简单介绍

目录 一、引言 二、STM32的总体架构 1、三个被动单元 (1)内部SRAM (2)内部闪存存储器 (3)AHB到APB的桥(AHB to APBx) 2、四个主动(驱动)单元 &#x…

【PHP】 环境以及插件的配置,自学笔记(一)

文章目录 环境的准备安装 XAMPPWindowMacOS 配置开发环境Vscode 关于 PHP 的插件推荐Vscode 配置 php 环境Apache 启动Hello php配置热更新 参考 环境的准备 下载 XAMPP , 可以从 官网下载 https://www.apachefriends.org/download.html 安装 XAMPP XAMPP 是一个跨平台的集成开…

跟着问题学5——深度学习中的数据集详解(1)

深度学习数据集的创建与读取 数据 (计算机术语) 数据(data)是事实或观察的结果,是对客观事物的逻辑归纳,是用于表示客观事物的未经加工的的原始素材。 数据可以是连续的值,比如声音、图像,称为模拟数据。…

实验-Linux文件系统和磁盘管理

操作1 远程连接Linux系统 下述连接方式2选一即可。 使用xshell工具连接Linux系统。打开xshell,新建连接,将主机ip修改为实际Linux系统的ip(ifconfig命令查看),可以新建多个xshell会话,使用不同的用户名登录,方便后续…

GPTZero:高效识别AI生成文本,保障学术诚信与内容原创性

产品描述 GPTZero 是一款先进的AI文本检测工具,专为识别由大型语言模型(如ChatGPT、GPT-4、Bard等)生成的文本而设计。它通过分析文本的复杂性和一致性,判断文本是否可能由人类编写。GPTZero 已经得到了超过100家媒体机构的报道&…