Javascript基礎之-this

this應該算是前期比較容易混淆的一個關鍵字了,在這裏,我就打算按照個人理解來講一下es6

首先呢,this的值是跟運行時被調用的位置相關的,而不是詞法做用域。windows

也就是說,他的綁定的值極可能是動態的,不一樣的調用位置,極可能值就不同,好比說:api

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

而後你會驚訝的發現,foo()經過不一樣的形式調用,他最終的值是不同的,我們這一節主要就來說一講他們裏面的這些綁定的規則瀏覽器

 

默認綁定app

 

獨立的函數調用,它的做用域是做用在全局的,這種綁定規則最多見而且是優先級最低的一種綁定規則。來一個例子函數

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

在瀏覽器中執行,會發現,若是foo()做爲一個函數單獨調用,那麼this指向的就是全局對象windows。this

 

可是呢,也有一種例外,若是函數使用的嚴格模式的話,那麼全局對象講沒法使用默認綁定,this將會綁定到undefined,仍是剛纔的例子:prototype

function foo() {
  "use strict"
  console.log(this.a);
  console.log(this === window);
}
var a = 2;
foo();  //Uncaught TypeError: Cannot read property 'a' of undefined

能夠看到,foo直接拋出一個error,說this是undefined,取不到a,若是用到嚴格模式的時候,能夠稍微注意下這個區別對象

 

我們剛剛的例子,說的是函數內用嚴格模式的話,講沒法使用默認綁定,那麼若是是在函數調用位置用嚴格模式,會受影響嗎排序

function foo() {
  console.log(this.a);      //2
  console.log(this === window);   //true
}
var a = 2;
(function () {
  "use strict"
  foo();
})();

很明顯,能夠看出,是不受影響的,還有一個問題,若是把var定義變成let會怎麼樣呢

function foo() {
  console.log(this.a);      //undefined
  console.log(this === window);   //true
}
let a = 2;
(function () {
  "use strict"
  foo();
})();

也就是說,let定義並不會加到全局變量中,這個和var的區別須要稍微留意一下

 

隱式綁定

 

這一條規則考慮的是是否有上下文對象,舉個例子

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

在這種狀況下,foo會使用obj的上下文對象來引用函數,此時的this指向的就是obj。

 

那麼若是是嵌套對象這種的,會怎麼處理呢?

function foo() {
  console.log(this.a);   // 1
  console.log(this === obj) //  true
  console.log(this === obj1) // fale
}
var obj = {
  a: 1,
  foo,
};
var obj1 = {
  a: 2,
  obj,
}
obj1.obj.foo();

能夠看到,實際上此時foo中的this指向了離他最近的對象obj,也就是說,對象屬性引用鏈的最後一層會影響它的調用位置

 

還有一種狀況。

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

這種狀況下,雖然f是obj.foo的引用,可是最後依然用的是默認綁定,而不是隱式綁定,因此說呢,我們能夠這麼理解,雖然他是對象中函數的引用,可是在執行的時候,調用的方式確是函數調用的方式,不是對象.屬性()的方式,這塊在使用的時候須要注意一下

 

顯式綁定

很簡單,顯式綁定就是手動直接改變其this指向的一種方式,這個主要是用幾種api來實現的

function foo() {
  console.log(this.a);
}
var obj = {
  a: 2,
};
foo.call(obj);
foo.apply(obj);
var f = foo.bind(obj);
f();

這個就是顯式綁定的常見的三種形式

 

這三種形式的話,call和apply差很少,就不展開了,詳情看文檔就行,我們仔細看一下bind函數,來在看一個例子

function foo(num1, num2) {
  console.log(num1 + num2);  //5
  console.log(this === obj);    // true
}
var obj = {
  a: 1,
};
var f = foo.bind(obj, 2);
f(3);

這個能夠看出,bind的用法和call以及apply仍是有一些差異的。

 

注意,若是顯式綁定傳入的是null或者是undefined的話,將會使用默認綁定規則,舉個例子

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

 

new綁定

這個主要是new 函數()的綁定形式的,這個的this指向的是函數構造出的那個對象,new的時候,它的執行過程能夠大概執行如下幾步

  1. 建立一個全新的對象

  2. 這個新對象會被執行prototype鏈接

  3. 新對象綁定到函數調用的this

  4. 若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象

 

注意最後一句話,若是函數沒有返回對象,那麼就會返回函數調用的新對象,若是返回了一個對象呢,那麼結果就是直接返回這個對象

var abc = {
  a: 10
}
function foo() {
  return abc;
}
foo.prototype = {
  b: 20
};
var a = new foo();
console.log(a === abc);   // true
console.log(a.b);         // undefined
function bar() {
  this.a = 10;
}
bar.prototype = {
  b: 20
};
var b = new bar();
console.log(b.a);   //  10
console.log(b.b);   // 20

 

以上四種常見的this綁定方式已經介紹的差很少了,那麼問題來了,誰的優先級更高呢

 

很明顯,默認綁定的優先級最低,就不展開說了

 

顯式綁定與隱式綁定哪一個優先級高呢,舉個例子

function foo() {
  console.log( this. a );
}
var obj1 = {
  a: 2,
  foo: foo
};
var obj2 = {
  a: 3,
  foo: foo
};
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2

很明顯,顯式綁定優先級要高於隱式綁定

 

那麼new綁定和顯式綁定誰更高呢,來再看個例子

function foo(something) {
  this.a = something;
}
var obj1 = {};
var bar = foo.bind( obj1 );
bar( 2 );
console.log( obj1.a ); // 2
var baz = new bar(3);
console.log( obj1.a ); // 2
console.log( baz.a ); // 3

很明顯,new的優先級會更高

 

因此,這樣的話,優先級排序就出來了,分別爲

new綁定  > 顯式綁定 > 隱式綁定 > 默認綁定

 

以上就是你們經常使用到的幾種,不過es6出現了一種箭頭函數,箭頭函數理論上不能應用上面四種的任意一種,它的this是根據代碼的詞法做用域來走的,好比說

function foo() {
  return (a) => {
    console.log( this.a );
  };
}
var obj1 = {
  a: 2
};
var obj2 = {
  a: 3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2

結果已經很明顯了,

 

好了,以上內容就講完了,經過個人一些講解,是否是有一點理解了呢,若是有任何的不一樣意見,歡迎討論哦

 

參考書籍《你不知道的Javascript》上卷

本文轉載自 http://www.lht.ren/article/4

相關文章
相關標籤/搜索