zoukankan      html  css  js  c++  java
  • 彻底弄懂js中this指向

     彻底弄懂js中this指向! 

    this是什么?
    定义:this是包含它的函数作为方法被调用时所属的对象。

      首先,this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁实际上this的最终指向的是那个调用它的对象(虽然在很多情况下那样去理解不会出什么问题,但是实际上会有些特殊存在,那么接下来我会深入的探讨这个问题。

    例子1:

    function a(){
        var user = " 小明";
        console.log(this.user); //undefined
        console.log(this); //Window
    }
    a(); 

    按照我们上面说的this最终指向的是调用它的对象,这里的函数a实际是被Window对象所点出来的,下面的代码就可以证明。

    function a(){
        var user = " 小明";
        console.log(this.user); //undefined
        console.log(this);  //Window
    }
    window.a();

    例子2:

    var o = {
        user:" 小明",
        fn:function(){
            console.log(this.user);  // 小明
        }
    }
    o.fn();

      这里的this指向的是对象o,因为你调用这个fn是通过o.fn()执行的,那自然指向就是对象o,this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁。

    如果要彻底的搞懂this必须看接下来的几个例子

    例子3:

    var o = {
        user:" 小明",
        fn:function(){
            console.log(this.user); // 小明
        }
    }
    window.o.fn();

      这段代码和上面的那段代码几乎是一样的,但是这里的this为什么不是指向window,如果按照上面的理论,最终this指向的是调用它的对象,这里先说个而外话,window是js中的全局对象,我们创建的变量实际上是给window添加属性,所以这里可以用window点o对象。

      这里先不解释为什么上面的那段代码this为什么没有指向window,我们再来看一段代码。

    var o = {
        a:10, 
        b:{
            a:12,
            fn:function(){
                console.log(this.a); //12
            }
        }
    }
    o.b.fn();

      这里同样也是对象o点出来的,但是同样this并没有执行它,那你肯定会说我一开始说的那些不就都是错误的吗?其实也不是,只是一开始说的不准确,接下来我将补充一句话,我相信你就可以彻底的理解this的指向的问题。

      情况1如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明在js的严格版中this指向的不是window。

      情况2如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。

      情况3如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象,例子3可以证明。


    接下来我们继续看几个例子。

    var o = {
        a:10,
        b:{
            // a:12,
            fn:function(){
                console.log(this.a); //undefined
            }
        }
    }
    o.b.fn();

    尽管对象b中没有属性a,这个this指向的也是对象b,因为this只会指向它的上一级对象,不管这个对象中有没有this要的东西。

    还有一种比较特殊的情况,例子4:

    var o = {
        a:10,
        b:{
            a:12,
            fn:function(){
                console.log(this.a); //undefined
                console.log(this); //window
            }
        }
    }
    var j = o.b.fn;
    j();

    这里this指向的是window,这句话同样至关重要this永远指向的是调用它的对象,也就是看它执行的时候是谁调用就指向谁,例子4中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行,所以最终指向的是window,这和例子3是不一样的,例子3是直接执行了fn。

     

    构造函数版this:

    function Fn(){
        this.user = " 小明";
    }
    var a = new Fn();
    console.log(a.user); // 小明

      这里之所以对象a可以点出函数Fn里面的user是因为new关键字可以改变this的指向,理解这句话可以想想我们的例子3,我们这里用变量a创建了一个Fn的实例(相当于复制了一份Fn到对象a里面),此时仅仅只是创建,并没有执行,而调用这个函数Fn的是对象a,那么this指向的自然是对象a,那么为什么对象a中会有user,因为你已经复制了一份Fn函数到对象a中,用了new关键字就等同于复制了一份。

    当this碰到return时

    function fn()  
    {  
        this.user = ' 小明';  
        return {};  
    }
    var a = new fn;  
    console.log(a.user); //undefined
    function fn()  
    {  
        this.user = ' 小明';  
        return function(){};
    }
    var a = new fn;  
    console.log(a.user); //undefined
    function fn()  
    {  
        this.user = ' 小明';  
        return 1;
    }
    var a = new fn;  
    console.log(a.user); // 小明
    function fn()  
    {  
        this.user = ' 小明';  
        return undefined;
    }
    var a = new fn;  
    console.log(a.user); // 小明

      从这些例子中可以总结出,如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。

    function fn()  
    {  
        this.user = ' 小明';  
        return undefined;
    }
    var a = new fn;  
    console.log(a); //fn {user: " 小明"}

    特殊情况null也是对象,但是在这里this还是指向那个函数的实例。

    function fn()  
    {  
        this.user = ' 小明';  
        return null;
    }
    var a = new fn;  
    console.log(a.user); // 小明

    当this遇到call、apply、bind方法时

    1、call()

      

    var a = {
        user:"小明",
        fn:function(){
            console.log(this.user); //小明
        }
    }
    var b = a.fn;
    b.call(a);

    通过在call方法,给第一个参数添加要把b添加到哪个环境中,简单来说,this就会指向那个对象。

    call方法除了第一个参数以外还可以添加多个参数,如下:

    var a = {
        user:"小明",
        fn:function(e,ee){
            console.log(this.user); //小明
            console.log(e+ee); //3
        }
    }
    var b = a.fn;
    b.call(a,1,2);

    2、apply()

    apply方法和call方法有些相似,它也可以改变this的指向

    var a = {
        user:"小明",
        fn:function(){
            console.log(this.user); //小明
        }
    }
    var b = a.fn;
    b.apply(a);

    同样apply也可以有多个参数,但是不同的是,第二个参数必须是一个数组,如下:

    var a = {
        user:"小明",
        fn:function(e,ee){
            console.log(this.user); //小明
            console.log(e+ee); //11
        }
    }
    var b = a.fn;
    b.apply(a,[10,1]);

    //注意如果call和apply的第一个参数写的是null,那么this指向的是window对象。

    var a = {
        user:"小明",
        fn:function(){
            console.log(this); //Window {external: Object, chrome: Object, document: document, a: Object, speechSynthesis: SpeechSynthesis…}
        }
    }
    var b = a.fn;
    b.apply(null);

    3、bind()

    bind方法和call、apply方法有些不同,但是不管怎么说它们都可以用来改变this的指向。

    先来说说它们的不同吧。

    var a = {
        user:"小明",
        fn:function(){
            console.log(this.user);
        }
    }
    var b = a.fn;
    b.bind(a);

    我们发现代码没有被打印,对,这就是bind和call、apply方法的不同,实际上bind方法返回的是一个修改过后的函数。

    var a = {
        user:"小明",
        fn:function(){
            console.log(this.user);
        }
    }
    var b = a.fn;
    var c = b.bind(a);
    console.log(c); //function() { [native code] }

    那么我们现在执行一下函数c看看,能不能打印出对象a里面的user

    var a = {
        user:"小明",
        fn:function(){
            console.log(this.user); //小明
        }
    }
    var b = a.fn;
    var c = b.bind(a);
    c();

    总结:call和apply都是改变上下文中的this并立即执行这个函数,bind方法可以让对应的函数想什么时候调就什么时候调用,并且可以将参数在执行的时候添加,这是它们的区别,根据自己的实际情况来选择使用。

    this遇到-定时器以及箭头函数时

    使用js中的定时器(setInterval,setTimeout),很容易会遇到this指向的问题。

    直接上例子:

      var name = 'my name is window';
      var obj = {
          name: 'my name is obj',
          fn: function () {
              var timer = null;
              clearInterval(timer);
              timer = setInterval(function () {
                  console.log(this.name);  //my name is window
              }, 1000)
         }
     }

    在这里,从this.name可以看出this的指向是window。

    如果没有特殊指向,setInterval和setTimeout的回调函数中this的指向都是window。这是因为JS的定时器方法是定义在window下的。但是平时很多场景下,都需要修改this的指向。这里总结了几种:

    1、最常用的方法:在外部函数中将this存为一个变量,回调函数中使用该变量,而不是直接使用this。

      var name = 'my name is window';
      var obj = {
          name: 'my name is obj',
          fn: function () {
              var that = this;
              var timer = null;
              clearInterval(timer);
              timer = setInterval(function () {
                  console.log(that.name);   //my name is obj
             }, 1000)
         }
     }

    在fn中加了var that = this; 回调函数中使用that代替this即可。这种方法最常见,使用也最广泛。

    2、使用bind()方法(bind()为ES5的标准,低版本IE下有兼容问题,可以引入es5-shim.js解决)

    bind()的作用类似call和apply,都是修改this指向。但是call和apply是修改this指向后函数会立即执行,而bind则是返回一个新的函数,它会创建一个与原来函数主体相同的新函数,新函数中的this指向传入的对象。

      var name = 'my name is window';
      var obj = {
          name: 'my name is obj',
          fn: function () {
              var timer = null;
              clearInterval(timer);
              timer = setInterval(function () {
                  console.log(this.name);   //my name is obj
              }.bind(this), 1000)
         }
     }

    在这里为什么不能用call和apply,是因为call和apply不是返回函数,而是立即执行函数,那么,就失去了定时器的作用。

    3、使用es6的箭头函数:箭头函数的最大作用就是this指向。

      var name = 'my name is window';
      var obj = {
          name: 'my name is obj',
          fn: function () {
              var timer = null;
              clearInterval(timer);
              timer = setInterval(() => {
                  console.log(this.name);  //my name is obj
              }, 1000)
         }
     }

    箭头函数没有自己的this,它的this继承自外部函数的作用域。所以,在该例中,定时器回调函数中的this,是继承了fn的this。当然箭头函数也有兼容问题,要是兼容低版本ie,需要使用babel编译才可以。

  • 相关阅读:
    UVA 10617 Again Palindrome
    UVA 10154 Weights and Measures
    UVA 10201 Adventures in Moving Part IV
    UVA 10313 Pay the Price
    UVA 10271 Chopsticks
    Restore DB後設置指引 for maximo
    每行SQL語句加go換行
    种服务器角色所拥有的权限
    Framework X support IPV6?
    模擬DeadLock
  • 原文地址:https://www.cnblogs.com/chenhuichao/p/9351754.html
Copyright © 2011-2022 走看看