歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

JavaScript中的this與函數講解

前言

JavaScript中沒有塊級作用域(es6以前),JavaScript中作用域分為函數作用域和全局作用域。並且,大家可以認為全局作用域其實就是Window函數的函數作用域,我們編寫的js代碼,都存放在Window函數內(這是個假設),也就是說JavaScript中只有函數作用域(前面假設做前提下)。

作用域是什麼

作用域是一個盒子,盒子內部的變量只能在當前盒子中使用,作用域盒子是可以嵌套的,內部盒子的變量對父級盒子是不可見的,因為盒子封閉了他們並且盒子不透明,但是盒子可以看到父級盒子內部定義的變量,因為內部這個盒子與父級的變量同處一個空間,他們是互相看得到的。就像css中的盒模型一樣。

以上這個圖分為3層作用域,全局作用域、foo函數作用域、bar函數作用域,我們可以清晰的看到三層作用域各自的范圍。

this是什麼

我們經常用到this,this是代表著什麼?this是代表著當前方法執行的環境上下文,那麼何為環境上下文,通俗的說,誰調用了函數,誰就是這個函數的環境上下文。例如:

function run () {
  console.log(this)
}

var o = {
  run: run
}

run()  //  window對象
o.run  //  o對象

在上述代碼中,run是一個函數,首先執行run()返回了window對象,為什麼呢?我們已經講過,var定義的變量會默認掛在到window對象中去,那麼function定義的函數呢,也會默認掛在到window對象中去,其實我們在執行一個函數如:run() 跟window.run()是一樣的,所以,window調用了run,因此函數的this是window。

注意:this的綁定是產生在函數調用時,並非在函數定義時

var o = {
  color: 'red',
  getColor: function () {
    console.log(this.color)
  }
}

var color =  'blue'

var getColor = o.getColor

o.getColor // red
getColor  // blue

上述代碼,我們將o.getColor方法賦值給了一個變量getColor,然後我們分別執行了兩個方法,缺得到了不同的結果,對於o.getColor並沒有什麼疑問,但是初學者可能認為getColor返回的也應該是red,但事實是blue,這就說明了,this是產生在函數調用時,因為在o.getColor賦值語句,就相當於一下語句

var getColor = function () {
  console.log(this.color)
}

o.getColor返回的是這個函數,並沒有綁定this,也就是說getColor其實是被賦值了一個函數,僅此而已,this是在調用時綁定的,所以得到了那樣的結果。

不要被模樣給蒙蔽

看下面代碼:

var o = {
  run : function () {
    setTimeout(function () {
      console.log(this)   
    }, 1000)
  }
}
o.run()  //  window對象

以上運行了o.run方法,隔一秒輸出this,輸出了window對象,初學者可能會疑問,o.run調用時明明是在o對象下啊,this應該是o對象啊!不錯,有這個疑問說明對this有一定了解了,但是上面案例是在setTimeout方法中的回調函數中輸出的this,此回調函數執行時,是以fn()方式執行的(涉及到異步回調),前面說過直接執行函數相當於window.fn,因此輸出了window對象。

拓展回調:

回調是進行異步操作常用的功能,上面示例中,我們可以假設為setTimeout是這樣定義的

function setTimeout (fun) {
  fun()
}


setTimeout (function () {
  console.log(this)
})

可以看出,fun是直接調用的,輸出的this必定是window對象。

暴力綁定this

有時候,我們需要強制某個函數中的this為某一對象,我們這時候需要暴力的綁定this,這裡,暴力的方法有3個: call、apply、bind。下面通過一個簡單的demo來看一下他們的用法。

var color = 'blue'

var o = {
  color: 'red',
  say: function (animal, beautiful) {
    console.log(this.color + '顏色的' + animal + (beautiful ? '好看': '不好看'))
  }
}

var say= o.say

say('貓', false)   // blue顏色的貓不好看
say.call(o, '貓', true)  // red顏色的貓好看
say.apply(o, ['貓', true])  // red顏色的貓好看
say.bind(o)('貓', true)  // red顏色的貓好看
o.say.call(null, '貓', false)  // blue顏色的貓不好看

我們這次將o函數加一個say方法,say方法接收兩個參數,第一個是動物,第二個是布爾值代表好不好看,我們還是將o.say賦值給變量say。

say('貓', false) 如期返回了blue顏色

say.call(o, '貓', true),say.apply(o, ['貓', true]),say.bind(o)('貓', true) 這三個都返回了red,可見我們都暴力的綁定了this為o對象。

call和apply的第一個參數都是接收要綁定的對象,也就是告訴引擎:'我要讓say方法在o對象的環境下運行,你給我幫頂一下',引擎回答:‘包在我身上’,然後我們就綁定成功了,接下來我們需要傳遞參數到方法中去,call方法是在第二個參數之後,按順序的傳遞參數 到方法中去,apply方法不同,apply方法是在第二個參數以數組的方式將參數傳遞到方法中去。而bind方法是es5中的方法,用途就在於綁定this指向然後返回已綁定好了的函數,因此,以上三個都輸出了red

同樣o.say綁定到null或者undefined,引擎會自動綁定到全局對象window下面。

進階:函數的執行過程

本著菜鳥的思路,努力講述一下簡單的函數執行背後的歷程,有錯誤請指正。

function fn (a, b) {
  var c = 2;
  function f () {...}
}

var o = {}

fn.call(o,1,'ok')

函數fn執行,會首先產生一個函數活動對象,這個活動對象對外是不可見的,該示例中,call方法先綁定了此次調用的this指向o,fn函數活動對象通過參數之間的賦值形成arguments對象,然後進行了函數內部的初始化變量,最後執行函數內部的賦值過程。過程如下:

function fn (a = 1, b = 'ok') {  
  var c
  var f
  //活動對象初始化完畢,進行函數體內的其他操作,這根變量提升和函數聲明提升有關
  c = 2
  f = function () {...}
}

總結

總而言之,函數是javascript中最主要的結構,this是javascript甚至每一門高級語言中都需要的動態綁定的指針。能力一般,水平有限,如有錯誤,輕噴輕罵。

Copyright © Linux教程網 All Rights Reserved