zoukankan      html  css  js  c++  java
  • 深入理解this,bind、call

    直接看this
    直接看call和bind

    首先放一道题:

    var a={
    	a:'haha',
    	getA: function(){
    		console.log(this.a);
    	}
    }
    var b= {
    	a:'hello'
    }
    var getA = a.getA;
    var getA2 = getA.bind(a);
    function run(fn){
    	fn();
    }
    a.getA();//
    getA();//
    run(a.getA);//
    getA2.call(b);//
    

    输出是什么?

    可以花几分钟先自己想想。

    嘿嘿~
    嘿嘿~
    嘿嘿~
    嘿嘿~
    嘿嘿~
    嘿嘿~
    嘿嘿~
    嘿嘿~
    嘿嘿~
    嘿嘿~

    公布答案:

    a.getA();//haha
    getA();//object
    run(a.getA);//object
    getA2.call(b);//haha
    

    答对了么?

    这里考察了三个点:

    形参实参的理解、this的指向、call和bind对this指向的影响。

    第一个我相信大家都没问题,这里主要分析后面的两个问题。

    this指向问题

    this是大部分刚入前端的人都会遇到的坑。
    这里通过借鉴网上的文章和自己的理解来总结一下。

    this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,实际上this的最终指向的是那个调用它的对象。

    我的理解是,看执行的时候谁在调用就指向谁,但这样理解this也不算完全准确。

    这里有三种简单情况:

    1. 如果函数中的this没有调用它的对象,那么this指向的就是window(严格模式下这种情况的this会为空,即undefined)。
    2. 如果函数中的this被不包含子对象的对象所调用,那么this指向的就是调用它的对象。
    3. 如果函数中的this被包含多级对象的对象调用,this指向的也只是它上一级的对象,如下例。
    var demoObj = {
        a:1,
        b:{
            fun:function(){
                console.log(this.a); 
            }
        }
    }
    demoObj.b.fun();//undefined
    

    这里this不是指向demoObj对象,而是指向demoObj.b对象,这里找不到demoObj.b对象里的a,所以会输出undefined。

    还有三种特殊情况:

    • 还是上面的例子,改一下调用函数的方式,如下。
    var demoObj = {
        a:1,
        b:{
            fun:function(){
                console.log(this.a); 
            }
        }
    }
    var newFun = demoObj.b.fun;
    newFun();//undefined
    

    这里还是得到undefined,但是this的指向却是window,这里的undefined是因为没找到window对象里的a,才输出的undefined。
    虽然函数fun是被对象b所调用,但是在将fun赋值给变量newFun的时候并没有执行,newFun的上级对象window,所以最终执行时指向的是window。

    • 构造函数用new实例对象时对this的影响。
    function Fun(){
        this.name = "haha";
    }
    var stu = new Fun();
    console.log(stu.name); //haha
    

    这里之所以对象stu.name可以输出haha,是因为new关键字就是创建一个对象实例,这个stu对象中包含了this.name这个属性,相当于复制但却没有执行。在执行时调用这个函数Fun的是对象stu,所以this指向的就是对象stu。

    用new操作符创建对象时发生的事情:

    第一步: 创建一个Object对象实例。
    第二步: 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)。
    第三步: 执行构造函数中的代码(这里的执行并不是真的让this指向哪里,而是为这个新对象添加属性)。
    第四步: 返回新生成的对象实例

    原本的构造函数是window对象的方法,如果不用new操作符而直接调用,那么构造函数的执行对象就是window,即this指向了window。现在用new操作符后,this就指向了新生成的对象。理解这一步至关重要。

    • 有return的函数在new时对this的影响(正常的构造函数是没有return语句),我们先看下面的几个例子。
    //例1
    function Fun()  
    {  
        this.name = 'haha';  
        return {};  
    }
    var stu = new Fun();  
    console.log(stu.name); //undefined
    
    
    //例2
    function Fun()  
    {  
        this.name = 'haha';  
        return function(){};
    }
    var stu = new Fun();  
    console.log(stu.name); //undefined
    
    //例3
    function Fun()  
    {  
        this.name = 'haha';  
        return 123;
    }
    var stu = new Fun();  
    console.log(stu.name); //haha
    
    //例4
    function Fun()  
    {  
        this.name = 'haha';  
        return undefined;
    }
    var stu = new Fun();  
    console.log(stu.name); //haha
    

    可以看出:
    如果return的是一个对象,那么this会指向返回的对象,如果return的不是一个对象,那么this还是指向函数的实例。

    但是return的是null时比较特殊。虽然null也是对象,但是this还是指向函数的实例。

    //例5
    function Fun()  
    {  
        this.name = 'haha';  
        return null;
    }
    var stu = new Fun();  
    console.log(stu.name); //haha
    

    至此,this就说这么多,相信前三个输出大家都能理解了,接下来说说call和bind。

    call和bind

    call和apply只有参数不同,这里就只讨论call,因为call和bind参数使用方法是一样的。

    • call是动态的改变this的指向,即换个对象执行原对象方法的方法,并立即执行
    • bind是静态改变this的指向,并返回一个修改后的函数

    就拿开始的题目最后一个输出来说:

    如果只是使用call的话:

    getA.call(b);//hello
    getA.call(a);//haha
    

    在执行到这两句时动态改变了this的指向,所以call(b)的输出hello,call(a)的输出haha。

    接下来看有bind影响的:

    var getA2 = getA.bind(a);
    

    这里getA其实是a.getA大家应该理解,那么getA.bind(a)将this指向a,其实还是返回了a.getA函数赋值给了getA2。注意:其实函数没有变化,但是内部已经将this指向了a

    getA2.call(b);//haha
    //相当于a.getA.call(b);
    

    之后call在动态调用时,内部的this已经指向了a,不会再指向b,因此会输出haha。

    如果还不是很理解,就将绑定a改成b,如下:

    var getA2 = getA.bind(b);
    getA2.call(a);//hello
    

    此时无论call里是a还是b,都会输出hello,因为内部的this已经被bind绑定指向b了。

    总的来说,call方法是在调用时改变this并立即执行这个函数,bind方法可以先改变函数中的this,之后对应的函数可以在需要的时候再调用。


    解决了题,再学一点点。

    bind的参数可以在执行的时候再次添加,但是要注意的是,参数需要按照形参的顺序添加。例如:

    var demoObj = {
        name:"haha",
        fun:function(a,b,c){
            console.log(a,b,c);
        }
    }
    var newFun = demoObj.fun;
    var newFun2 = newFun.bind(demoObj,5);
    newFun2(7,9);//5,7,9
    

    看看应该就知道怎么用了。

    这里放一个很棒的文章JavaScript 的 this 指向问题深度解析

    好了,谢谢大家阅读~

  • 相关阅读:
    Java中的Iterator的用法
    Java中Collection与Collections的区别
    hashCode()
    网络安全态势感知(NSSA)----引言
    并发与并行
    进程和线程的关系
    由默认参数list引起的疑惑——python
    python堆和栈在list的表现
    一个解决python抓取中文网页乱码的办法
    python编写调用shell命令脚本
  • 原文地址:https://www.cnblogs.com/macq/p/6526809.html
Copyright © 2011-2022 走看看