深入学习JavaScript之初识this

  JavaScript是一个词法作用域的编程语言,词法作用域和动态作用域的区别我也说过了,具体在我的《深入理解JavaScript之词法域》https://blog.csdn.net/qq_41889956/article/details/83061472中有介绍,

  总的来说

  动态作用域是根据调用栈关系来确定变量值的,比如在当前的函数找不到,那么就会到它调用的函数中找。

  词法作用域是在词法分析阶段(代码编写时)就已经决定的了。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

今天要学习的  this机制   与   动态作用域差不多。

  this是JavaScript中最为重要的机制之一,同时也是最为复杂的机制之一,在大型项目里,this来this去会让你感到一头雾水。这个  this  与英语单词中理解的  this  不太一样,并不是单纯的指这个函数对象的本身。

 1.1为什么要用this

  接下来请看看例子,让我们来看看this的用处


        function identify() {
            return this.name.toUpperCase();

        }
        function speack() {
            var greeting="Hello,I'm"+identify.call(this);
            console.log(greeting);
        }
        var me={
            name:"Kyle"
        };
        var you={
            name:"Reader"
        };

       console.log(identify.call(me));    //KYLE
       console.log( identify.call( you ));      //READER

        speack.call(me);      //Hello,I'm KYLE
        speack.call(you);     //Hello,I'm RAEDER
    

  在理解这篇代码之前,让我们先来看看,.call()是什么,这是一个能够修改" this"指针的方法,作用是将函数内的"this"指针对象转移到"()"内的对象中

  接下来我们看看代码,代码中,identify(...)函数将"this.name.toUpperCase()"作为返回值,此函数是修改传入对象的name值,使其输出大写。

   这段代码可以在不同的上下文对象(me和you)中重复使用函数identify(...)以及speack(...)来输出不同的结果,而不用对不同的对象编写不同的结果。

如果不使用  this  的话,我们就需要显示的创建一个上下文对象,


        function identify(context) {
        return context.name.toUpperCase();
    }
    function speak(context) {
        var greeting = "Hello, I'm " + identify(context);
        console.log( greeting );
    }
    var me = {
        name: "Kyle"
    };
    var you = {
        name: "Reader"
    };
   console.log(identify(me));    //KYLE
   console.log( identify( you ));      //READER
    speak( me ); // Hello, 我是 KYLE
    speak( you ); // Hello, 我是 READER

  使用  this  能够隐式的传递对象的引用,让你的代码变得更加的优美,简洁,易于复用。

  随着你使用的模式越来越复杂,显式传递上下文对象会让代码变得越来越混乱,使用  this  能够很好的解决这个问题

1.2  误解

   在我们正式了解  this  的作用机制的时候,先让我们来消除一些对  this  常有的误解。

   太拘泥于  this  的字面意思会产生一个误解。有两种对  this  的误解,它们都是错误的

1.2.1  误解一:指向自身

   在英语字面上解释  this  的话,通常是指向函数本身,在函数作用域中使用它,目的也是指向自己(函数内部调用自己),那么在函数内部指向自己(执行自己)是在什么情况呢?

  一般是在递归的情况下,比如:阶乘函数,就需要不断的调用自己、或者在第一次调用时自己解绑的事件处理器。

 在JavaScript一些开发者很容易混淆存储状态的位置(存储状态=属性的值),因为JavaScript中有多种模式可以存储状态,有一部分人认为,函数是一个对象(在JavaScript中,函数看作对象),那么就可以在调用函数时存储状态 这是可行的,但是你要记住,存储状态不止这一个位置。

   接下来我们看看这个代码


        function foo(num) {
            console.log("foo"+num);

            this.count++;
        }
        foo.count=0;
        var i;
        for(i=0;i<10;i++){
            if(i>5){
                foo(i);     //6-7-8-9
            }
        }
        console.log("foo被调用:"+foo.count+"次");    //0
    

  console.log(...)输出了四条语句,证明foo(...)函数确实被调用了四次,但是foo(...)函数的计数器并没有发生计数,很明显,是foo(...)中this  对象的错误。

  执行  foo.count=0  时,确实是向函数对象  foo  添加了一个属性  count。但是函数内部代码  this.count依然是0,那就证明  this.count中的  this  并不是指向  foo  这个对象,属性相同但是对象不同。

     实际上如果深究的话,foo(...)中  this.conut  是创建了一个全局变量conut,它的值为NaN。

    为什么呢?还记得我们之前讲过的查找方式吧,this.count++  使用的是LHS查找,在当前作用域中查找不到count变量,于是它返回到上一层查找,这时,在上一层(词法作用域)中同样没有查找到,因为进行的是LHS查找,所以它会自动在全局作用域中创建一个变量count,它的值为NaN

-------------------------------------------这充分说明了  this  并不是指向函数本身

  要解决上面的问题有多种方法

  • 创建全局变量count,此方法没有用到  this  而是用到了词法作用域
  • 利用foo代替this,此方法同样没有用到  this  用到的是foo(...)的函数作用域
  • 利用call强制foo(...)中的  this  与foo进行绑定,在foo(i)------->>>foo.call(foo,i)   call正确用法是:this存在的函数.call(需要this绑定的函数,传入this存在的函数的参数)

1.2.2  误解二:它的作用域

  第二种常见的误区是将this指向它的作用域。这个问题涉及到了很多东西,在某些情况下,它是正确的,在某些情况下,它是错误的。

    this在任何情况下都不会指向函数的词法作用域,在JavaScript内部,作用域和对象类似,可见的标识符都是它的属性。但是作用域对象无法通过JavaScript代码访问,它存在于JavaScript引擎内部。

我们看看以下的代码,

  function foo() {
           var a=2;
           this.bar();
       }
       function bar() {
           console.log(this.a);
       }
       foo();

  这个代码中有很多个错误,这段代码似图跨过边界,使用 this 来隐式引用函数的词法作用域。但这是无法实现的

  首先,这段代码似图通过  this.bar()  来引用bar(...)函数。这个是不可能成功的,要调用bar(...)最好的方法是直接使用词法引用标识符bar(...),不要  this。

  此外这段代码还试图用  this  沟通foo()、bar()的词法域,从而让bar()能够访问foo()中的变量a,这是不可能实现的,

因为你无法使用this来引用词法作用域里面的东西。

1.3  this到底是什么?

    我们之前说过  this  是在运行时进行绑定的,就同动态作用域一样,它的上下文取决于函数调用的各种条件

    this的绑定和函数声明的位置没有任何的关系,只取决于函数调用的方式

 简单说下  this  。当一个函数被调用时,会创建一个活动记录(执行上下文)。这个记录会包含函数在哪被调用(调用栈),函数调用的方法,传入的参数等等。this就是记录其中的一个属性而已,会在函数执行时用到。

总结:this 机制是JavaScript中最为复杂以及最为重要的机制之一,它的用处非常广。

          要认识this机制首先要知道,this并不是指向函数自身,也不是指向函数的作用域。

          this  实际上是在函数被调用时绑定的,它指向什么完全取决于函数在哪被调用。

 

猜你喜欢

转载自blog.csdn.net/qq_41889956/article/details/83349383