美文网首页
this、call、apply、bind

this、call、apply、bind

作者: dingFY | 来源:发表于2020-08-10 08:43 被阅读0次

    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.apply(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.apply(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;
    }
    

    文章每周持续更新,可以微信搜索「 前端大集锦 」第一时间阅读,回复【视频】【书籍】领取200G视频资料和30本PDF书籍资料

    相关文章

      网友评论

          本文标题:this、call、apply、bind

          本文链接:https://www.haomeiwen.com/subject/wuuynhtx.html