zoukankan      html  css  js  c++  java
  • 让人又爱又恨的this

    this是个神奇的东西,

    既可以帮助我们把模拟的类实例化、

    又可以在事件绑定里准确指向触发元素、

    还可以帮助我们在对象方法中操作对象的其他属性或方法、

    甚至可以在使用apply、call、bing、filter等方法时指向我们指定的对象,以便灵活的使用。

    但是如此强力的东西,势必有时难以把握,今天遇到一问题,出了错,重新温故下常见的this指向问题。

    要说灵活无比的this的指向,很有必要先知道一个知识点,JavaScript中有四种调用模式。

    this的指向很大程度收调用模式的影响,具体如下

    1.构造器调用模式=>this指向实例化出来的对象

    2.方法调用模式=>this指向方法所属的对象

    3.函数调用模式(即函数不是对象的方法,仅仅普通调用时,今天就错在这,5555)=>this指向全局对象global(在浏览器运行环境下,global其实就是window)

    4.apply调用模式=>this指向apply方法接收的第一个参数

    看文字或许不太明了,上点代码

    //先来看看构造器调用模式和方法调用模式
    
    var name='你查看的是全局对象的属性';
    var Foo=function (name) {
        this.name=name;
        //这里的this是在构造函数里,当new+构造函数实例化对象时,this会被绑定到实例化的对象上
        this.say=function () {
            console.log(this.name);
           // 这里或许会有误解,事实上此处的this并没有绑定到构造函数实例化的对象上!!
        }
    }
    var foo=new Foo('我是构造函数实例化的对象');
    //这里就是构造器调用模式,构造函数里的this就是此时绑定到实例化对象上的
    
    foo.say();//打印:我是构造函数实例化的对象
    //这里的调用就是方法调用模式,它的特点是,有一个.或者[]这样的属性提取操作
    
    
    //下面我们来证明console里的this没有被绑定到实例化的对象上
    var say1=foo.say;
    say1();//打印:你查看的是全局对象的属性
    
    //造成这样的原因在于,console所属于的函数被赋值给了this.say,但是并没有被调用,所以this的指向并没有决定。知道这很重要
    //再来看看函数调用模式,此处代码接着上面的写
    
    function foo2() {
        console.log(this.name);
    }
    foo2();//打印:你查看的是全局对象的属性
    //这就是函数调用模式
    
    console.log(this.name);//打印:你查看的是全局对象的属性
    //注意哈,当不在函数里时this也是指向全局对象
    
    var obj1={
        name:'我是对象obj1的属性',
        say:function(){
            console.log("1:"+this.name);
            (function(){
               console.log("2:"+this.name)  
            })();
             //别被误导了哈,这里没有什么作用域链,紧记不是apply调用(即this指向没有被强制改变)、方法、构造器调用时,都是函数调用模式,this都指向全局对象!!  
        }
    }
    obj1.say();
    //打印:1:我是对象obj1的属性-----此处this所在的函数是say,它是被obj1.say()用方法调用模式调用的
    //打印:2:你查看的是全局对象的属性------此处的this所属的函数是一个立即执行的匿名函数,它不是被方法调用的,是函数调用模式,此时不管它属于哪个对象,只要它没有被bind等方法改变this指向,它里面的this一定指向全局对象    
    //最后来看看apply调用模式
    
    var obj2={
         name : '我是对象obj2的属性', 
    }
    foo.say.apply(obj2);
    //打印: 我是对象obj2的属性
    //这就是apply调用模式,相似的还有  
     foo.say.call(obj2);
    //打印: 我是对象obj2的属性
    
    //两者的共同点,就是
    //调用的函数里的this会指向apply/call的第一个参数
    
    //至于两者的区别,主要是传递的其他参数不同,感兴趣的可以自己了解,百度谷歌都很多

    其实还有其他改变this指向的方法,就是一开始题到的bindfilter等方法,

    但其实都大同小异,本质都是改变this的指向,使其指向传入参数,和apply/call类似,就不多做阐述了

    最后上一下今天做错的题

    //代码很简单,就是一个构造函数,
    //要求在waitAndShout方法被调用时,延迟调用shout方法
    
    //刚开始我是这么写的,毫无疑问,这样错了
    var Obj=function (msq) {
        this.msq=msq;
        this.shout=function () {
            alert(this.msq);
            }
        this.waitAndShout=function () {
            setTimeout(function () {
                this.shout()//会在这里报错,not a function
            },2000);
        }
    }
    var obj=new Obj('ddd');
    obj.waitAndShout() 
    
    //这是为什么呢?
    //如果你仔细看上面的代码,想必早已知道原因。
    //没错,原因就是setTimeout所在的函数只是被赋值给waitAndShout,
    //但并没有被调用,他里面的this也不会在构造函数被调用时,指向实例化的对象
    //另一个关键是setTimeout里执行的函数是被函数调用模式调用的,所以这里的this是全局对象,而我们没有给window创建shout方法,当然也就不是个函数了
    
    //那么要怎么解决这个问题呢,解决方法很简单
    //用一个变量接收this
    this.waitAndShout=function () {
        var that=this;
           //重点就在这里!注意看,这里的this所属的函数被赋值给了waitAndShout
           //也就是说this所在的函数就是waitAndShout,当他被调用时
           //obj.waitAndShout()很明显是方法调用,那this就指向了obj
           //此时我们把这个对象的地址传给变量that,那that就一直指向这个对象了,
           //延迟调用时就不涉及this的指向问题,不用担心setTimeout延迟执行是以函数调用模式,也就实现了目标。
    
        setTimeout(function () {
            that.shout()
        },2000);
    }

    汗,本来只是想随便写写,温习下的,结果写那么多

    表达能力还是需要锻炼啊,注释比代码多那么多

    时间关系,这次暂时不精简注释了,以后多注意下,多写写,应该就能简短的注释说明问题了吧。0.0

  • 相关阅读:
    editplus 快捷键
    python 堆栈
    python 矩阵转置
    python 单向链表
    python 读空的json文件
    c++ 结构体
    手把手教你如何利用Meterpreter渗透Windows系统
    vuejs npm chromedriver 报错
    强大的开源网络侦查工具:IVRE
    在vue 中使用Stylus
  • 原文地址:https://www.cnblogs.com/liyan-web/p/5893852.html
Copyright © 2011-2022 走看看