javascript 之this指針-11

 前言

在《javascript 之執行環境-08》文中說到,當JavaScript代碼執行一段可執行代碼時,會建立對應的執行上下文(execution context)。對於每一個執行上下文,都有三個重要屬性:javascript

  • 變量對象(Variable object,VO)
  • 做用域鏈(Scope chain)
  • this

  JavaScript中的this跟其餘語言有些不同,好比Java .net語言中的this是在代碼的執行階段是不可變的,而JavaScript的this是在調用階段進行綁定。也由於這一性質給了this很大的靈活性,即當函數在不一樣的調用方式下均可能會致使this的值不一樣;java

定義

  this 對象是在運行時基於函數的執行環境綁定的,跟函數的調用位置有關而不是聲明的位置;能夠理解爲this是在函數調用階段綁定,也就是執行上下文建立的階段進行賦值,保存在變量對象中;緩存

四種綁定規則

new 構造函數綁定,this指向新建立的對象

 1 function createPerson(){
 2        return new person();
 3     }
 4     function person() {
 5         this.name = "Joel";
 6         this.say=function(){
 7             console.log(p)
 8             console.log('hello' + this.name)
 9         }
10     }
11     var p = new person();
12     p.say();
13     console.log(p.name);//Joel
14     console.log('開始')
15     var t=createPerson();
16     t.say();

無論是直接new 仍是經過function createPerson 函數返回的對象,this 都是指向了新建立出來的對象;閉包

顯示綁定,this指向傳進去的對象

 1     //call() apply() bind() 顯示綁定
 2     window.color = "red";
 3     var Obj = { color: "blue" };
 4     function sayColor() {
 5         console.log(this.color);
 6     }
 7     sayColor();// red this window
 8     sayColor.call(this);//red
 9     sayColor.call(window);//red
10     sayColor.call(Obj);// blue 把this指針改成對象Obj
11     sayColor.apply(Obj);//blue 把this指針改成對象Obj
12     sayColor.bind(Obj)();//blue 把this指針改成對象Obj

若是你把null/undefined做爲this的綁定對象傳入call/apply/bind,這些值在調用時會被忽略,實際用的是默認的綁定規則;app

1   function foo() {
2       console.log(this.a)
3     }
4    var a=2;
5     foo.call(null);//2
6     foo.call(undefined);//2

隱士綁定

以對象的方法形式調用,this指向當前這個對象函數

 1    function foo(){
 2         console.log(this)//{a: 2, name: "Joel", foo: ƒ}
 3         console.log(this.a)//2
 4     }
 5     var obj={
 6         a:2,
 7         name:'Joel',
 8         foo:foo
 9     };
10     obj.foo();

這時this指向當前obj對象,可是若是換種寫法會形成this 丟失問題。oop

 1     function foo(){
 2         console.log(this)//{a: 2, name: "Joel", foo: ƒ}
 3         console.log(this.a)//2
 4     }
 5     var obj={
 6         a:2,
 7         name:'Joel',
 8         foo:foo
 9     };
10     obj.foo();
11     //this 丟失的問題
12     var t= obj.foo;
13     t(); //window undefined

變量t此時保存的是函數的引用跟obj已經沒有關係,因此此時this指向window。this

默認綁定 嚴格模式下this 綁定到undefined,不然綁定到全局對象 window

 1   function foo(){
 2         console.log(this)//window
 3         console.log(this.a)//Joel
 4     }
 5     var a='Joel';
 6     foo();
 7 
 8     //嚴格模式
 9     function fo(){
10         'use strict'  //嚴格模式
11         console.log(this)//undefined
12         console.log(this.b)//報錯 Cannot read property 'b' of undefined
13     }
14     var b='Joel';
15     fo();

以上是基本的this綁定規則,其中new、顯示綁定很容易判斷,其中比較容易錯的是容易把默認綁定誤認爲是隱士綁定 如匿名函數、閉包、函數當作參數等;spa

獨立調用:this 指向window.net

 1   var name='Joel',age=12;
 2     function say(){
 3         function say1(){
 4             console.log(this.name);//window
 5             function say2(){
 6                 name+='-l'
 7                 console.log(this.name);//window
 8             }
 9             say2()
10         }
11         say1();
12     }
13     say();
14 
15     //匿名函數
16     (function(){
17        console.log(this.name)
18     })()

function 當作參數傳遞其實跟獨立調用同樣的原理

 1 function foo() {
 2         console.log(this)//window
 3         console.log(this.a)//oops global
 4     }
 5     function doFoo(fn) {
 6         console.log(this);//window
 7         fn();//相似與 foo()
 8     }
 9     var obj = {
10         a: 2,
11         foo: foo
12     }
13     var a = 'oops global';
14     doFoo(obj.foo);

同理setTimeout 也是同樣

 1  var obj = {
 2         a: 2,
 3         foo: function() {
 4             console.log(this); 
 5         },
 6         foo2: function() {
 7             console.log(this);   //this 指向 obj 
 8             setTimeout(this.foo, 1000);   // this 指向 window 
 9         }
10     }
11     var a = 'oops global';
12     obj.foo2();

閉包中的this

 1     var name = "Joel";
 2     var obj = {
 3         name: "My object",
 4         getName: function() {
 5             // var that = this;   // 將getNameFunc()的this保存在that變量中
 6             return function() {
 7                 return this.name;
 8             };
 9         }
10     }
11     console.log(obj.getName()());   // "Joel"

這裏的雖然是對象的方法形式調用obj.getName(),在getName中的this是指向obj,可是返回的匿名函數中的this 爲何是window呢?

把最後的一句拆成兩個步驟執行:

1 var t=obj.getName();
2 t();

是否是有點像在獨立調用呢?若是須要訪問obj中的name,只須要把this對象緩存起來,在匿名函數中訪問便可,把var that=this;去掉註釋便可;

總結

  • this 是變量對象的一個屬性,是在調用時被綁定的,跟函數的調用位置有關而不是聲明的位置;
  • 找到調用位置,根據綁定規則來分析this 綁定;
  • 默認綁定嚴格模式下this 綁定到undefined,不然綁定到全局對象 window;

思考題

 1     var length = 5;
 2     var obj = {
 3         foo: function (fn) {
 4             console.log(this.length); // this => obj
 5             fn(); // this => window
 6             arguments[0](); // this => arguments
 7             var bar = arguments[0];
 8             bar(); // this => window
 9         },
10         length: 10
11     }
12     var fn = function () {
13         console.log(this.length);
14     }
15     obj.foo(fn);
16     //10, 5, 1, 5
相關文章
相關標籤/搜索