this、call、apply、bind

This指针

每一个方法或函数都会有一个this对象,this对象是方法(或函数)在执行时的那个环境,也可以说是这个函数在那个作用域下运行的。说的更通俗一点:this就相当于咱们平时说话时候说的“我”,“我家”的概念。就是说当一个方法在运行的时候,它是属于谁的。它在运行的时候它的家是谁家。

在 ES5 中,其实 this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象

例1:对象的调用

    var person = {
      name: 'Demi',
      describe: function () {
        return '姓名:' + this.name;
      }
    };
    console.log(person.describe()) // 姓名:Demi

上面代码中,this.name是在describe方法中调用,而describe方法所在的当前对象是person,因此this最后调用的那个对象就是person, this.name就是person.name

例2:对象赋值给另外一个对象调用

    var A = {
      name: 'Demi',
      describe: function () {
        return '姓名:' + this.name;
      }
    };
    var B = {
      name: 'dingding'
    };
    B.describe = A.describe;
    console.log(B.describe()) // 姓名:dingding

上面代码中,A.describe属性被赋给B,于是B.describe就表示describe方法所在的当前对象是B,所以this.name就指向B.name

例3:函数调用

    var name = "windowsName";
    function a() {
      var name = "Demi";
      console.log(this.name);          // windowsName
      console.log("inner:" + this);    // inner:[object Window]
    }
    a();
    console.log("outer:" + this);      // outer:[object Window]

上面代码中,this.name是在a()函数中调用, a()前面没有调用的对象,那么this会指向顶层对象Window,相当于window.a()

例4:this 永远指向最后调用它的那个对象,不会向上一个对象寻找未定义变量

    var name = "windowsName";
    var a = {
      // name: "Demi",
      fn: function () {
        console.log(this.name);      // undefined
      }
    }
    window.a.fn();

上面代码中,最后调用fn()的对象是a, 所以this指向的是a对象,也就是说 fn 的内部的 this 是对象 a,而对象 a 中并没有对 name 进行定义,所以 log 的this.name的值是 undefined

例5:匿名函数this指向window

    var obj = {
      name: 'Demi',
      times: [1, 2, 3],
      print: function () {
        this.times.forEach(function (n) {
          console.log(this.name);
        });
      }
    };
    obj.print(); // 无输出

上面代码中,obj.print内部this.times的this是指向obj的,这个没有问题。但是,forEach方法的回调函数内部的this.time却是指向全局对象,导致没有办法取到值。

如何改变this指向

this的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把this固定下来,避免出现意想不到的情况。改变this指向有以下几种方法:

  • 使用ES6箭头函数
  • 在函数内部使用_this=this 
  • JavaScript 提供了call、apply、bind这三个方法来切换this的指向
  • new实例化一个对象

箭头函数

this对象的指向是可变的,但是在箭头函数中,它是固定的。箭头函数体内的this对象,就是定义时所在的对象,而不是调用时所在的对象。

例5:通过箭头函数改变原来指向window的this

    let name = "windowsName";
    let a = () => {
      let name = "Demi";
      console.log(this.name);          // Demi
      console.log("inner:" + this);    // inner:[object a]
    }
    a();
    console.log("outer:" + this);      // outer:[object Window]

上面代码中,原来是window调用a()函数,this指向的是window,将函数改成箭头函数,this指向定义时所在的对象,也就是说this.name指向a

在函数内部使用_this = this

那么这种方式应该是最简单的不会出错的方式了,我们是先将调用这个函数的对象保存在变量 _this中,然后在函数中都使用这个 _this,这样 _this就不会改变了

    var obj = {
      name: 'Demi',
      times: [1, 2, 3],
      print: function () {
        var _this = this
        this.times.forEach(function (n) {
          console.log(_this.name);
        });
      }
    };
    obj.print(); // Demi Demi Demi

Function.prototype.call()

函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。

使用格式:func.call(thisValue, arg1, arg2, ...)  call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数。this.Value为空、null、undefined,则默认传入全局对象。

    var obj = {
      name: 'Demi'
    }
    function a() {
      console.log(this.name);
    };
    
    a.call(obj); // Demi


    function add(a, b) {
      return a + b;
    }
    add.call(this, 1, 2) // 3

Function.prototype.apply()   

apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。

使用格式:func.apply(thisValue, [arg1, arg2, ...])  第一个参数也是this所要指向的那个对象,如果thisValue设为nullundefined,则默认传入全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加。

    function a(x, y) {
      console.log(x + y);
    };

    a.call(null, 1, 1); // 2
    a.call(null, [1, 1]); // 2

Function.prototype.bind()

bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。bind 是创建一个新的函数,我们必须要手动去调用它才会执行。

使用格式:func.bind(thisValue, arg1, arg2, ...) () 第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数。this.Value为空、null、undefined,则默认传入全局对象。

    var a = {
      name: "Demi",
      fn: function (a, b) {
        console.log(a + b)
      }
    }
    var b = a.fn; 
    b.bind(a, 1, 2)() // 3

使用构造函数调用函数

如果函数调用前使用了 new 关键字, 则是调用了构造函数。
这看起来就像创建了新的函数,但实际上 JavaScript 函数是重新创建的对象

    // 构造函数:
    function myFunction(arg1, arg2) {
      this.firstName = arg1;
      this.lastName = arg2;
    }

    // This creates a new object
    var a = new myFunction("Ding", "Demi");
    console.log(a.lastName);  // 返回 "Demi"

new实例化过程

  1. 创建一个空对象 obj;
  2. 将新创建的空对象的隐式原型指向其构造函数的显示原型。
  3. 使用 call 改变 this 的指向
  4. 如果无返回值或者返回一个非对象值,则将 obj 返回作为新对象;如果返回值是一个新对象的话那么直接直接返回该对象。

所以我们可以看到,在 new 的过程中,我们是使用 call 改变了 this 的指向。

var a = new myFunction("Ding","Demi");

new myFunction{
    var obj = {};
    obj.__proto__ = myFunction.prototype;
    var result = myFunction.call(obj,"Ding","Demi");
    return typeof result === 'obj'? result : obj;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@Demi

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

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

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

打赏作者

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

抵扣说明:

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

余额充值