zoukankan      html  css  js  c++  java
  • this指向和apply,call,bind三者的区别

    一、前言

    this指向,apply,call,bind的区别是一个经典的面试问题,同时在项目中会经常使用到的原生的js方法。同时也是ES5中的众多坑的一个。ES6中可能会极大的避免了this产生的错误,有时候需要维护老的项目还是有必要了解一下this的指向和apply,call,bind三者的区别。

    二、this的指向

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

    首先我们看一个栗子1:

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

    输出windowsName,是因为“this永远指向最后调用它的那个对象”,我们看到调用a的地方a(),前面没有调用的对象那么就是全局对象window,就是全局对象调用a(),相当于window.a()。

    如果使用严格模式,全局对象就是undefined,会报错name of undefined

    栗子2:

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

    在这个栗子中,函数fn是对象a调用的,所以console是a中的name

    栗子3:

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

    这个栗子中,记住“this永远指向最后一个调用它的那个对象”,调用fn的对象有window,a,但是最后调用fn是a对象,所以this指向对象a中的name。

    栗子4:

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

    为啥undefined,调用fn的对象有:window,a,最后一个调用fn是a,但是a中没有对那么进行定义,也不会继续向上一个对象寻找 this.name,而是直接输出 undefined,所以this.name为undefined。

    栗子5(比较坑):

    var name = "windowsName";
    var a = {
        name : null,
        // name: "Cherry",
        fn : function () {
            console.log(this.name);      // windowsName
        }
    }
    var f = a.fn;
    f();

    这个栗子比较坑,为啥 不是null,因为虽然将a对象的fn方法赋值给变量f,但是没有调用,“this永远执行最后一个调用ta的那个对象”,由于刚刚的f没有调用,所以fn()最后仍然是被window调用的,所以this指向的也就是window。

    注意:this的指向并不是在创建的时候可以确定,在ES5中,永远都是this永远指向最后调用它的那个对象

    栗子6:

    var name = "windowsName";
    function fn() {
        var name = 'Cherry';
        innerFunction();
        function innerFunction() {
            console.log(this.name);      // windowsName
        }
    }
    fn()

    三、怎样改变this的指向

    改变this的指向,我总结以下的方法:

    (1)使用ES6中箭头函数

    (2)函数内部使用_this = this

    (3)使用apply,call,bind方法

    (4)new实例化一个对象

    举个栗子7:

    var name = "windowsName";
    var a = {
        name : "Cherry",
        func1: function () {
            console.log(this.name)     
        },
        func2: function () {
            setTimeout(  function () {
                this.func1()
            },100);
        }
    };
    a.func2()     // this.func1 is not a function

    在这个栗子中,不使用箭头函数情况下,会报错的,因为最后调用setTimeout的对象时window,但是在window并没有func1函数。

    我们改变this的指向这一节将吧这个栗子作为demo进行改造。

    1、ES6中的箭头函数

    众所周知,ES6的箭头函数是可以避免ES5中this的坑,箭头函数的this始终指向函数定义时候的this,而并不是执行时候。箭头函数需要记住这句话:“箭头函数没有this绑定,必须通过查找作用域来决定其值,如果箭头函数被非箭头函数包含,则this的绑定的是最近一层非箭头函数的this,否则,this为undefined”

    栗子8:

    var name = "windowsName";
    var a = {
        name : "Cherry",
        func1: function () {
            console.log(this.name)     
        },
        func2: function () {
            setTimeout( () => {
                this.func1()
            },100);
        }
    };
    a.func2()     // Cherry

    2、在函数内部使用_this = this

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

    栗子9:

    var name = "windowsName";
    var a = {
        
        name : "Cherry",
        func1: function () {
            console.log(this.name)     
        },
        func2: function () {
            var _this = this;
            setTimeout( function() {
                _this.func1()
            },100);
        }
    };
    a.func2()       // Cherry

    在func2中,首先设置var _this = this,这里this是调用func2的对象a,为了防止在func2中的setTimeout被window调用而导致的在setTimeout中的this为window。我们将this赋值给一个变量_this,这样在func2中我们使用_this就是指向对象a了。

    3、使用apply

    栗子10:

    var a = {
        name : "Cherry",
        func1: function () {
            console.log(this.name)
        },
        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.apply(a),100);
        }
    };
    a.func2()            // Cherry

    在栗子中,apply()方法调用一个函数,其具有一个指定的this值,以及作为一个数组(或者类似数组的对象)提供的参数,fun.apply(thisArg, [argsArray])

    thisArg:在fun函数运行时指定的this值。指定this的值并不一定是函数执行时真正的this值,如果是原始值的this会指向该原始值的自动包装对象。

    argsArray:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给fun函数。参数为null或者undefined,则表示不需要传入任何参数。

    4、使用call

    栗子11:

    var a = {
        name : "Cherry",
        func1: function () {
            console.log(this.name)
        },
        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.call(a),100);
        }
    };
    a.func2()            // Cherry

    在栗子中,call()方法调用一个函数,其具有一个指定的this值,以及若干个参数列表,fun.call(thisArg, arg1, arg2, ...)

    thisArg:在fun函数运行时指定的this值。指定this的值并不一定是函数执行时真正的this值,如果是原始值的this会指向该原始值的自动包装对象。

    arg1, arg2, ...:若干个参数列表

    5、使用bind

    栗子12:

    var a = {
        name : "Cherry",
        func1: function () {
            console.log(this.name)
        },
        func2: function () {
            setTimeout(  function () {
                this.func1()
            }.bind(a)(),100);
        }
    };
    a.func2()            // Cherry

    在栗子中,bind()方法创建一个新的函数,当被调用时,将其this的关键字设置为提供的值,在调用新函数时,在任何提供一个给定的参数序列。

    bind创建了一个新函数,必须手动去调用。

    四、apply,call,bind区别

    1、apply和call的区别

    apply和call基本类似,他们的区别只是传入的参数不同。apply传入的参数是包含多个参数的数组,call传入的参数是若干个参数列表。

    栗子13:

    var a ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b);
            console.log( this.name );
        }
    }
    var b = a.fn;
    b.apply(a,[1,2])     // 3   Cherry

    栗子14:

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

    2、bind和apply、call区别

    bind方法会创建一个新的函数,当被调用的时候,将其this关键字设置为提供的值,我们必须手动去调用。

    var a ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b);
            console.log( this.name );
        }
    }
    var b = a.fn;
    b.bind(a,1,2)()   //3   //Cherry
  • 相关阅读:
    Mysql中varchar类型的猫腻!
    震惊!java中日期格式化的大坑!
    mysql数据库限制多次登录失败,限定用户重试时间
    2021年回顾与展望
    多线程循环打印abc
    2020年总结-用学习过的技术搭建一个简单的微服务框架 + 源码
    回溯算法
    PyTorch 中 weight decay 的设置
    数据结构与算法——计数排序
    数据结构与算法——堆排序
  • 原文地址:https://www.cnblogs.com/chengxs/p/10554578.html
Copyright © 2011-2022 走看看