深入理解this的指向问题

1.js 中多种函数介绍

1.普通函数,使用function关键字定义的函数。

function foo(){
  //code
}

2.箭头函数:用=> 运算符定义的函数

const foo = () = >{
 //code
}

3.生成器函数:用function * 定义的函数

function* foo(){
//code
}

4.在class中定义的函数。

class Person(){
  foo(){
    //code
  }
}

5.用class定义的类,实际上也是函数

class Foo{
  constructor(){
    //code
  }
}

第六 / 七 / 八种,异步函数:普通函数、箭头函数和生成器函数加上 async 关键字。

async function foo(){
    // code
}
const foo = async () => {
    // code
}
async function foo*(){
    // code
}

2.this 关键字

JavaScript中的this是让很多开发者头疼的地方,而this关键字又是一个非常重要的语法点。毫不夸张地

说,不理解它的含义,大部分开发任务都无法完成。

2.1 普通函数this指向

this是执行上下文中很重要的一个组成部分。同一个函数调用方式不同,得到的this值也同,我们看下面的例子。

      function foo() {
        console.log(this);
      }
      var o = {
        foo: foo,
      };
      foo(); //window
      o.foo();//o

上面的例子中,foo函数被调用了两次,但是得到的结果却不一样。为什么呢?

this关键字是谁调用就指向谁,函数中的this只能在运行时才能最终确定。

在全局作用域下调用foo(),想当于 window.foo(); 函数,所以this就会指向全局作用域对象window。

o.foo();调用foo方法的是o对象,所以 this 指向的是o;

2.2 箭头函数this指向

那么如果把foo函数改为箭头函数呢?

 const foo = () => {
        console.log(this);
      };
      var o = {
        foo: foo,
      };
      foo(); //window
      o.foo(); //window

我们从结果可以看出,改为箭头函数后,不论用什么引用来调用它,都不影响它的this值。

箭头函数this 指向箭头函数定义时所处的对象,而不是箭头函数使用时所在的对象,默认使用父级的this)。

而且箭头函数的this按照词法作用域绑定好之后,就无法再通过call(),apply()或者bind()修改this的值了,这些方法传入的新作用
域参数会被无视。

const obj = {
    a: () => {
        console.log(this)
    }
}
obj.a()  //打出来的是window

在使用箭头函数的例子里,因为箭头函数默认不会使用自己的this,而是会和外层的this保持一致,最外层的this就是window对象。

2.定时器中的this

const obj = {
    a: function() {
        console.log(this) //obj
        window.setTimeout(() => { 
            console.log(this) //obj
        }, 1000)
    }
}
obj.a.call(obj)  //第一个this是obj对象,第二个this还是obj对象

函数obj.a没有使用箭头函数,因为它的this还是obj,而setTimeout里的函数使用了箭头函数,所以它会和外层的this保持一致,也
是obj;如果setTimeout里的函数没有使用箭头函数,那么它打出来的就是是window对象。

// 对li进行操作
const oLis = document.querySelectorAll('li');
oLis.forEach(function(item,key){
    // console.log(this);  // 输出的是forEach的函数的this指向
    // 箭头函数的this,是父级程序,forEach()的this,是window
    item.addEventListener('click' , ()=>{
        // console.log(key,this);
    })
})

forEach()中 函数的this指向,就是window
const obj = {
        // 普通函数,this指向对象
        fun1: function () {
          console.log(this);
        },
        // 箭头函数this指向是,父级程序
        // 父级程序是对象
        // 只有函数有this,obj对象没有this
        // 父级程序没有this,指向的是window
        fun2: () => {
          console.log(this);
        },

        // fun3是一个普通函数,this指向的是obj对象
        fun3: function () {
          // fun4,是一个箭头函数,this指向的是父级程序的this指向
          // 父级程序是fun3,fun3的this是对象,fun4箭头函数的this也是对象
          const fun4 = () => {
            console.log(this);
          };
          fun4();
        },
      };
      obj.fun1();
      obj.fun2();
      obj.fun3();

3.改变this的指向

重点: 箭头函数,不能改变this指向,只有普通function函数,能改变this指向

改变this指向的方法

1, call()方法
语法: 函数.call(参数1,其他参数…可以是多个或者没有 )
作用: 调用并且执行函数,同时,将函数的this指向,定义为指定的内容(参数1)
参数1,是改变的this的指向
其他参数,是原始函数的实参,原始函数有几个形参,此时就要对应的输入几个实参,没有形参,就没有实参

2, apply()方法
语法: 函数.apply(参数1,参数2) 只有两个参数
参数1:改变的this的指向内容
参数2:原始函数的实参,必须是一个数组的形式,将实参定义成数组的单元
其他用法和作用于 .call是相同的

总结: call方法与apply方法,作用,效果,都是完全一致的
只是对于原始函数的参数赋值方法,不同
call方法是通过其他多个参数来实现
apply方法是通过一个数组参数,来实现
两个方法没有本质的区别,爱用哪个用那个

3, bind()方法
语法: const 变量 = 函数.bind(参数1);
不是立即执行函数(下一篇博客有介绍 立即执行函数)
生成一个新的函数,这个新的函数是改变this指向之后的新的函数
参数1,定义的要改变的的this指向
其他参数,一般不定义,是使用函数原有的形参

总结:
call apply 都是立即执行函数
参数1,都是改变的this指向
其他参数,是原始函数的形参(可以有,也可以没有)
bind 不是立即执行函数,是生成一个新的函数
参数1,是改变的this指向
就使用原始函数的形参。

const obj1 = {
    name:'张三',
    age:18,
    sex:'男',
}

const obj2 = {
    name:'李四',
    fun2 : function(){
        console.log(this);
    }
}
// 对象中的函数,this指向的是这个对象,obj2
obj2.fun2();
// 改变this指向,指向的是obj1这个对象
// 代用,并且执行fun2这个函数,同时将fun2的this指向,从原始的obj2,改变为obj1
obj2.fun2.call(obj1);

// 带有参数的函数,this指向的改变

// 定义的带有参数的普通函数
function fun3(name,age,sex){
    console.log(name,age,sex,this);
}
// 执行时,输出实参,此时this指向是window
fun3('张三',18,'男');

// 改变this指向 , call方法
fun3.call(obj1,'李四',20,'女');

// 改变this指向 , apply方法
fun3.apply(obj1 , [ '王五' , 20 , '不知道' ])

// bind方法,不是立即执行函数,而是定义生成一个新的函数
// 新生成的函数,this指向是参数1
// 新生成的函数,形参是原始函数fun3的形参
const fun4 = fun3.bind(obj1);

fun4('王二麻子' , 100 , '不详');

this函数的总结

1,普通的function函数

声明式 --- window
赋值式 --- window
forEach循环 --- window
定时器,延时器 --- window
对象中的函数 --- 对象本身
事件绑定事件处理函数 --- 绑定事件的标签

2,箭头函数的this指向
父级程序的this指向
如果父级程序有this指向(父级程序也是函数),this指向的就是父级程序的this指向
如果父级程序没有this指向(数组,对象…),this指向的是window

面向对象和面向过程

从面向过程,改造成面向对象
1,获取的数据,标签对象,要以参数的形式,定义给构造函数和实例化对象
获取标签对象时,一般获取父级,传参父级,在构造函数中,通过父级标签,获取子级标签独享
2,必须非常非常非常注意 this的指向,一般在面向对象中 都是使用箭头函数
如果万一不能清楚地知道this指向,可以先输出 this
3,其他步骤和思路基本相同,没有区别

1,改不改箭头函数,看内部是否需要 指向实例化对象的this
如果需要,可以改箭头函数,或者是提前存储this指向
如果不许需要,改不改箭头函数都行
一切以实现程序为最终需求,程序能执行就可以
2,之前面向过程的是参数,数据等,
现在是面向对象编程,要在对象的属性中,定义参数数据
也就是通过 构造函数 this.属性 = 属性值 语法来定义需要获取的参数数据
3,定义在构造函数方法中的一些变量数据,并不是定义在实例化对象中的属性
没有必须写成 this.属性 = 属性值 的形式
只要写成普通的变量定义即可
使用时,也是直接使用变量,不需要添加this
4,在构造函数的方法中,调用其他的函数方法
语法形式 应该是 实例化对象.函数方法名称()
在构造函数中,使用this来指向实例化对象,写成 this.函数方法名称()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半夏_2021

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值