JavaScript中this之全面解析

JavaScript中this之全面解析, this一直都是一個談論的話題,其實我感覺this不是僅僅看一篇this的總結就能理解的東西;this是一個綜合的知識體現:它需要你對原型、作用域和引用的綜合理解,才能透徹理解這個多變的this。

為什麼要用this

首先看兩端代碼:

(1)about this

 function sayName(){
             return this.name.toUpperCase();
         }
         function intro(){
               var myself = "Hello,I am "+sayName.call(this); 
               console.log(myself);
         }
         var me = {
            name : "jack"
         }

         var you ={
            name : "rose"
         }

         sayName.call(me);
         sayName.call(you);

         intro.call(me); //Hello,I am JACK
         intro.call(you);//Hello,I am ROSE

(2)顯示傳遞上下文

function sayName(context){
             return context.name.toUpperCase();
         }
         function intro(context){
               var myself = "Hello,I am "+sayName(context); 
               console.log(myself);
         }
         var me = {
            name : "jack"
         }

         var you ={
            name : "rose"
         }

         sayName(me); 
         sayName(you);

         intro(me);  //Hello,I am JACK
         intro(you);  //Hello,I am ROSE

上面兩段代碼,可以看到一樣的效果;

通過this隱式”傳遞”一個對象的應用,看起來簡潔、容易復用; 顯式傳遞上下文,結構復雜的話,會使代碼變得越來越復雜,或許參數context就看著越來越混亂

this的誤解

誤解:this理解成指向函數自身

上代碼:

 function foo(num){
            console.log("foo: "+num);
            this.count++;
         }

         foo.count = 0;

         var i;
         for (var i = 0; i < 10; i++) {
                if (i>5) {
                  foo(i);
            }
        }
        console.log(foo.count);
        console.log("window count:"+this.count);

結果

    foo: 6
    foo: 7
    foo: 8
    foo: 9
    0
    window count:NaN

結果可以看出:

foo.count依然是0,如果this指向自身的話,怎麼不是4呢!! 如果是this指向自身,this.count又怎麼會是NaN呢
原因:這裡的this.count其實是在window(也就是global)裡面創建瞭一個count,並不是foo.count,然而初始值是Undefined,所以,在this.count++的過程返回瞭NaN;

this的綁定規則

通過上面我們可以看出,函數中的this到底指的誰,其實完全取決於函數調用的位置;

默認綁定

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

這裡foo()是window在調用(window.foo());所以這裡的this也就是window,this.a自然是全局的2瞭;當然,嚴格模式下會報錯的;

隱式綁定

var a = 3;
         function test(){
             console.log(this.a);
         }
         var obj = {
             a:2,
             foo:test
         }
         obj.foo();//2

結果:obj.foo()是2,分析可以看出,test()函數是不屬於obj對象,跟obj沒毛線關系,就像聲明瞭一個函數一樣;在obj對象裡面,有一個屬性foo引用,指向test這個函數;所以obj調用foo()的時候,this指向瞭obj對象,this.a就是2嘍;

再看一個隱式的例子

         var a = "Hello World";
         function test(){
             console.log(this.a);
         }
         var obj = {
             a:2,
             foo:test
         }
         var bar = obj.foo;
         bar();  //Hello World

這裡輸出的是全局的Hello World;這也更說明obj和test沒毛線關系,obj.foo是函數名,一個引用類型,賦值給bar,這時候bar也指向test;所以相當於全局調用test()嘍;

在看一個,傳入回調函數會怎樣

        var a = "Hello World";
         function test(){
             console.log(this.a);
         }
         function doFoo(fn){
            fn();    //調用位置
         }
         var obj = {
             a:2,
             foo:test
         }
         doFoo(obj.foo);  //Hello World

這裡把obj.foo,(函數的引用/名字)作為doFoo函數的參數,在裡面才調用;這時候同上一個例子一樣,也相當於執行test();

再再看一個

     var name = "Hello";
        var obj = {
          name: 'World',
          foo: function() {
            console.log(this.name);
          },
          foo2: function() {
            console.log(this.name);
            setTimeout(this.foo, 1000);
          }
        }
      obj.foo2();

結果先輸出World,1S後輸出Hello; 第一次是 foo2 中直接輸出”World”,指向 obj 這個對象

setTimeout 也隻是一個函數而已,把 this.foo 當作一個參數傳給 setTimeout 這個函數,就像上面的fn,和obj無關瞭,所以輸出Hello

顯示綁定

  function foo(){
            console.log(this.a);
         }
         var obj ={
            a:2
         }
         foo.call(obj);//2

強制把它的this綁定到obj上;

new綁定

使用new初始化構造函數,在這個過程一定要透徹的知道做瞭那些事!!!

使用new操作符經歷4個步驟:

創建一個新對象; 將構造函數的作用域給新對象(因此this就指向瞭這個新對象); 執行構造函數中的代碼(為這個新對象添加屬性) 返回新對象

看個面試題或許就明白瞭

function MyObj(){
            this.p.pid++;
         }
         MyObj.prototype.p = {"pid":0};
         MyObj.prototype.getNum = function(num){
             return this.p.pid + num;
         }

         var _obj1 = new MyObj();
         var _obj2 = new MyObj();
         console.log(_obj1.getNum(1)+_obj2.getNum(2)); //7

結果是7,分析:在new MyObj()過程都會執行this.p.pid++,根據原型鏈,自己找不到去原型裡面找,這裡的pid是引用類型所以每次都會++,this.p.pid++最終得到2,同時this的作用域分別是_obj1和_obj2;

當你好好去理解面向對象裡面的東西,這些就迎刃而解瞭,當然還有箭頭函數指定this,這個我還沒有深入研究;以後會補上;

發佈留言