首頁  >  文章  >  web前端  >  深入淺出JavaScript之this的詳解

深入淺出JavaScript之this的詳解

黄舟
黄舟原創
2017-03-03 15:34:51893瀏覽

JavaScript中的this比較靈活,根據在不同環境下,或是同一個函數在不同方式呼叫下,this都有可能是不同的。但是有一個總的原則,那就是this指的是,呼叫函數的那個物件。

系列目錄

  • 深入淺出JavaScript之閉包(Closure)

  • 深入淺出JavaScript之this

  • #深入淺出JavaScript之原型鍊和繼承

#下面是我的學習筆記,把它羅列成8種情況。

全域的this(瀏覽器)

全域作用域的this一般指向全域對象,在瀏覽器中這對象就是window,在node中這物件就是global。

console.log(this.document === document); // true (document === window.document)
console.log(this === window); // true 
this.a = 37;  //相当于创建了一个全局变量a
console.log(window.a); // 37

一般函數的this(瀏覽器)

一般的函數宣告或是函數表達式,直接呼叫函數的話,this依然指向全域對象,在瀏覽器中這物件就是window,在node中這物件就是global。

function f1(){  
  return this;  
} 
f1() === window; // true, global object

再舉一個例子,看完就非常透徹了

function test(){
 this.x = 1;
  alert(this.x);
}
test(); // 1

為了證明this就是全域對象,對程式碼做一些改變:

var x = 1;
function test(){
 alert(this.x);
}
test(); // 1

運行結果還是1。再變一下:

var x = 1;
function test(){
 this.x = 0;
}
test();
alert(x); //0

但是在嚴格模式下,一般函數呼叫的時候this指向undefined,這也是node為什麼要用嚴格模式的一個原因。

function f2(){  
  "use strict"; // see strict mode  
  return this; 
} 
f2() === undefined; // true

作為物件方法的函數的this

this作為物件方法來使用是比較常見的。

下面這個例子,我們創建了一個對象字面量o,o裡面有個屬性f,它的值是一個函數對象,把函數當作對象屬性的值這種方式我們常常叫作對象的方法。作為對象的方法呼叫的時候,這時候this指向對象o

var o = {  
   prop: 37,  
   f: function() {    
     return this.prop;    
  } 
};  

console.log(o.f()); // logs 37

我們不一定要定義成函數字面量這樣子的對象,像下面這種情況,我們只定義了一個對象o,如果直接呼叫independent()函數的話,this會指向window,但是我們透過賦值的方式,暫時建立一個屬性f,並指向函數物件的時候,我們還是拿到了37。

var o = {prop: 37}; 

function independent() {  
   return this.prop; 
} 

o.f = independent;  
console.log(o.f()); // logs 37

所以並不是看函數是怎麼創建的,而是只要將函數作為物件的方法去調用,this就會指向這個物件。

物件原型鏈上的this

下面這個範例中:我們先建立了一個物件o,裡面有一個屬性f,函數作為物件屬性的值,我們透過Object.create(o)創建了一個物件p,p是一個空對象,它的原型會指向o,然後使用p.a = 1; p.b = 4建立物件p上的屬性,那麼當我們呼叫原型上的方法時,this.a,this.b仍能取到物件p上的a和b。 這裡要注意的是p的原型才是o,我們呼叫p.f(),呼叫的是原型鏈o上的屬性f,原型鏈上的this可以拿到目前的物件p。

var o = {f:function(){ return this.a + this.b; }};
var p = Object.create(o); 
p.a = 1; 
p.b = 4; 
console.log(p.f()); // 5

get/set方法與this

get/set方法中的this一般會指向get/set方法所在物件裡面

function modulus(){   
   return Math.sqrt(this.re * this.re + this.im * this.im); 
} 
var o = { 
  re: 1, 
  im: -1, 
  get phase(){      
     return Math.atan2(this.im, this.re);    
  } 
}; 
Object.defineProperty(o, 'modulus', {       //临时动态给o对象创建modules属性
  get: modulus, enumerable:true, configurable:true}); 

console.log(o.phase, o.modulus); // logs -0.78 1.4142

建構子中的this

用new把MyClass當作建構函式呼叫的話,this會指向空的對象,而這個物件的原型會指向MyClass.prototype(可以看這篇文章對原型鏈的總結),但是呼叫的時候做了this.a = 37的賦值,所以最後this會作為返回值(沒寫return語句,或者return的是基本類型的話,會將this作為返回值),第二個例子return語句返回了對象,那麼就會將a = 38作為回傳值

function MyClass(){    
   this.a = 37; 
} 
var o = new MyClass();  
console.log(o.a); // 37 

function C2(){    
   this.a = 37;   
   return {a : 38};  
} 

o = new C2();  
console.log(o.a); // 38

call/apply方法與this

除了不同的呼叫方式外,函數物件有些方法能修改函數執行的this,例如call/ apply。

call和apply基本上沒差別,只不過call傳參的方式是平的,而apply是把一個陣列傳進去。如下面這個範例

什麼時候用call和apply呢?例如我們想要呼叫Object.prototype.toString,但是我們想指定某個this的時候,那我們就可以就用Object.prototype.toString.call(this)這樣子的方式來呼叫些無法直接呼叫的方法。如下面這個例子:

function add(c, d){  
   return this.a + this.b + c + d;  
} 
var o = {a:1, b:3}; 
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16     //第一个参数接收的是你想作为this的对象
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34 

function bar() {  
   console.log(Object.prototype.toString.call(this)); 
} 
bar.call(7); // "[object Number]"

bind方法與this

bind方法是es5開始提供的,所以ie9+才支援

function f(){  
   return this.a;  
} 

var g = f.bind({a : "test"});   //想把某个对象作为this的时候,就把它传进去,得到一个新对象g
console.log(g()); // test       //重复调用的时候,this已经指向bind参数。这对于我们绑定一次需要重复调用依然实现绑定的话,会比apply和call更加高效(看下面这个例子)

var o = {a : 37, f : f, g : g};  
console.log(o.f(), o.g()); // 37, test  
 //o.f()通过对象的属性调用,this指向对象o;比较特殊的是即使我们把新绑定的方法作为对象的属性调用,o.g()依然会按之前的绑定去走,所以答案是test不是g

總結

#做項目的時候才發現這些基礎概念有多麼的重要,如果不把它們逐一落實了,真的是一不小心就會掉進坑裡。後續我還會對原型鏈,作用域,繼承,鍊式調用,正則等知識進行總結,歡迎關注

 以上就是深入淺出JavaScript之this的詳解的內容,更多相關內容請關注PHP中文網(www.php.cn)!

#

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn