我們都知道call,apply,bind函數都是為了改變this的指向,那麼對於三種函數有什麼相同點有什麼不太點或者有什麼應用呢?下面我們來進行介紹
call與apply函數
在javascript種,call,apply的出現是為了改變函數體內部this的指向,下面我們來看一個栗子,並從中進行分析。
var a = "我是window的小a";
var obj = {
a:"我是obj的小a",
foo:function (...arg) {
console.log(this.a,this,...arg)
}
}
obj.foo() //此時的this為obj
var f2 = obj.foo
f2() //此時的this為window
f2.call(obj,1,2,3) //call改變了this的指向,此時的this為obj,並傳入參數
f2.call(obj,[1,2,3]) //輸出obj中的a
f2.apply(obj,[1,2,3],3,4,5) //apply改變了this的指向為obj,傳入參數數組,在參數數組之後傳遞參數,並不能傳入該參數
f2.apply(obj,1,2,3) //報錯!apply第二個參數必須為參數數組
我們來看一下輸出結果。
由上述輸出結果來看,使用call和apply能夠改變this的指向。函數f2原本的指向為window,使用call和apply函數綁定obj後this的指向為obj。對於上述的輸出結果還有一個報錯?立馬不淡定了。原來apply的第二個參數只能傳入參數數組,不能傳入多個參數。
對於上述輸出結果的小總結為:
- call和apply都用於去改變this的指向問題,第一個參數為this所指向的對象
- call和apply都為直接調用函數,返回值就為調用函數的返回值。
- call的第二個位置和之後傳遞參數列表,當向call中傳遞數組時,則視為只傳遞了一個參數(這個參數為數組)
- apply的第二個位置只能傳遞參數數組,在參數數組之後傳遞參數,均失效。
根據call和apply的特點,我們可以有以下應用。
使用call進行繼承
function father(name,age,hometown,hobby) {
this.name = name;
this.age = age;
this.hometown = "中國"
this.hobby = hobby;
}
function son(name,age,hobby,hometown) {
father.call(this,name,age,hometown)//繼承父類中的多個屬性
this.hobby = hobby;
}
let f = new father("小頭爸爸",38,"中國","釣魚");
let s = new son("大頭兒子",16,"打球");
console.log(f)
console.log(s)
輸出結果:
使用call判斷數據類型
console.log(Object.prototype.toString.call("pp"))
console.log(Object.prototype.toString.call({age:15}))
console.log(Object.prototype.toString.call(23))
console.log(Object.prototype.toString.call([1,2,3]))
在這我就不一一列舉關於數據的數據類型了,此時肯定有人產生疑惑,為什麼平時我們用的obj.toString和object.prototype.toString.call(obj)的結果為什麼不一樣了呢?因為我們使用的obj.toString()在一些數據類型中都重寫了toString的方法,對於函數和數組來講,使用obj.toString都會直接輸出字符串,故使用obj.toString()不能輸出數據類型。但為什麼Object.prototype.toString.call(obj)能夠輸出數據類型的值呢?因為toString為Object的原型方法,並沒有像obj.toString()一樣重寫該方法。使用Object上的原型中的toString方法的返回值為數據類型。故我們可以通過Object.prototype.toString.call(obj)來判斷數據類型。
使用call使偽數組使用數組方法
<div class="xixi"></div>
<div class="xixi"></div>
<div class="xixi"></div>
<div class="xixi"></div>
<script>
//將偽數組轉換為數組,使其可以調用數組所具有的方法
var realArr = Array.prototype.slice.call(document.getElementsByTagName("div"));
realArr.push(1)
console.log(realArr)
//偽數組
var div = document.getElementsByTagName("div")
div.push(1) //報錯為偽數組中沒有數組具有的方法
</script>
輸入結果:
對於我們調用的一些方法例如document.getElementsByName()、document.getElementsByTagName() ,childNodes/children 等返回的均為偽數組,此時不能使用數組的方法,我們可以使用call函數使其轉換為真正數組所帶有真正數組方法的對象,這個時候我們就能夠調用數組的所有方法啦。
使用apply進行數組的連接
var arr1 = [1,"xixi",{age:17},34]
var arr2 = [3,"haha",{age:44},21]
Array.prototype.push.apply(arr1,arr2)//進行數組的連接
console.log(arr1)
使用apply獲取數組中的最大值
var arr1 = [1,3,4,5,93]
//實際為向Math.max中傳入參數arr1,等同於Math.max(arr1)
var max = Math.max.apply(Math.max,arr1)
console.log(max)
bind函數
與call和apply相似,其作用都是用於改變函數內部this的指向。但第二個參數開始是傳入參數列表,這點與call相似,但與其有什麼不同的地方呢?call與apply是立即執行函數而bind函數是將函數返回。
綁定函數
使用bind方法會返回一個函數,使其函數無論怎麼調用,其this都會指向原來所綁定的那個對象
var a = "我是window的小a";
var obj = {
a:"我是obj的小a",
foo:function (...arg) {
console.log(this.a,this,...arg)
}
}
obj.foo() //此時的this為obj
var f1 = obj.foo() //此時的this為window
var f2 = f1.bind(obj) //綁定this為obj,並返回一個新函數
f2() //輸出我是obj的小a
配合使用setTimeout
在我們沒有給setTimeout綁定this的情況下,當我們在setTimeout中使用this,this關鍵字會指向window對象,我們可以利用bind函數綁定this,使其能夠利用this調用想要綁定的函數。
function foo() {
this.age = 20
}
//函數f1
foo.prototype.f1 = function () {
console.log(this) //此時的this為foo
setTimeout(this.f2,1000) //但是此時的this為window,而不是foo,因此無法調用f2
var _this = this //獲取到當前的this
setTimeout(this.f2.bind(_this),1000) //將this.f2的this綁定給foo
}
//函數f2
foo.prototype.f2 = function () {
console.log("我的年齡為:" + this.age)
}
var foo = new foo() //創造實例
foo.f1()
使用bind將類數組轉換為數組
function foo() {
var TempSlice = Array.prototype.slice;
// 把函數的call方法綁定在數組slice方法上,之後再給call方法傳遞參數
var slice = Function.prototype.call.bind(TempSlice);
return slice(arguments);
}
console.log(foo(1,2,3)) //[1,2,3]
區別bind()、call()、apply()
- 都能指定函數中的this
- call()和apply是立即調用函數
- bind()是將函數返回
對於bind(),cal(),apply()的介紹就到這裏啦,有幫助的話就點個讚唄,歡迎評論區指正!