news 2026/3/28 11:07:05

this的指向问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
this的指向问题

这是一篇关于 this 指向问题的分享会文档

必要知识准备

介绍 this 之前,先简单介绍四个概念(普通函数、对象方法、构造函数、箭头函数),作为我们的知识储备,为后面我们介绍 this 做铺垫

  1. 普通函数:函数是一段可以被重复调用的代码块,普通函数的声明方法基本结构很简单,我们都知道,就是下面的这种

    function函数名(){函数体}// 调用函数函数名()
  2. 对象方法

    什么是对象?

    • 概念:对象也是 JS 数据类型的一种,和之前学习的数值类型、字符串类型、布尔类型是一样的。对象数据类型可以被理解成是一组无序的 键值对的集合,是属性的容器。

      • : 又称属性名,通常是一个字符串
      • : 可以是任何数据类型,包括:数字、字符串、布尔值、数组、函数,甚至是另一个对象(嵌套)
      • 属性:属性是成对出现的,包括属性名和属性值,它们之间使用英文:分隔,多个属性之间使用英文,分隔

    怎样创建一个对象呢?

    • 最常用的一种就是使用花括号{ }直接创建
    let对象名={属性名1:属性值1,属性名2:属性值2,属性名3:属性值3,属性名4:函数,...}

    什么是方法?

    • 属性值可以是任何数据类型,这里面有一个数据类型比较特殊,就是函数,如果一个属性的值是一个函数,那么我们称这个属性是这个对象的一个方法

    由于普通函数和方法之间有点像,所以我们举个例子带大家区分一下

    // 创建一个函数functionfn(){console.log('111')}// 直接放在script标签里面,他就是一个普通函数functionfn(){console.log('111')}// 将这个函数放进一个对象里面letobj={fn:function(){// 是这个obj对象的方法console.log('111')}}

    怎样访问对象里面的属性呢?

    • 创建了一个对象之后,我们怎么访问该怎样访问这个对象里面的属性呢?

      • 声明对象并添加了若干属性后,可以使用.[]获得对象中属性对应的值,

      • 基本上写法就是对象名.属性名或者对象名[‘属性名’]

      • 如果我们需要为这个属性重新赋值或者添加一个属性,也很简单对象名.属性名 = 新的属性值

    • 我们平常写代码经常写的

    // 获取DOM元素letoDiv=document.querySelector('div')// 给这个元素添加一个字体颜色为红色的样式oDiv.style.color='red'// style 就是 oDiv 的一个属性,这个属性的值其实也是一个对象,而 color 是 style 的一个属性

    上面的是不是很熟悉,其实当我们把div这个标签获取过来并赋给变量oDiv时,我们也相当于创建一个一个对象名为oDiv的对象,只是这个对象里面已经很多自带的属性和方法了,我们用的时候直接使用.调用我们需要的属性,并给它重新赋值就行了

    怎样访问对象里面的方法呢?

    • 基本上和访问属性的写法是一致的,只有一点不一样,我们都知道普通函数的调用方法,函数名(),最后必须要加上一个小括号,这个函数的调用的方法是不论在哪里都不变的,前面讲到方法的属性值是函数,既然是函数,那我们也应该在属性名后面加上(),即对象名.属性名()
    letperson={name:'小红',age:18,singing:function(){console.log('两只老虎,两只老虎,跑的快,跑的快...')},run:function(){console.log('我跑的非常快...')}}// 调用person对象中 singing 方法person.singing()// 调用person对象中的 run 方法person.run()
  3. 构造函数

    • 在前面我们学习了对象之后,应该能更好的理解构造函数,因为构造函数其实也是在创建对象

    • 什么是构造函数?

      • 本质上就是一个普通的函数,但是会有两个地方不太一样

        • 命名方面上,构造函数的名称首字母要大写,这其实是一种普遍的命名的约定用来区别普通函数

        • new操作符一起使用:构造函数通过new操作符来调用,用于创建并初始化一个新对象。你们也可以这样理解,当一个函数使用new操作符调用时,它就成为了“构造函数”,即一个用于构造新对象的函数

    • 构造函数的作用:创建对象

      • 在没有构造函数之前,我们使用对象字面量创建单个对象

        // 对象字面量创建单个对象letperson1={name:"Alice",age:25,greet :function(){console.log(Hello,my name is11);}}person1.greet();// 输出: Hello, my name is 11
      • 但如果要创建多个结构相似(都有 name, age, greet 属性)的对象,重复写对象字面量会很麻烦,这时构造函数就派上用场了

      • 构造函数就像一个“工厂模具”,可以批量生产具有相同属性和方法结构的对象

    • 怎样使用构造函数呢?

      • 第一步:定义构造函数,在其内部使用this来定义未来的实例对象里面的属性和方法

      创建一个函数(首字母要大写)

      // 1. 定义构造函数 (首字母大写)// 这里相当于创建了一个模板functionPerson(name,age){// 2. 使用 this 来添加属性this.name=name;this.age=age;}
      • 第二步:使用new调用构造函数,使用new操作符来调用Person函数,它会按照构造函数的模板创建一个新的对象并返回它
    // 使用 new 创建实例// 我们在这里调用这个模板letperson1=newPerson('Alice',25);letperson2=newPerson('Bob',30);console.log(person1.name);// 输出: Aliceconsole.log(person2.age);// 输出: 30

    我们可以看一个例子:


    到这里,我想问大家,现在大家知道上面第二个图片里的变量person1person2的值是什么数据类型吗?

  4. 箭头函数

    • 箭头函数是 ES6 中引入的一种新的函数语法,它提供了一种更简洁的方式来编写函数

    • 基本语法:

      • 我们与传统的函数表达式对比来学
      constadd=function(a,b){returna+b}
      // 完整写法(多行语句或需要显式返回对象时)constadd=(a,b)=>{returna+b}// 简洁写法(单行表达式,隐含 return)constadd=(a,b)=>a+b
      • 大家观察上面的两种写法有什么不同?

        • 仔细观察,赋值等号左边的没有变化,变化在右边,箭头函数删去了 function ,变成了 => ,并且将 => 放到了 小括号 和 大括号 的中间

    好啦,到这里我们的前置只知识准备就完成了,这就开始我们第二部分的介绍吧!

this 到底什么?

  • 在正式开始介绍 this 到底是什么之前,这里有两个问题:

    • this 是不是变量?

      • 不是,this是一个特殊关键字(像function,if,new一样),不是可以声明的变量或对象的属性
    • this 是对象吗?学到现在,大家应该或多或少都应该见过this.sayName()这种写法,那按照我们上面对于对象方法的学习,大家觉得this 是对象吗?

      • 也不是,this 很多变,它本身不是一个对象,只是它的值实际根据是谁在调用它而变的,只是一般情况下 this 的值(它所指向的东西),总是一个对象
    • 有一个类比,万能电视遥控器(this)

      • 万能遥控器(this)本身不是电视,但按下遥控器的按钮(调用 this)的效果,取决于它当前指向并控制的是哪台电视,你可以用同一个遥控器(this),通过改变它的指向(不同的调用方式)来控制不同的电视(不同的对象)
  • 所以 this 到底是什么呢?

    • 一般来说,this 都是放在一个函数中使用的,调用这个函数也就相当于在调用这个 this ,

    • 当我们总结上面的问题和类比例子后,其实可以用一句话总结,就是this的值不是在我们定义函数时确定的,而是在我们调用这个函数时才被确定的

    • 一个贴近生活的比喻:

      • 想象你是一个员工(函数),你有一句话(函数体):“我正在为this公司工作“

      • 如果你在A公司的办公室里说这句话,this就代表 A公司

      • 如果你在B公司的办公室里说这句话,this就代表 B公司你(函数)本身没变,但你所在的环境(调用者)变了,this的含义也就随之改变了

  • 说多不如练多,这里有一个小问题,大家可以先试试能不能解答
constperson={name:'小明',sayHi:function(){console.log('你好,我是'+this.name)}}person.sayHi()

六个常见环境下的 this 指向问题

  1. 全局环境中的 this

    1. 全局环境就是在<script></script>里(不在任何函数或对象内部),此时的 this 始终指向的是全局对象,在浏览器中就是 window
    console.log(this)
  2. 普通函数中的 this

    当一个函数被直接调用时,要考虑两种情况

    this 在非严格模式下指向全局对象(window)

    functionfun(){console.log(this)}fun()// 我们实际上是在全局作用域下直接调用函数// fun() 实际上是window.fun(), 所以this -> window

    在严格模式下指向undefined

    JS 严格模式:JavaScript 在语法和行为上存在一些模糊的特性,可能导致一些不易察觉的错误,为提高代码的质量和可维护性,JS 引入了严格模式,通过启用一些额外的规则,强制执行更严格的语法和行为,帮助提前发现和修复潜在 bug。

    // 演示严格模式下的情况functionfu(){"use strict"// 在函数体中写一句 "use strict" ,就可以启用函数的严格模式console.log(this)}fu()// this 指向 undefined
  3. 对像方法中的this

    1. 当函数作为对象的方法被调用时,this指向该方法所属的对象
    letperson={name:"John",sayName:function(){console.log(this.name)}}person.sayName()
    letname='卡卡';letcat={name:'有鱼',eat1:{name:'年年',eat2:function(){console.log(this.name);}}}cat.eat1.eat2();
  4. 构造函数中的this

    1. 使用 new 关键字(实例化)调用函数时,该函数被当作构造函数,this 会指向新创建的对象实例
    // 构造函数functionPerson(name){this.name=namethis.sayHello=function(){console.log("Hello, I'm "+this.name)}}// 创建实例letjohn=newPerson("John");john.sayHello()// 输出 "Hello, I'm John",这里 this 指向 john 实例letjohn={name:name,sayHello:function(){console.log("Hello, I'm "+this.name)}}
    // 构造函数functionPerson(name){this.name=namethis.sayHello=function(){console.log(`你好,我是${this.name}`)}}// 创建实例constperson1=newPerson('张三')person1.sayHello()
  5. 事件处理中的 this

    1. 在 DOM 事件处理函数中,this 通常指向触发事件的元素
    <button id="myButton">Click me</button><script>varbutton=document.getElementById("myButton")button.onclick=function(){console.log(this)}</script>
    <buttonclass="btu">Click</button><script>letoBtu=document.querySelector('.btu')oBtn.addEventListenter('click',clickBtu)functionclickBtu(){console.log(this)}</script>
  6. 箭头函数中的 this

    1. 箭头函数没有自己的this,它的 this 直接“捕获”或“继承”**外层作用域(它父级)**的 this 的值

    2. 前面介绍过普通函数 的this动态的,取决于如何被调用

    3. 而箭头函数 的this词法的取决于定义时的上下文,且一旦定义就固定不变

    4. 词法的 = 写代码时候的位置决定的(this的出生地)

// 普通函数functionouterFunction(){this.name="Outer"varinnerFunction=function(){console.log(this.name)}innerFunction()}// 箭头函数functionouterFunctionWithArrow(){this.name="外层作用域的this"varinnerFunction=()=>{console.log(this.name)}innerFunction()}// 一种情况newouterFunction()newouterFunctionWithArrow()// 另一种调用情况outerFunction()outerFunctionWithArrow()

改变 this 指向的方法

由于箭头函数的this来自于继承,箭头函数无法使用以下三种方法改变this指向

  1. call()方法

    call()方法附加在函数调用后面使用,可以忽略函数本身的 this 指向,然后将这个函数本身的 this 指向绑定到call()方法的第一个参数上面

    函数.call(thisArg,arg1,arg2,...,argN)
    • 参数解析:

      • thisArg:就是你想将调用这个方法的函数本身的 this 指向绑定到哪个对象上面

      • arg1-argN:从参数arg1开始,依次是向函数传递参数

    • 注意点:

      • 使用call()方法时,相当于你调用了这个函数,会立刻执行这个函数
    functiongreet(){console.log(this.animal,"的睡眠时间一般在",this.sleepDuration,"之间");}constobj={animal:"猫",sleepDuration:"12 到 16 小时",};greet.call(obj);// 猫 的睡眠时间一般在 12 到 16 小时 之间
  2. apply()方法

    call()方法很相似,几乎一样

    函数名.apply(thisArg,[arg1,arg2,...,argN])
    • 参数解析:

      • thisArg:同样为你想将这个函数本身的 this 指向绑定到哪个对象上面

      • [arg1,arg2,...,argN](主要区别点):一个数组,数组里面的每一项依次是向函数传递的参数,这里可以直接写入一个数组,也可以写一个值为数组的变量

  3. bind()方法

    和上面两个方法不一样,bind()方法创建一个新函数并且还有返回值,使用bind()方法后不会立即执行函数,而是返回一个已经改变了 this 指向的函数

    函数名.bind(thisArg,arg1,arg2,..,argN)
    • 参数解析:

      • thisArg:作用与前两个一致

      • arg1, arg2, ..,argN: 在调用函数时,插入到传入绑定函数的参数前的参数

    • 返回值:

      • 一个改变了 this 指向以后的 function 函数
constmodule={x:42,getX:function(){returnthis.x;},};console.log(module.getX())constunboundGetX=module.getX;//undefined 函数未被调用,存储的是一个值console.log(unboundGetX());constboundGetX=unboundGetX.bind(module);console.log(boundGetX());

总结及综合演练

this 指向:

1、一般函数:谁调用函数,this 就指向谁,没有调用者就指向全局对象 Window

2、箭头函数:箭头函数不会创建 this,它的 this 继承自上层作用域中的 this

<buttonclass='btu'>Click</button><script>console.log(this)// 一个普通函数functionfn(){console.log(this)}// 创建一个对象letobj={fn:fn}// 直接调用fn()// 对象方法调用obj.fn()// 事件处理中调用letoBtu=document.querySelector('.btu')oBtu.addEventLister('click',fn)</script>
// 构造函数functionFn(age){this.name=111this.age=age,this.currentAge=function(){console.log(this.age)}}letFn1=newFn(16)Fn1.currentAge()
// call()方法functionProduct(name,price){this.name=name;this.price=price;}functionFood(name,price){Product.call(this,name,price);this.category="food";}console.log(newFood("cheese",5).name);
  • 代码解析:

    • 关键点:Product.call(this, name, price)

    • 这行代码的作用是:

      • 在 Food 的上下文中调用 Product 构造函数

      • 第一个参数 this 是新创建的 Food 实例

      • call()方法将 Product 中的 this 绑定到传入的 Food 实例上

      • 相当于在 Food 实例上设置 name 和 price 属性

    • 执行过程:

      • new Food("cheese", 5)创建 Food 实例

      • Food 构造函数中的 this 指向这个新实例

      • Product.call(this, "cheese", 5)让 Product 构造函数在这个 Food 实例上工作

      • 最终创建的 Food 对象包含:{name: "cheese", price: 5, category: "food"}

constouter={name:"Outer Object",innerFunction:function(){constinner={name:"Inner Object",nestedFunction:function(){console.log(this.name)}}inner.nestedFunction()}}outer.innerFunction();
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/26 21:32:18

AI智能体的五脏六腑

从工具到伙伴&#xff1a;AI智能体重塑人机关系传统的大模型就像是一个知识渊博但行动不便的专家&#xff0c;你问什么它答什么&#xff0c;但仅此而已。而AI智能体不一样&#xff0c;它更像是一个能够独立思考和行动的数字员工。好比这么个场景&#xff1a;周一早晨&#xff0…

作者头像 李华
网站建设 2026/3/27 13:12:39

军工项目CKEDITOR粘贴涉密图片如何安全上传PHP?

PHP程序员的Word导入插件探索记 各位老铁们好啊&#xff01;我是西安一名苦逼的PHP程序员&#xff0c;最近接了个CMS企业官网的活儿&#xff0c;客户突然甩给我个需求&#xff0c;让我整个人都不好了… 客户的"小"需求 客户说&#xff1a;“小王啊&#xff0c;我们…

作者头像 李华
网站建设 2026/3/28 5:18:46

金融系统CKEDITOR上传图片到PHP服务器如何防篡改?

企业网站后台管理系统Word粘贴与导入功能解决方案评估与实施报告 一、背景与需求分析 作为广西某集团企业的项目负责人&#xff0c;我们近期在企业网站后台管理系统的升级过程中&#xff0c;遇到了一个关键需求&#xff1a;在现有的文章发布模块中增加Word粘贴和文档导入功能…

作者头像 李华
网站建设 2026/3/27 2:49:01

告别「上帝视角」,机器人仅凭几张图精准锁定3D目标,新基准SOTA

来自厦门大学、上海创智学院、复旦大学、字节跳动等机构的研究团队提出了一种全新的解决方案——MVGGT。试想一下&#xff0c;如果你在一个未曾去过的杂乱房间&#xff0c;你只能快速地看三次房间里的布置&#xff0c;接下来就要求你去定位墙角的一张木桌子。这就是具身智能体目…

作者头像 李华
网站建设 2026/3/16 0:28:55

T3Ster®热瞬态测试仪采用电压法(Voltage Method)来测量半导体器件的结温(Junction Temperature)和瞬态热响应

测试流程中关键步骤的详细讲解 T3Ster热瞬态测试仪采用电压法(Voltage Method)来测量半导体器件的结温(Junction Temperature)和瞬态热响应。这种方法基于p-n结(半导体器件中的基本结构,如二极管、晶体管中的结)的温度敏感特性。具体来说,它利用p-n结在恒定小电流下的…

作者头像 李华