JavaScript this關鍵字的深入詳解
this關鍵字是JavaScript中最復雜的機制之一。它是一個很特別的關鍵字,被自動定義在所有函數的作用域中。對于那些沒有投入時間學習this機制的JavaScript開發者來說,this的綁定一直是一件非常令人困惑的事。
學習this的第一步是明白this既不指向函數自身也不指向函數的詞法作用域,你也許被這樣的解釋誤導過,但其實它們都是錯誤的。隨著函數使用場合的不同,this的值會發生變化。但總有一條原則就是 JS中的this代表的是當前行為執行的主體 ,在JS中主要研究的都是函數中的this,但并不是說只有在函數里才有this, this實際上是在函數被調用時發生的綁定,它指向什么完全取決于函數在哪里被調用 。如何的區分this呢?
三、this到底是誰這要分情況討論,常見有五種情況:
1、函數執行時首先看函數名前面是否有'.',有的話,'.'前面是誰,this就是誰;沒有的話this就是window
function fn(){ console.log(this);}var obj={fn:fn};fn();//this->windowobj.fn();//this->objfunction sum(){ fn();//this->window}sum();var oo={ sum:function(){ console.log(this);//this->oo fn();//this->window }};oo.sum();
2、自執行函數中的this永遠是window
(function(){ //this->window })(); ~function(){ //this->window }();
3、給元素的某一個事件綁定方法,當事件觸發的時候,執行對應的方法,方法中的this是當前的元素,除了IE6~8下使用attachEvent(IE一個著名的bug)
DOM零級事件綁定
oDiv.onclick=function(){ //this->oDiv };
DOM二級事件綁定
oDiv.addEventListener('click',function(){ //this->oDiv },false);
在IE6~8下使用attachEvent,默認的this就是指的window對象
oDiv.attachEvent('click',function(){ //this->window });
我們大多數時候,遇到事件綁定,如下面例子這種,對于IE6~8下使用attachEvent不必太較真
function fn(){ console.log(this);}document.getElementById('div1').onclick=fn;//fn中的this就是#divldocument.getElementById('div1').onclick=function(){console.log(this);//this->#div1fn();//this->window};
4、在構造函數模式中,類中(函數體中)出現的this.xxx=xxx中的this是當前類的一個實例
function CreateJsPerson(name,age){//瀏覽器默認創建的對象就是我們的實例p1->thisthis.name=name;//->p1.name=namethis.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->f1this.getX=function(){console.log(this.x);//this->需要看getX執行的時候才知道 }}var f1=new Fn;f1.getX();//->方法中的this是f1,所以f1.x=100var ss=f1.getX;ss();//->方法中的this是window ->undefined
5.call、apply和bind
我們先來看一個問題,想在下面的例子中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方法:
call方法的作用:
①首先我們讓原型上的call方法執行,在執行call方法的時候,我們讓fn方法中的this變為第一個參數值obj;然后再把fn這個函數執行。
②call還可以傳值,在嚴格模式下和非嚴格模式下,得到值不一樣。
//在非嚴格模式下var obj={name:'浪里行舟 '};function fn(num1,num2){console.log(num1+num2);console.log(this);}fn.call(100,200);//this->100 num1=200 num2=undefinedfn.call(obj,100,200);//this->obj num1=100 num2=200fn.call();//this->windowfn.call(null);//this->windowfn.call(undefined);//this->window
//嚴格模式下 fn.call();//在嚴格模式下this->undefinedfn.call(null);// 在嚴格模式 下this->nullfn.call(undefined);//在嚴格模式下this->undefined
**apply和call方法的作用是一模一樣的,都是用來改變方法的this關鍵字并且把方法
執行,而且在嚴格模式下和非嚴格模式下對于第一個參數是null/undefined這種情況的規
律也是一樣的。**
兩者唯一的區別:call在給fn傳遞參數的時候,是一個個的傳遞值的,而apply不是一個個傳,而是把要給fn傳遞的參數值統一的放在一個數組中進行操作。但是也相當子一個個的給fn的形參賦值。 總結一句話:call第二個參數開始接受一個參數列表,apply第二個參數開始接受一個參數數組
fn.call(obj,100,200);fn.apply(obj,[100,200]);
bind:這個方法在IE6~8下不兼容,和call/apply類似都是用來改變this關鍵字的 ,但是和這兩者有明顯區別:fn.call(obj,1,2);//->改變this和執行fn函數是一起都完成了
fn.bind(obj,1,2);//->只是改變了fn中的this為obj,并且給fn傳遞了兩個參數值1、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指向箭頭函數正如名稱所示那樣使用一個“箭頭”(=>)來定義函數的新語法,但它優于傳統的函數,主要體現兩點: 更簡短的函數并且不綁定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擴展閱讀
箭頭函數-廖雪峰
JS中的箭頭函數與this
this、apply、call、bind
總結到此這篇關于JavaScript this關鍵字深入詳解的文章就介紹到這了,更多相關JavaScript this關鍵字內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!
相關文章:
