this簡單而又神祕,使用場景多變而又複雜,這造就了它成爲了初級javascript開發人員不肯接觸的東西,高級javascript都想探究的東西。文本亦是對this的致敬。javascript
this的原理java
首先,從函數的調用開始node
函數的調用通常有三種狀況(ES5中)數組
1.foo(p1,p2);
2.obj.foo(p1,p2);
3.foo().call(context,p1,p2);//或者apply
一般狀況下,大部分童鞋經常使用的都是前兩種方式,只有在不得不綁定context的狀況下才會使用第三種。其中,第三種方式,纔是函數調用的正確方式。瀏覽器
foo().call(context,p1,p2);
之因此,咱們在使用前兩種方式也能正常的運行,這其實就是一種語法糖而已,在js運行機制中,會把前兩種調方式,當作下面的方式進行處理閉包
foo(p1, p2) 等價於 foo.call(undefined, p1, p2) obj.foo(p1, p2) 等價於 obj.foo.call(obj, p1, p2)
所以,若是咱們之後再調用函數時,能準確的找到context,就能夠惟一的確認this的值app
3.1 全局環境使用函數
不管是否在嚴格模式下,在全局執行環境中(在任何函數體外部)this 都指向全局對象。
// 在瀏覽器中, window 對象同時也是全局對象:
console.log(this === window); // true
var a=1;
console.log(this.a) //1
console.log(window.a) //1
3.2 普通函數中調用this
在非嚴格模式下,且this的值沒有被調用函數設置,this 的值默認指向全局對象。
funnction func(){ console.log(this) } //瀏覽器環境中 func() // window //node環境中 func() // global
在嚴格模式下,this的值若是不設置,就是是undefined
funnction func(){ "use strict" console.log(this) } func() === undefined; // true
3.3 做爲對象中的方法調用prototype
var obj={
name:'我是對象obj', ofun:function(){ console.log(this.name) } } var name='我是全局name'; var func= obj.ofun; obj.ofun(); //我是對象obj func(); //我是全局name
這裏有兩點須要注意:
1.函數做爲對象的方法進行調用時,this指向當前的對象。 2.對象中的函數方法名存儲的僅是一個函數的地址(地址中會有該函數的屬性:[[value]、[[writable]]、[[enumerable]]、[[congifable]]) 當把一個對象的方法賦值給另外一個變量(例如:func)時,引擎會將函數單獨保存在內存中,而後再將函數的地址賦值給foo屬性的value屬性。
3.4 構造函數中使用
this在構造函數中使用,值爲實例化的對象
function Fun(name){ this.name=name; this.speak=function(){ console.log(this.name); } } var fun1=new Fun('jack'); fun1.speak(); var fun2=new Fun('rose'); fun2.speak();//rose
3.5 bind/call/apply
this再被bind/call/apply強制綁定上下文執行環境時,屬於硬綁定。
function foo(somerthing){ console.log(this.a); } var obj={ a:2 } var bar = function(){ foo.call(obj); }; bar(); //2 //硬綁定的bar不可能再修改它的this bar.call(window);//2 }
硬綁定的典型應用場景就是建立一個包裹函數,傳入全部的參數並返回接收到的全部值;
function foo(somerthing){ console.log(this.a,something); return this.a +something; } var obj={ a:2 } var bar = function(){ return foo.apply(obj,arguments); }; var b=bar(3);// 2 3 console.log(b);//5
因爲硬綁定是一種很是經常使用的模式,因此在ES5中提供了內置的方法Function.prototype.bind,它的用法以下;
function foo(somerthing){ console.log(this.a,something); return this.a +something; } var obj={ a:2 } var bar=foo.bind(obj); var b=bar(3);// 2 3 console.log(b);//5
call,apply,bind方法的區別:
a:第一個參數都是要綁定的上下文執行環境,即this的值。
b:均可在函數調用時傳遞參數。call,bind方法須要直接傳入,而apply方法須要以數組的形式傳入。
c:call,apply方法是在調用以後當即執行函數,而bind方法沒有當即執行,須要將函數再執行一遍。有點閉包的味道。
d:改變this對象的指向問題不只有call,apply,bind方法,也可使用that/self等變量來固定this的指向。
3.6 回調函數中調用
在回調函數中通常有兩種狀況: 1. 回調函數爲匿名函數時,回調函數的this會指向window,須要對回調函數bind(this)。 2. 回調函數爲箭頭函數時,回調函數的this會指向他的直接上層。
var obj = { name: "test for call-func", ofun: function(){ //undefined 丟失 setTimeout(function(){ console.log(this.name) }, 100); //work well 硬綁定 setTimeout(function(){ console.log(this.name) }.bind(this), 100);//test for call-func // arrow function test for call-func setTimeout( () => console.log(this.id), 100) } }; obj.ofun();
3.7 自執行函數
自執行或者當即執行函數中的this指向Window
首先自執行函數的幾種調用方式
//常見的有兩種: (function(){})() (function(){}())//w3c推薦方式 //只有表達式才能被執行符號()執行 +function test(){}();//+ - ! =等運算符可使前面的函數成爲表達式,加上後面()可當即執行。 //函數聲明沒法當即執行一個函數,可是函數表達式能夠 var a = function(){}();//這種是函數表達式,後面有()能夠當即執行。成爲一個當即執行函數。
var obj = { number: 3, xxx: (function () { console.log(this + '--')//當即執行函數中的this指向window,由於當即執行函數是window調用的 console.log(this.number + '~~~')//刪除上面一行,此處this.number返回undefined return function () { console.log(this.number + '】') this.number += 7; console.log(this + '+') } })() } obj.xxx() //至關於obj.xxx.call(),因此此處的this指向obj console.log(obj.number) //10