深刻淺出JavaScript之this

JavaScript中的this比較靈活,根據在不一樣環境下,或者同一個函數在不一樣方式調用下,this都有多是不一樣的。可是有一個總的原則,那就是this指的是,調用函數的那個對象。html

下面是個人學習筆記,把它羅列成8種狀況。node

全局的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

再舉一個例子,看完就很是透徹了app

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爲何要用嚴格模式的一個緣由。this

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

做爲對象方法的函數的this 

this做爲對象方法來使用是比較常見的。es5

下面這個例子,咱們建立了一個對象字面量o,o裏面有個屬性f,它的值是一個函數對象,把函數做爲對象屬性的值這種方式咱們經常叫做對象的方法。做爲對象的方法調用的時候,這時候this指向對象ospa

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

總結

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

相關文章
相關標籤/搜索