從this指向開始談談JavaScript函數的一些內部實現

###** arguments** 廢話很少說,直接先看一段最簡單的函數:數組

function getSum(a,b,c,d){
	return a+b+c+d
}

啥意思?教你求和?app

答案就是求和,你沒看錯。求4個5個數的和還能夠這樣寫,要是10個呢?a+b+c+d+e.....函數

那要是100個呢?完了,字母都不夠用了。其實你能夠這樣寫:this

function getSum(){
	var sum = 0
	for(var i=0;i<arguments.length;i++){	
		sum += arguments[i]
	}
	return sum
}
console.log(getSum(1,2,3,4))  //輸出10

函數內部的arguments是什麼?也沒有聲明,也沒有傳參。其實它是函數的隱式參數,每一個函數都會有,而且arguments是一個僞數組,用來代替不肯定個數的參數的,能夠用arguments加下標的方式獲取傳給getSum函數的參數,它還有length屬性,這些都與真正的數組類似,但它沒有數組的方法,好比push、jion等。prototype

this

除了arguments,函數還有個隱式參數,即this。有函數的地方就會有this,this指向的是函數調用的上下文,上下文?啥意思?記得初中高中作語文閱讀題最怕的一句話就是:聯繫上下文理解某句話或某個詞語的意思。code

可是若是咱們明白了函數的四種調用方式,對應this有四種指向,就不怕不清楚this的指向了。對象

1. 函數做爲普通函數被調用;作用域

2. 函數做爲對象的方法被調用;get

3. 函數做爲構造器被調用;回調函數

4. 使用apply()和call()進行調用;

第一種:

var name = "cat"
function fun(){
	var name = "dog"
	console.log(this.name)    //cat
	console.log(this==window) //true
}
fun()

函數做爲普通函數被調用,this指向全局做用域,即window對象,將這種調用方式與下面第二種會更加清楚。

第二種

var name = "cat"
var obj={
	name:"dog",
	getName:function(){
		console.log(this.name)
	}
}
obj.getName()  //dog

這種,函數 function ( ){ console.log(this.name) }做爲objde方法getName被調用,因此this指向調用對象,即obj,因此this.name就是obj.name。

第三種

function Cat(name){
	this.name = name
}
var cat1 = new Cat("小黃")
console.log(cat1.name) //小黃

當Cat函數被運用關鍵字new調用時,就是被當作構造器調用的,這時會發生:

1.建立一個空對象{ }

2.讓this指向這個空對象,那麼這個空對象將被增長一個name屬性,而且賦值爲「小黃」,即{name:"小黃"}

3.最後讓cat1指向這個對象,即cat1={name:"小黃"}

4.因此cat1.name值爲「小黃」

這種方式this指向的是對象的實例。

第四種

每一個函數都有call()或apply()方法,能夠運用他們來強制改變this的指向,語法爲:函數名.call(指向對象,參數)。好比下面咱們能夠限制讓fun函數中的this指向對象obj或者fun本身。

var name = "cat"
function fun(){
	console.log(this.name)    
}
var obj={
	name:"pig"
}
fun()  //cat

改造後爲

var name = "cat"
function fun(){
	console.log(this.name)    
}
var obj={
	name:"pig"
}
fun.call(obj)  //pig
fun.call(fun)  //fun 由於函數都有name屬性,函數fun的name就是fun

再看下面的實際例子,或許會更加清楚:

document.getElementById("div").onclick=function(){
	console.log(this)  //div的DOM對象
	function fun(){
		console.log(this)  //window
	}
	fun()
}

上面這個例子其實就是第一種和第二種的結合,首先div被點擊後,回調函數將被執行,其實在內部這個函數被賦值給了div對象的onclick屬性,這就對應第二種,函數被當作對象的方法被調用,因此內部的this執行div對象。 可是其內部又還有一個內部函數fun,它被當作普通函數調用,對應第一種,因此this指向window,但咱們有時還想讓這時的this指向div對象,能夠有兩種改造方式,that=this和call或者apply:

document.getElementById("div").onclick=function(){
	console.log(this)  //div的DOM對象
	var that = this
	function fun(){
		console.log(that)  //div的DOM對象
	}
	fun()
}
document.getElementById("div").onclick=function(){
	console.log(this)  //div的DOM對象

	function fun(){
		console.log(this)  //div的DOM對象
	}
	fun.call(this)
}

call和apply

call和apply的做用

1. 如上面所述,call和apply能夠用來強制改變this指向

2. 用來借用其餘對象的方法

在講第2個做用以前先來看看call和apply的區別,它們的做用是同樣的,惟一區別就在於參數的傳遞形式。

call傳入的參數個數不固定,第一個參數是this的強制指向對象,第二個參數開始日後就是做爲參數傳給函數:

var fun=function(a,b,c){
	console.log([a,b,c])
}
fun.call(null,1,2,3)   // [1,2,3]

而apply的參數就是兩個,第一個與call同樣表明this的強制指向對象,第二個能夠爲數組,也能夠爲僞數組arguments:

var fun=function(a,b,c){
	console.log([a,b,c])
}
fun.apply(null,[1,2,3])   // [1,2,3]

爲何咱們兩次傳的參數不一樣,call傳1,2,3, apply傳[1,2,3],卻輸出相同的結果,這是由於js函數內部的實現運用了arguments,當運用call時,即爲正常狀況,函數內部讓第一個參數對應arguments[0],第二個參數對應arguments[1],以此類推.......,當運用apply時,函數內部直接讓arguments等於數組[1,2,3]。

Math.max()方法用來獲取幾個數中的最大值,如Math.max(1,2,3)==3,可是不能用於獲取數組中的最大值,因此能夠利用apply來將這個方法運用於數組:

function getMax(array){
	return Math.max.apply(null,array)
}
getMax([1,2,3]) // 3

如今再來看看call和apply的第二個做用:借用別人的方法。 咱們想讓一個空對象{ }也有數組的push方法,實現以下:

function fnn(){
	var obj={}
	Array.prototype.push.apply(obj,arguments)
	console.log(obj)

}
fnn(1,2,3)

apply和call能夠借用別人的方法的祕密就在於:函數內部運用了大量的this和arguments。正常狀況下push的用法爲

var arr=[ ]
arr.push(1);

push對應的函數,做爲方法被空數組arr調用(第二種調用),因此其內部的this指向arr,可是call和apply卻能夠改變this指向,使得this指向obj,那麼此後,arguments[0]將被賦值給obj[0],而不是arr[0],而arguments[1]將被賦值給obj[1],而不是arr[1],以此類推..........以此達到借用的目的。

相關文章
相關標籤/搜索