理解JavaScript中的this關鍵詞

理解JavaScript中的this關鍵詞

this關鍵詞是JavaScript語言中一個很重要,同時也是一個很是複雜的機制,它同時也是一個很特殊的關鍵詞,它通常會被自動定義在函數的做用域中。很多頗有經驗的開發者也常常會被 this的指向搞暈,當開發者搞不清楚它的指向的時候,心裏的感覺實際上差很少是這個樣子: 二營長,你他孃的意大利炮呢
接下來咱們將會具體討論 this關鍵詞到底指向什麼。

this的綁定規則

首先明確一點,this的綁定和函數的聲明位置是沒有任何關係的,它只取決於函數的調用位置和調用方式。jquery

1. 默認綁定

首先咱們先看一下最多見的也是最簡單的函數調用,全局中的獨立的函數調用:編程

var a = 'hello';
function foo() {
  console.log(this.a);
}
foo();// "hello"

衆所周知,聲明在全局做用域中的變量,就是全局對象(window或者global)的一個同名的屬性,在本例中,this.a被解析成了全局變量a,而函數foo就是在直接在全局對象下、不帶有任何修飾地進行調用的,因此,this的默認綁定規則就是指向全局對象。可是,當咱們使用嚴格模式(strict)進行開發的時候,狀況發生了變化app

"use strict";
var a = 'hello';
function foo() {
  console.log(this.a);
}
foo();// undefined

在嚴格模式下,this被禁止綁定到全局對象中,因此在本例中,this指向了undefined函數

2.做爲對象方法的調用

看下面的代碼:this

var a ="world";
var foo = function () {
  console.log(this.a);
};
var obj = {
  a:"hello",
  b:foo
};
obj.b(); //"hello"

本例中,分別在全局對象中定義變量a,和在全局對象中定義屬性obj.a,運行結果是this被綁定到了被調用函數所在的對象中,而並不是是全局對象中的a,或者你能夠說,this指向了該函數的上級對象中。可是嚴格來講,不管是直接在obj對象中直接定義,仍是先在全局對象中定義再添加到obj中,foo函數都並不屬於obj對象,然而調用位置會使用obj的上下文來引用函數。簡單說,當函數引用有上下文對象時,隱式綁定規則會將函數調用中的this綁定到這個上下文對象中。spa

3.在構造函數及prototype裏的調用

var Foo = function () {
  this.a = 'hello';
}
Foo.prototype.bar = function () {
   console.log(this.a);
};
Foo.prototype.bar2 = function () {
   this.bar();
};
var foo = new Foo();
foo.bar2();//"hello"

本例與上例相似,this在「Foo類」(嚴格說Foo是應該是構造函數,但通常在開發過程當中認爲Foo是類,這樣有助於在面向對象編程中減小誤解)中綁定的是「類」裏的共有變量和共有方法。prototype

this隱式綁定丟失

上述的幾種應用場景仍是很是容易理解的,也是比較符合咱們對this字面意義的理解的,相信對JavaScript有過接觸和研究的童鞋很快就會掌握。下面將繼續介紹幾種讓人感受匪夷所思的this綁定丟失。
思考如下代碼:code

var a = "world";
function foo (){
   console.log(this.a);
}
var obj = {
  a:"hello",
  foo:foo
};
var bar = obj.foo; //函數別名
bar(); //"world"

代碼執行完成後,咱們看到this被綁定到了(或者說指向了)全局對象上,而並非和前幾種狀況下綁定到obj對象中,這是爲啥呢?
雖然barobj.foo的一個引用,可是實際上它引用的是foo函數自己,所以此時bar實際上是一個不帶任何修飾的函數調用,所以適用於默認綁定狀況。
下面再來看看回調函數中this的綁定狀況對象

function foo() {
  console.log(this.a);
}
function do(fnc) {
  fnc();
};
var obj = {
  a:2,
  foo:foo
};
var a = "world";
do(obj.foo);//"world"

參數傳遞其實就是一種隱式賦值,所以傳入函數時也會被隱式賦值,因此結果和上一個例子沒有區別,即使是將函數傳入語言內置的的函數(好比setTimeout())中,結果也是沒有區別的,this要麼被綁定到全局對象中,要麼綁定到undefined
除此以外,還有一種狀況也會修改this,在一些JavaScript庫中傳入回調函數,可能會強制改變this的綁定,例如在jquery中事件

$("#some-id").on('click',function () {
  console.log(this.id);//"some-id"
});

本例中的this就是被強制改變綁定到了觸發事件的DOM元素上。

顯式綁定

隱式綁定實際上就是在一個對象的內部包含一個指向函數的屬性,並經過這個屬性間接地引用屬性,從而把this間接地綁定到這個對象上。
咱們可使用callapply兩個函數進行顯式綁定。它們的第一個參數是一個對象,它們會把這個對象綁定到this,接着在調用函數時指定這個this

var a = "world";
function foo() {
  console.log(this.a);
}
var obj = {
  a:"hello"
};
foo.call(obj);//"hello"
foo() //"world"
單純的實現 this綁定的功能的話, callapply是同樣的,它們的區別體如今別的參數中。

思考下面的代碼:

function foo() {
  console.log(this.a);
}
var obj = {
  a:"hello"
};
function bar() {
  foo.call(obj);
}
bar(); //"hello"
setTimeout(bar,500);// "hello"
bar.call(window); //"hello",硬綁定的bar不能再修改它的this

咱們建立了函數bar,而且手動在它的內部調用foo.call(obj),所以強制把foothis綁定到obj,以後不管怎麼調用函數bar,它總會在obj上調用foo,不會丟失,這種綁定方式被稱爲硬綁定.

相關文章
相關標籤/搜索