記得剛開始,我理解 this
的時候 也是雲裏霧裏的,哈哈,但願經過這篇文章,對你有幫助吧。javascript
關於 this
最多的說法,就是:誰調用它,this就指向誰。這話呢,不能說它錯了,只能說它講的不嚴謹,爲何呢?咱們先來了解下 this
的幾種綁定規則。java
默認綁定 發生在全局環境 。數組
全局環境中,this
默認綁定到 window
。(嚴格模式下同樣)app
console.log(this === window); //true "use strict"; //使用嚴格模式執行代碼 var a = 666; console.log(this.a) //666
隱式綁定 發生在 方法調用(執行)。函數
什麼是方法呢?一般把 對象的屬性值 是函數的,稱爲方法。this
var obj = { fun: function(){} } //這裏obj 對象裏 的fun 屬性值是個函數, 就把 這個fun 屬性稱爲方法了。
一般,方法調用時,this
隱式綁定到其 所屬的對象 上。prototype
var a = "window"; var obj = { a: "obj", fun: function(){ console.log(this.a); } } obj.fun(); //"obj"
來個難一點的:code
var obj1 = { a: "obj1", obj2: { a: "obj2", fun: function(){ console.log(this.a); } } } obj1.obj2.fun(); //"obj2"
這裏的答案 跟你想的同樣嗎? 出現這個答案的關鍵 就是於,fun
函數 在做爲對象方法執行時, this
綁定的是它的 所屬對象,也就是 obj2
。 而非外層 的 obj1
。對象
在判斷是不是隱式綁定的時候,最容易出問題的地方就是發生在 隱式綁定丟失。ip
隱式丟失是指被隱式綁定的函數丟失綁定對象,從而綁定到window。這種狀況容易出錯卻又常見。(嚴格模式下,綁定到undefined)
隱式綁定丟失 通常 發生在 函數獨立調用時。
啥是獨立調用呢?就是一個簡單的函數執行. 函數名的前面沒有任何引導內容。
function ff(){}; ff(); //獨立調用
當函數獨立調用(執行)時,this
就會隱式綁定丟失,而綁定到 window
對象上。(非嚴格模式下)
function fun(){ console.log(this === window); } fun(); //true
那麼嚴格模式下呢?是指向 undefined
的。
function fun(){ "use strict"; //使用嚴格模式執行代碼 console.log(this); } fun(); //undefined
考考你:
var a = "window"; var obj = { a: "obj", fun1: function(){ console.log(this.a); } } var fun2 = obj.fun; fun2(); //"window"
判斷是否隱式綁定丟失的關鍵就在於, 判斷函數 是不是哪一種調用。
上面的例子,關鍵點就在 最後兩行代碼。
先看其中的第一行:
var fun2 = obj.fun;
這裏把 obj 的fun 方法 賦值給了 一個變量 fun2,這裏的 fun 並無做爲對象的方法來執行,由於,fun 方法這裏沒有執行。
其後:
fun2();
再執行 fun2,它保存着 fun1 方法,這時候執行 fun2(等於fun1) ,可是,它是獨立調用。由於,沒有做爲對象的方法來調用。因此 this 就被指向 window了。
那麼怎麼解決隱式丟失問題呢?
var a = "window"; var obj = { a: "obj", fun: function(){ return function(){ console.log(this.a); } } } obj.fun()(); //"window" //這裏咱們想要的是 obj裏 a 的值,但是,隱式綁定丟失致使獲取到了 window 裏 a 的值。
能夠基礎好的已經知道答案了:
var a = "window"; var obj = { a: "obj", fun: function(){ var that = this; return function(){ console.log(that.a); } } } obj.fun()(); //"obj"
由於 fun
是做爲方法調用的,因此 this
綁定到 obj
對象上,所以,咱們就能夠先用一個變量 that
來保存 this
,而後 在內部的 匿名函數中 使用 that
就能夠了。它保存着 上面 this
的綁定對象。
突然靈機一動,想出個題目,看人家都玩出題,我也試試,哈哈:
var a = "window"; function fun(){ var a = "fun"; return (function(){ return this.a; })() } fun() //「你猜」
這道題中 有當即執行函數、this問題,哈哈,乍一看挺噁心,其實看完上面的,應該能夠看出來的。
經過call()、apply()、bind()方法把 this
綁定到對象上,叫作顯式綁定。對於被調用的函數來講,叫作間接調用。
var a = "window" var obj = { a:"obj", } function fun(){ console.log(this.a); } fun.call(obj); //"obj";
這裏使用了 call 方法,把fun 中的 this 綁定到 obj 對象上。
javascript內置的一些函數,具備顯式綁定的功能,如數組的5個迭代方法:map()、forEach()、filter()、some()、every(),以及建立對象的 Object.create() 函數(後面原型鏈中會細說),均可以手動綁定this。
你們能夠去看下API文檔,數組的這幾個函數的最後一個參數,就是指定函數內 this 的綁定,若是不指定,則是window
,嚴格模式下是undefined
。
var a = [1,2,3]; a.forEach(function(){ console.log(this) },a); //綁定 this 爲 a 這個數組 //(3) [1, 2, 3] //(3) [1, 2, 3] //(3) [1, 2, 3]
若是 使用 new
來建立對象,由於 後面跟着的是構造函數,因此稱它爲構造器調用。對於this
綁定來講,稱爲new
綁定。
想知道 構造器調用 中 this
的綁定,就要知道 new
到底作了啥了。
先來個 new 的實現。看不懂沒關係,在後面原型鏈那篇,還會說的。
function New(proto){ //proto 爲傳進來的構造函數 var obj = {}; obj.__proto__ = proto.prototype; proto.apply(obj, Array.prototype.slice.call(argument,1)); //你這要看懂這步就行。這裏把構造函數裏的 this 綁定到了 新的obj 對象上,最後 返回了該新對象,做爲實例對象。 return obj; }
因此在使用 new 來建立實例對象時,new 內部把 構造函數的 this 綁定到 返回的新對象 上了。
function Person(name){ this.name = name; } var c = new Person("zdx"); c.name;
總結: this的四種綁定規則:隱式綁定、隱式綁定丟失、顯式綁定和new綁定,分別對應函數的四種調用方式:方法調用、獨立調用、間接調用和構造器調用。
一、關於this綁定 的優先級問題。
簡單提一下吧:
new 綁定 > 顯示綁定 > 隱式綁定 > 默認綁定 。
二、ES6 中,箭頭函數的 this 綁定。
箭頭函數內的 this 綁定的 是所屬的環境(函數或者對象), 它是固定不變的。
先看下上面的這個例子
var a = "window"; var obj = { a: "obj", fun: function(){ return function(){ console.log(this.a); } } } obj.fun()(); //"window"
上面咱們使用 一個變量來保存 this 的綁定,下面咱們來用 箭頭函數解決問題
var a = "window"; var obj = { a: "obj", fun: function(){ return () => { console.log(this.a); } } } obj.fun()(); //"obj"
實際上,箭頭函數內部是沒有this
的,因此,它不能使用 new
構造器調用,call
等顯示綁定。因此它內部就是使用了一個變量來保存 箭頭函數 所屬環境的(函數或者對象) this
。
就至關於:
var a = "window"; var obj = { a: "obj", fun: function(){ that = this; return function(){ console.log(that.a); } } } obj.fun()(); //"obj"
考考你:
var a = "window"; var obj = { a: "obj", fun1: function(){ return () => { console.log(this.a); } } } var fun2 = obj.fun1; fun2()();