JS中this到底指向谁(call、apply、bind)

什么是this

JavaScript中的this是什么?
定义:this是包含它的函数作为方法被调用时所属的对象。

function fn1(){
	this.name = "halo";
}
fn1();
  • 我们将定义拆分一下
    • 包含它的函数:包含this的函数是fn1。
    • 作为方法被调用:fn1(); 此处fn1函数被调用。
    • 所属的对象:函数式调用函数默认所属的对象是window。

通过上面三点分析,很容易知道fn1函数里的this指向的是window。
那么如果是更复杂的场景我们如何判断this的指向呢?
在es6箭头函数之前,想要判断一个函数内部this指向谁,就根据以下四种方式来决定的。

函数的4种调用方式

一、函数式调用

	var age = 18;
    var p = {
        age:15
        say:function(){
            console.log(this.age);
        }
    }
	var s1 = p.say;
    s1();       //18

这个栗子可以很清晰的看到,包含 this的函数是say,函数执行时,函数默认所属的对象是window,那么这里的this就是window,输出18。所以通过函数式调用,函数内的this指向window
注:一个构造函数如果不用new去调用的话,那么构造函数内部的this也不是指向构造函数的实例,而是指向window。

二、方法调用

  • 方法调用栗子一
	var age = 18;
    var p = {
        age:15,
        say:function(){
            console.log(this.age);
        }
    }
    p.say(); //方法调用 this=>p  15
    var fn1 = p.say;
    fn1(); // 函数调用 this=>window  18

方法式调用,this指向看是哪个对象调用的方法,在这里this指向的是调用say方法的p对象。再看一个稍微复杂的栗子。

  • 方法调用栗子二
	var clear = function () {
        console.log(this.name);
    }
    var name = 'window';
    var jerry = { j: clear, name: 'jerry' };
    var tom = { c: jerry, name: 'tom' };
    tom.c.j(); // jerry

找调用方法的对象只找一级,就是函数“.”前面的对象。我们看调用j函数(也就是clear函数)的对象是c。对象c是tom对象的一个属性,属性指向的是jerry对象,所以这里调用j函数的对象是jerry,因此this指向的是jerry,输出"jerry"。

三、new调用

new调用也叫构造函数调用

  • 栗子1
 var age = 20;
 var p = {
   age: 15,
   say: function () {
        this.age = 10;
        console.log(this.age);
    }
 }
 new p.say(); // 10

通过new方式调用就是构造函数方式调用,那么函数内部的this就是该构造函数的实例。所以这里输出10。

四、call、apply、bind(上下文方式)

call、apply、bind都是改变函数this指向的方法,call和apply用法相近。

call & apply
  • 栗子1
var length = 25;
function f1(p1, p2) {
	console.log(this.length);
}

var arr = [1, 2, 3];
f1(); // 25
f1.call(this); // 25 
f1.call(arr); // 3 
f1.apply(arr); //3 

call & apply改变了函数this的指向,第一个参数如果是一个对象,意为将函数内部的this指向该对象。

  1. 直接调用f1()的时候,函数内的this指向的是window,所以输出length的值为25
  2. 调用f1.call(this),这里参数this相当于window的this,那么就是将函数的this指向全局的this。所以输出25
  3. 调用f1.call(arr),这里将arr这个数组作为参数,那么就是将函数的this指向该数组。函数内部this.length就相当于arr.length,输出3
  4. apply的用法和call相近,第一个参数都是改变函数的this指向。不同之处是如果函数具有实参
    4.1 call(arr,“param1”,“param2”); 实参用逗号分割
    4.2 apply(arr,[“param1”,“param2”]); 实参放到数组中

:第一个参数如果不是对象

  1. 如果是null、undefined那么函数的this指向window
  2. 如果是数字、字符串、布尔型等,函数内部this指向对应构造函数(Number、String、Boolean)的实例
f1.call(5); // undefined   this=>new Number(5)
f1.call("123"); // undefined   this=>new String("123") 
f1.call(true); // undefined   this=>new Boolean(true)
//上述代码可以用apply替换
bind

bind也是改变函数this指向,但是在es5中才有,注意兼容问题(ie9+)。

  • 栗子1
function fn2() {
   console.log(this.length);
}

var length = 10;
var fnBind = fn2.bind({ length: 100 });

fnBind(); // 100

调用函数的bind方法会产生了新的函数,新函数里面的逻辑和原函数一致,唯一不同是this指向为传入的对象。
注:调用bind方法不会执行函数,而是返回新的函数,所以通常简写为:

(function fn2() {
   console.log(this.length);
}).bind({length: 100})();
  • 栗子2

老问题新思路,开发中我们经常会遇到下面的问题

var obj = {
     age: 18,
     run: function () {
         var _that = this;//传统的解决方案
         setTimeout(function () {
             console.log(this.age);//不会输出18,输出undefined,this=>window
             console.log(_that.age);//18
         }, 50);
     }
}

因为在setTimeout中this指向了window,所以我们通常会在setTimeout之前var _that = this;,然后通过_that变量获取到原始指向obj对象的this。
有了bind后:

var obj = {
    age: 20,
    run: function () {
        setTimeout((function () {
            console.log(this.age);
        }).bind(this), 50);
    }
}
obj.run();//20

这里传入的this是指向obj的,通过执行了bind方法,匿名函数本身没有执行,只是改变了函数内部的this指向,指向了obj对象。

  • 栗子3
//示例1
var obj1 = {
   name: "aaa",
   drink: function () {
       console.log(this.name);
   }
}
obj1.drink.bind({ name: "bbb" })(); // bbb


//示例2
var obj2 = {
   age: 10,
   run: function () {
       setInterval((function () {
           console.log(this.age);
       }).bind(this), 1000)
   }
}
obj2.run(); // 10

如果对输出都没有疑问,说明已经掌握了bind的用法。

call、apply、bind总结
  • 相同点:三者都可以改变函数内this指向
  • 不同点:call&apply改变this指向的同时执行了函数。bind没 有执行函数,而是创建了一个新的函数,并改变了函数内的this指向。

this总结

本文通过介绍了函数的四种调用方式讲解了js中this指向的问题。并重点介绍了call、apply、bind方法如何改变this的指向。
this是js中最重要的知识点之一,也是学习很多其他js基础知识的前置。希望大家可以通过阅读本文掌握其用法。
如文中有不足、不严谨、不正确的地方,还请指出。

猜你喜欢

转载自blog.csdn.net/weixin_42397257/article/details/87887139