首页 > 其他 > 详细

一脚踹开this的大门

时间:2021-04-11 21:46:27      阅读:18      评论:0      收藏:0      [点我收藏+]

技术分享图片

一脚踹开 this 的大门

1、this是什么

在js中,有一套神奇的this机制

我们先来看一个例子:

 var person = {
     namer: ‘张三‘,
     sayNmae: function() {
         console.log(namer);
     }
 }
 ?
 var namer = ‘李四‘
 person.sayNmae();  // 李四

我的天呐,怎么跟我们想的不一样?

这是因为sayNmae方法中的变量是属于全局作用域下面的,但是我们想要访问的是对象里面的变量

使用this便可以帮我们访问到对象里面的变量

 var person = {
     namer: ‘张三‘,
     sayNmae: function() {
         console.log(this.namer);
     }
 }
 ?
 var namer = ‘李四‘
 person.sayNmae(); // 张三

这是因为方法中的this指向的是方法所在的对象

this是一个对象

分类

this 分为全局作用域下的this函数中的this

全局作用域下的this指向的是windows对象

技术分享图片

函数中this的指向就比较复杂了,接下里我们就来详细介绍一下函数中this的指向

2、函数中this的指向

技术分享图片

this的指向是我们在调用函数的时候确定的。调用方法的不同导致this的指向不同

调用方式this 的指向
普通函数调用 window
构造函数调用 实例对象 原型对象里面的方法也指向实例对象
对象调用方法 该方法所属对象
事件绑定函数 绑定事件对象
定时器函数 window
立即执行函数 window
箭头函数 箭头函数的`this`就是它外层函数的 this

1、普通函数

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

返回window对象

2、构造函数

 // 构造函数this指向 p 这个实例对象
 function Person() {}
 // 原型对象里面的 方法也指向实例对象
 Person.prototype.say = function() {
     console.log(this);
 }
 ?
 var p = new Person()
 p.say();  // Person {}

3、对象方法

 var person = {
     namer: ‘张三‘,
     sayNmae: function() {
         console.log(this.namer);
     }
 }
 person.sayNmae(); // 张三

当函数作为对象的方法调用时,函数中的this就是该对象

但是,如果将该对象赋值全局变量,那么this就指向window对象

 var namer = ‘李四‘
 var p = person.sayNmae;
 p(); // 李四

4、事件绑定函数

 var btn = document.querySelector(‘button‘)
 btn.onclick = function() {
     console.log(this); // <button>this指向事件对象</button>
 }

事件绑定函数中this指向绑定的事件,所以点击按钮,输出 button事件

5、定时器函数

 setTimeout(function() {
     console.log(this);
 }, 500)

0.5s后打印window对象

6、立即执行函数

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

立即执行函数本质上也是普通函数,其this自然而然地指向 window对象

7、箭头函数

ES6中的箭头函数,由于它没有自己的执行上下文,所以箭头函数的this就是它外层函数的this

注意:

  • 是箭头函数的外层函数中的this,不是对象中的this
  • 若没有外层函数,其this默认指向window对象
 var namer = ‘李四‘
 var person = {
     namer: ‘张三‘,
 }
 // 箭头函数
 var sayNmae = () => console.log(this.namer)
 sayNmae();  // 李四7
 ?
 // 添加箭头函数
 person.sayNmae = sayNmae
 // 访问到的仍然是全局变量
 person.sayNmae(); // 李四

箭头函数中,this引用的是定义箭头函数的上下文,上面箭头函数的两次调用中,this指向的都是window对象,因为箭头函数都是在window中定义的

3、改变函数内部this的指向

js为我们提供了一些方法来改变函数内部this的指向,常用的有3个:

  • call()
  • apply()
  • apply()
技术分享图片

3.1 call()方法

call() 方法有两个作用:改变this的指向调用函数

 var person = {
     name: ‘张三‘
 }
 ?
 function foo(a, b) {
     console.log(this);
     console.log(a + b);
 }
 foo.call(person, 1, 2)  // { name: ‘张三‘ }  3

此时this已经指向person对象了

call 方法的主要作用是 实现继承

3.2 apply() 方法

 func.apply(thisAry, [argsArray])
  • thisAry:在func函数运行时指定的this
  • argsArray:传递的值,必须包含在数组里面
  • apply() 会自动将数组转换为我们需要的数值类型
 var person = {
     name: ‘张三‘
 }
 ?
 function foo(a, b) {
     console.log(this);
     console.log(a + b);
 }
 foo.apply(person, [1, 2]) // { name: ‘张三‘ }  3

apply() 经常跟数组有关系,比如借助内置的数组对象查找数组的最大值和最小值

Math.max() 规定必须传入单个的数值

 var max = Math.max(1, 20, 3, 10, 15)
 console.log(max);  // 20

我们通过apply将数组分解为单个的值,然后传给max,实现查找数组的最大值

 var arr = [11, 20, 3, 10, 15]
 var max = Math.max.apply(Math, arr)
 console.log(max);  // 20
 ?
 var min = Math.min.apply(Math, arr)
 console.log(min);  // 3

apply中的this指向函数的调用者 Math


3.3 bind()方法

bind() 最大的特点是 不会调用函数,用法与call() 无异

 func.bind(thisAry, arg1, arg2, ...);
  • thisAry:在func函数运行时指定 this
  • arg1arg2:传递单个的参数
 var person = {
     name: ‘张三‘
 }
 ?
 function foo(a, b) {
     console.log(this);
     console.log(a + b);
 }
 var f = foo.bind(person, 1, 2) 
 f();  // { name: ‘张三‘ }  3

如果有的函数我们不需要立即调用,但是又想改变函数内部this的指向时,可以使用bind

bind也是这三个方法中我们用得最多的

4、this有一个的缺陷

缺陷:嵌套函数中的this不会继承外层函数中的this

 var namer = ‘李四‘
 var obj = {
     namer: ‘张三‘,
     show: function() {
         console.log(this.namer);  // 张三
 ?
         function foo() {
             console.log(this.namer);  // 李四
         }
         foo()
     }
 }
 obj.say()

可以看到,show方法的this就指向其所在的对象obj,foo函数中的this并没有继承其外部函数showthis,而是全局对象window

有两个解决办法:

  1. 声明一个变量来保存this
  2. 使用箭头函数

方法一:声明一个变量that来保存this

 var namer = ‘李四‘
 var obj = {
     namer: ‘张三‘,
     show: function() {
         console.log(this.namer);  // 张三
         
         // 保存this
         var that = this  
         
         function foo() {
             console.log(that.namer);  // 张三
         }
         foo()
     }
 }
 obj.say()

方法二:箭头函数

箭头函数没有自己的上下文,所以箭头函数定义在哪里,它的上下文就在哪里的,因此其this也是指向哪里

 var namer = ‘李四‘
 var obj = {
     namer: ‘张三‘,
     say: function() {
         console.log(this.namer); // 张三
         var foo = () => {
             console.log(this.namer); // 张三
         }
         foo()
     }
 }
 obj.say()

小应用:当我们点击这个按钮,立即禁用这个按钮,2秒后再开启

说到延时,首先想到的就是利用 setTimeoutsetInterval

 var btn = document.querySelector(‘button‘)
 // 点击按钮
 btn.onclick = function() {
     // 事件绑定函数,this指向的就是被绑定的事件btn
     this.disabled = true;  // 先禁用按钮
     setTimeout(function() {
         // 此处this指向的是window对象
         this.disabled = false;
     }, 2000)
 }

按照我们上面的分析,这种方式绝对是gg的,无法实现效果

事件函数中的this指向的事件对象,这个某得问题,点击之后按钮被禁用了;但是延时函数中的this指向的是window对象,这个问题就来了

所以我们当务之急就是在延时函数中访问到事件对象,有4种方法:

  1. 直接使用btn事件对象
  2. 保存this
  3. 使用箭头函数
  4. 利用bind修改this指向

方法一:使用btn事件对象

 var btn = document.querySelector(‘button‘)
 btn.onclick = function() {
     this.disabled = true;
     setTimeout(function() {
         btn.disabled = false;
     }, 2000)
 }

缺点:事件名称一变,下面就要跟着修改,很不方便,不推荐

方式二:保存this

 btn.onclick = function() {
     this.disabled = true;
     var that = this  // 保存this
     setTimeout(function() {
         that.disabled = false
     }, 2000)
 }

缺点:需要开辟新的内存空间来存储that,增加代码

方式三:箭头函数

 btn.onclick = function() {
     this.disabled = true;
     // 箭头函数
     setTimeout(() => {
         this.disabled = false
     }, 1000)
 }

定义在事件点击函数中的箭头函数,其this指向的就是其上下文所在的地方 —— 事件对象

方式四:利用bind修改this指向

上面刚学完bind方法,还热乎乎的,bind有两大特点:修改this指向、不会调用函数

因为我们只需要修改延时函数中的this指向,不需要调用函数,调用函数让延时函数自己来

 btn.onclick = function() {
     this.disabled = true;
     setTimeout(function() {
         this.disabled = false
     }.bind(this), 2000)
 }

总结

  1. 非严格模式下,函数的this指向window对象;严格模式下,函数中的this的值为undefined
  2. callapply会自己执行函数,bind则不会,它会返回一个新的函数,这个新的函数已经绑定了新的this
  3. 箭头函数就像寄生虫,定义在谁的上下文,其this就指向谁
  4. 嵌套函数中的this不会继承其外层函数的this

转载:https://zhuanlan.zhihu.com/p/362555395?utm_source=qq&utm_medium=social&utm_oi=697417644340436992

一脚踹开this的大门

原文:https://www.cnblogs.com/qiaozhiming123/p/14644235.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!