一次性搞懂this

简介: 一次性搞懂this

this代表当前this直属函数的所处的对象


此时的this表示people
let people = {
  name: 'dong',
  sayName: function() {
    console.log(this.name)   
  }
}
// dong
const app = {
  init() {
    this.$btn = document.querySelector('button')
    this.bind()
  },
  bind() {
    this.$btn.onclick = function() {
      console.log(this)
      this.getData()    //这样写对吗,如果不对该如何修改?
    }
  },
  getData() {
    console.log('get data...')
  }
}
app.init()

init方法里的this指的是app


bind方法里,根据定义,第8,9行的this指的不是app,而是 $btn元素对象,$btn元素对象没有getData方法所以会报错

修改方法 方法一:先把this对象复制到self

bind() {
    let self = this
    this.$btn.onclick = function() {
      console.log(this)  // 这个this 还是 btn 对象
      self.getData()    //self 代表外面的this,也就是app
    }
  },

方法二:使用箭头函数 () 箭头函数没有this,所以此时this的直属函数是bind

bind() {
    this.$btn.onclick = () => {
      console.log(this)  // 在确定this直属的函数时,不考虑箭头函数
      this.getData()     // 所以当前this直属的函数是bind
    }
  },

案例

let name = '中国'
let people = {
  name: '郑州',
  sayName() {
    console.log(this.name)
  }
}
let sayAgain = people.sayName
function sayName(){
  console.log(this.name)
}
people.sayName()
sayAgain()
sayName()

执行people.sayName()会输出郑州,因为他的this的直属函数是sayName,所属对象是people


执行sayAgain(),就会调用执行 people.sayName(),调用了sayAgain才执行函数,所以this的直属函数是sayAgain,sayAgain所属的对象在非严格模式下我们普遍认为是window ,所以this指向window, 因为let不会在全局声明时(在最顶部的范围)创建window 对象的属性。所以name变量并不是window的属性,所以输出的是空的,如果使用var定义name变量那么就会打印出“中国”

执行sayName()时,此时this的直属函数是sayName(),它的所属对象就是window,所以this就是window


数组的this


let arr = []
for(let i=0; i<3; i++){
   arr[i] = function() { console.log(this) }
}
arr[0]()
let fn = arr[0]
fn()

数组也是一种对象,相当于{ 0:值,1:值}


for循环执行完后数组里面相当于存了三个函数 [ƒ, ƒ, ƒ] 执行 arr[0]() 时,this的直属函数就是arr[0],所属对象就是数组,所以会打印出数组

执行 fn()  , 此时this的所属对象就是fn(),而fn的所属对象是window,所以打印处window对象


定时器的this

let people = {
  name: 'dong',
  sayName() {
    setTimeout(function(){
      console.log(this.name)
    }, 0)
  }
}
people.sayName()

此时this的直属函数是function,而它的所属对象是window,所以this指向window,window并没有name属性,所以打印出undefined


箭头函数的this


箭头函数的this会被我们忽略掉,找它上一级的函数

箭头函数没有this或者理解成箭头函数的this是箭头函数直属的函数

let people = {
  name: 'dong',
  sayName() {
    setTimeout(() => {
      console.log(this.name)
    }, 0)
  }
}
people.sayName()

因为this直属的函数是箭头函数,箭头函数的this是 箭头函数直属的函数也就是sayName(),而sayName()属于people,所以this代表people,会打印出 dong


构造函数的this

function Wife(name) {
  this.name = name
}
Wife.prototype.showSkill = function() {
  console.log('我是' + this.name + ', 我的技能是唱歌、跳舞、打游戏')
}

一个直男幻想着:如果我以后有个女神老婆,她的技能最好是唱歌、跳舞、打游戏。上面代码里的this(出现在构造函数内和原型的方法内)就代表幻想的那个未来的那个“她”。


那么我现在开始创建,创建的是谁,这个this就是谁 我创建这个老婆是刘亦菲,那么这个this就是刘亦菲

let wife = new Wife('刘亦菲')
wife.showSkill()

我创建这个老婆是佟丽娅,那么这个this就是佟丽娅

let wife = new Wife('佟丽娅')
wife.showSkill()

只有new之后,真正的 wife 才创建,之前幻想时对 她(this) 的操作才真正开始实施。对this操作就是对wife的操作。


更改this指向的对象


this默认是当前this直属的函数所处的对象,可以使用 call/apply/bind,方法传入第一个参数就是指向的对象

let obj = {
  fn(a, b) {
    console.log(this)
  }
}
obj.fn(1, 2) //打印出obj对象
//等价于
obj.fn.call(obj, 1, 2)         // 所以 this 是 obj
obj.fn.apply(obj, [1, 2])
obj.fn.bind(obj)(1, 2)

call/apply/bind里面第一个参数传什么,函数里面的this就是什么。

let obj = {name: 'dong'}
function sayName(){
  console.log(this.name)
}
let fn = sayName.bind(obj)
fn() // 输出: 'dong'

不使用bind时,this所属的对象是window,使用bind并将obj对象传入,则this就是obj


复杂案例

let app = {
  container: document.querySelector('body'),
  init: function(){
    //点击的时候会执行 sayHello,sayHello 里面的 this 代表 body 对象
    this.container.addEventListener('click', this.sayHello)                  
    //点击的时候会执行 sayHello,sayHello 里面的 this 代表 app 对象
    this.container.addEventListener('click', this.sayHello.bind(this)) 
  }, 
  sayHello: function(){
    console.log(this)
  }
}
app.init()

init函数里的两个this指的是app


点击调用sayHello函数,此时sayHello里this直属的函数是click,click的所属对象就是DOM对象container也就是body

使用bind传参,传入的this就是当前对象app,所以此时再调用sayHello函数它里面的this就是app


总结


当看到this时,想知道它是什么,就需要看


  • this的直属函数是谁,忽略箭头函数
  • 直属函数是哪个对象上的方法
  • 有没有使用过call、apply、bind来修改this



目录
相关文章
|
11月前
|
存储 算法 固态存储
能解决你80%关于存储的疑惑
在计算机系统中,常用的存储介质包括寄存器、内存、SSD、磁盘等,寄存器的速写速度与CPU相同,一个时钟周期是0.3纳秒,而内存访问需要120纳秒,寄存器的读写速度比内存要快几百倍,固态硬盘访问需要50~150μs,磁盘访问需要一到十几毫秒,磁盘的读写速度比内存慢了几万倍,网络访问则更慢,需要几十到上百毫秒。
239 0
|
12月前
|
缓存 自然语言处理 小程序
这个迭代写了个小程序,顺便整理了一份笔记 📒 (4000字)
这个迭代写了个小程序,顺便整理了一份笔记 📒 (4000字)
143 0
|
存储 开发者
彻底搞懂函数,读这篇文章就够了
如果你之前使用过任何一门编程语言,那么对于你来讲想必已经知道什么是函数,以及如何使用函数了,那你大可不必往下读了。这篇文章是写给新手看的,也就是说我假设你对于函数没有任何的概念。 我们就先从什么是函数来说起吧!
79 0
|
缓存 算法 NoSQL
短链是什么原理?怎么实现呢?
内容营销中给用户推送营销消息最常见的方式就是发短信,比如三大运营商移动、联通、电信平时会发送一些诸如套餐办理、消费查询、话费充值这些短信,还有像银行、云服务厂商等等推送的各种包含查询服务的短信等等。
短链是什么原理?怎么实现呢?
感觉知识点都会了,但是还是有地方不懂?花点时间来看看
后者:将q的值给了tail的指针域,主要起了一个连接作用,即,把q当作tail的下一个节点,可以理解为插入操作,也可以理解为增加节点的操作。
54 0
|
缓存 负载均衡 Oracle
面试官:说下你在项目中是如何处理高并发的???
面试官:说下你在项目中是如何处理高并发的???
378 0
面试官:说下你在项目中是如何处理高并发的???
|
数据库
【高效编码】查询日志的命令老是记不住?没关系,这篇文章帮你记
您好,我是码农飞哥,一直想飞暂时在跑个那个老哥。
222 0
【高效编码】查询日志的命令老是记不住?没关系,这篇文章帮你记
|
存储 缓存 网络协议
五分钟搞懂缓存
五分钟搞懂缓存
239 0
五分钟搞懂缓存
|
缓存 并行计算 Java
Python编程:节省内存的办法(持续更新ing...)
本文将介绍Python编程时节省内存的方法。 其实我一般使用CPU运行代码时不太有内存问题,主要是在用GPU时会遇到OOM问题,需要节省内存。因此本文接下来的内容会涉及CPU、GPU通用的方法,和使用GPU(主要是使用PyTorch框架)时的解决方案。
|
前端开发 API
我明白了,前端并发函数
谷歌浏览器的并发数是6个,有六个请求正在处理,那么其它的任务就会排队,等六个请求任务中的某一个完成之后,就会立马插入进去执行。这样一来,就清楚了。
157 0
我明白了,前端并发函数

热门文章

最新文章