this關鍵字是JavaScript中最複雜的機制之一。它是一個很特別的關鍵字,被自動定義在全部函數的做用域中。對於那些沒有投入時間學習this機制的JavaScript開發者來講,this的指向一直是一件很是使人困惑的事。javascript
學習this的第一步是明白this既不指向函數自身也不指向函數的詞法做用域,你也許被這樣的解釋誤導過,但其實它們都是錯誤的。隨着函數使用場合的不一樣,this的值會發生變化。但總有一條原則就是JS中的this表明的是當前行爲執行的主體,在JS中主要研究的都是函數中的this,但並非說只有在函數裏纔有this,this其實是在函數被調用時發生的綁定,它指向什麼徹底取決於函數在哪裏被調用。如何的區分this呢?java
這要分狀況討論,常見有五種狀況:git
function fn(){ console.log(this); } var obj={fn:fn}; fn();//this->window obj.fn();//this->obj function sum(){ fn();//this->window } sum(); var oo={ sum:function(){ console.log(this);//this->oo fn();//this->window } }; oo.sum();
(function(){ //this->window })(); ~function(){ //this->window }();
oDiv.onclick=function(){ //this->oDiv };
oDiv.addEventListener("click",function(){ //this->oDiv },false);
oDiv.attachEvent("click",function(){ //this->window });
咱們大多數時候,遇到事件綁定,以下面例子這種,對於IE6~8下使用attachEvent沒必要太較真github
function fn(){ console.log(this); } document.getElementById("div1").onclick=fn;//fn中的this就是#divl document.getElementById("div1").onclick=function(){ console.log(this);//this->#div1 fn();//this->window };
function CreateJsPerson(name,age){ //瀏覽器默認建立的對象就是咱們的實例p1->this this.name=name;//->p1.name=name this.age=age; this.writeJs=function(){ console.log("my name is"+this.name +",i can write Js"); }; //瀏覽器再把建立的實例默認的進行返回 } var p1=new CreateJsPerson("尹華芝",48);
必需要注意一點:類中某一個屬性值(方法),方法中的this須要看方法執行的時候,前面是否有".",才能知道this是誰。你們不妨看下接下來的這個例子,就可明白是啥意思。數組
function Fn(){ this.x=100;//this->f1 this.getX=function(){ console.log(this.x);//this->須要看getX執行的時候才知道 } } var f1=new Fn; f1.getX();//->方法中的this是f1,因此f1.x=100 var ss=f1.getX; ss();//->方法中的this是window ->undefined
咱們先來看一個問題,想在下面的例子中this綁定obj,怎麼實現?瀏覽器
var obj={name:"浪裏行舟"}; function fn(){ console.log(this);//this=>window } fn(); obj.fn();//->Uncaught TypeError:obj.fn is not a function
若是直接綁定obj.fn(),程序就會報錯。這裏咱們應該用fn.call(obj)就能夠實現this綁定obj,接下來咱們詳細介紹下call方法:app
①首先咱們讓原型上的call方法執行,在執行call方法的時候,咱們讓fn方法中的this變爲第一個參數值obj;而後再把fn這個函數執行。函數
②call還能夠傳值,在嚴格模式下和非嚴格模式下,獲得值不同。post
//在非嚴格模式下 var obj={name:"浪裏行舟 "}; function fn(num1,num2){ console.log(num1+num2); console.log(this); } fn.call(100,200);//this->100 num1=200 num2=undefined fn.call(obj,100,200);//this->obj num1=100 num2=200 fn.call();//this->window fn.call(null);//this->window fn.call(undefined);//this->window
//嚴格模式下 fn.call();//在嚴格模式下this->undefined fn.call(null);// 在嚴格模式 下this->null fn.call(undefined);//在嚴格模式下this->undefined
執行,並且在嚴格模式下和非嚴格模式下對於第一個參數是null/undefined這種狀況的規
律也是同樣的。**學習
二者惟一的區別:call在給fn傳遞參數的時候,是一個個的傳遞值的,而apply不是一個個傳,而是把要給fn傳遞的參數值統一的放在一個數組中進行操做。可是也至關子一個個的給fn的形參賦值。總結一句話:call第二個參數開始接受一個參數列表,apply第二個參數開始接受一個參數數組
fn.call(obj,100,200); fn.apply(obj,[100,200]);
fn.call(obj,1,2);//->改變this和執行fn函數是一塊兒都完成了 fn.bind(obj,1,2);//->只是改變了fn中的this爲obj,而且給fn傳遞了兩個參數值一、2, 可是此時並無把fn這個函數執行 var tempFn=fn.bind(obj,1,2); tempFn(); //這樣才把fn這個函數執行
bind體現了預處理思想:事先把fn的this改變爲咱們想要的結果,而且把對應的參數值也準備好,之後要用到了,直接的執行便可。
call和apply直接執行函數,而bind須要再一次調用。
var a ={ name : "Cherry", fn : function (a,b) { console.log( a + b) } } var b = a.fn; b.bind(a,1,2)
上述代碼沒有執行,bind返回改變了上下文的一個函數,咱們必需要手動去調用:
b.bind(a,1,2)() //3
必需要聲明一點:遇到第五種狀況(call apply和bind),前面四種所有讓步。
箭頭函數正如名稱所示那樣使用一個「箭頭」(=>)來定義函數的新語法,但它優於傳統的函數,主要體現兩點:更簡短的函數而且不綁定this。
var obj = { birth: 1990, getAge: function () { var b = this.birth; // 1990 var fn = function () { return new Date().getFullYear() - this.birth; // this指向window或undefined }; return fn(); } };
如今,箭頭函數徹底修復了this的指向,箭頭函數沒有本身的this,箭頭函數的this不是調用的時候決定的,而是在定義的時候處在的對象就是它的this。
換句話說,箭頭函數的this看外層的是否有函數,若是有,外層函數的this就是內部箭頭函數的this,若是沒有,則this是window。
<button id="btn1">測試箭頭函數this_1</button> <button id="btn2">測試箭頭函數this_2</button> <script type="text/javascript"> let btn1 = document.getElementById('btn1'); let obj = { name: 'kobe', age: 39, getName: function () { btn1.onclick = () => { console.log(this);//obj }; } }; obj.getName(); </script>
上例中,因爲箭頭函數不會建立本身的this,它只會從本身的做用域鏈的上一層繼承this。其實能夠簡化爲以下代碼:
let btn1 = document.getElementById('btn1'); let obj = { name: 'kobe', age: 39, getName: function () { console.log(this) } }; obj.getName();
那假如上一層並不存在函數,this指向又是誰?
<button id="btn1">測試箭頭函數this_1</button> <button id="btn2">測試箭頭函數this_2</button> <script type="text/javascript"> let btn2 = document.getElementById('btn2'); let obj = { name: 'kobe', age: 39, getName: () => { btn2.onclick = () => { console.log(this);//window }; } }; obj.getName(); </script>
上例中,雖然存在兩個箭頭函數,其實this取決於最外層的箭頭函數,因爲obj是個對象而非函數,因此this指向爲Window對象
因爲this在箭頭函數中已經按照詞法做用域綁定了,因此,用call()或者apply()調用箭頭函數時,沒法對this進行綁定,即傳入的第一個參數被忽略:
var obj = { birth: 1990, getAge: function (year) { var b = this.birth; // 1990 var fn = (y) => y - this.birth; // this.birth還是1990 return fn.call({birth:2000}, year); } }; obj.getAge(2018); // 28
文章於2018.9.25從新修改,若是文章對你有些許幫助,歡迎在個人GitHub博客點贊和關注,感激涕零!