一、回顧
在上篇文章call和apply的使用-基礎篇中兽肤,我們已經(jīng)提到了call和apply的功能和語法套腹,這里稍作回顧:
介紹:call和apply都是函數(shù)的方法,需要加在函數(shù)體后執(zhí)行资铡。
功能:都是用來修改函數(shù)的執(zhí)行上下文(this)电禀。
語法:
- call(thisObj,arg1,arg2,arg3,……)
- apply(thisObj,argArr)
說明:call和apply的主要區(qū)別就是:call可以接受一個或以上的參數(shù),當接受多個參數(shù)時笤休,從第二個參數(shù)開始尖飞,后面所有的參數(shù)都會改變原函數(shù)的參數(shù);apply只能接受一個或兩個參數(shù)宛官,當接受兩個參數(shù)時葫松,第二個參數(shù)必須是一個數(shù)組或類數(shù)組,數(shù)組中的數(shù)據(jù)底洗,會改變原函數(shù)arguments中的參數(shù)腋么。
而call和apply的第一個參數(shù),都是用來改變原函數(shù)的this指向亥揖。
所以珊擂,以下演示以call為主圣勒,如果只有一個參數(shù),那么可以直接替換成apply摧扇,并無區(qū)別圣贸;如果存在兩個以上參數(shù),替換成apply時扛稽,需要把第二個及后面所有參數(shù)放在一個數(shù)組中吁峻。
二、使用
使用方式1:執(zhí)行時一個對象可以使用另一個對象的方法
function Doctor(){
this.name = "Doctor";
this.say = function(){
console.log(this.name);
}
}
function Stephen(){
this.name = "Stephen Strange";
}
var doctor = new Doctor();
var stephen = new Stephen();
// 通過call將stephen對象傳入doctor的say方法在张,此時say方法中的this被指向stephen對象
doctor.say.call(stephen); //Stephen Strange
使用方式2:實現(xiàn)繼承
function Doctor(name){
this.name = name;
this.say = function(){
console.log(this.name)
}
}
function Stephen(name){
//當前函數(shù)內(nèi)的this指向函數(shù)Son的實例化對象
//在執(zhí)行Stephen時執(zhí)行Doctor用含,同時將Doctor內(nèi)的this改變成Stephen的this
Doctor.call(this,name)
}
var doctor = new Doctor("Doctor")
doctor.say(); //Doctor
var stephen = new Stephen("Stephen Strange")
// 在Stephen中并沒有say方法,但是因為在new Stephen時帮匾,執(zhí)行了Doctor啄骇,
// 并將Doctor中的this指向Stephen的this,
// 那么在new Stephen后瘟斜,得到的實例缸夹,也具有了Doctor內(nèi)的屬性和方法
stephen.say(); //Stephen Strange
使用方式3:多繼承
function People(){
this.say = function(){
console.log(`My name is ${this.name}. I will have ${this.attr} ${this.skill}.`)
}
}
function Doctor(){
this.skill = "cure";
}
function Magic(){
this.attr = "Amazing";
}
function Stephen(name){
this.name = name;
// 執(zhí)行其他函數(shù)的同時將原函數(shù)的this指向都改成Stephen的this,此時所有屬性和方法可以互相訪問
People.call(this);
Doctor.call(this);
Magic.call(this);
}
var stephen = new Stephen("Stephen Strange");
stephen.say(); //My name is Stephen Strange. I will have Amazing cure.
使用方式4:改變系統(tǒng)函數(shù)的this指向螺句,實現(xiàn)偽數(shù)組轉真數(shù)組
我們知道js中有很多類(偽)數(shù)組虽惭,偽數(shù)組雖然也按照索引存儲數(shù)據(jù),有l(wèi)ength屬性壹蔓,但是卻不具有數(shù)組的方法趟妥,如push,pop等佣蓉。
如果我們想使用數(shù)組的方法來操作偽數(shù)組披摄,那么需要先將偽數(shù)組轉成真數(shù)組,偽轉真的方法有很多種勇凭,這里我們只說使用call方法轉換:
var ali = document.querySelectorAll("li");
// instanceof:查看一個實例是否指向某個構造函數(shù)的原型(查看一個實例是否屬于某個類)
console.log(ali instanceof Array); //false
// ali.push("hello"); //報錯:ali.push is not a function
var arr = new Array(4,5,6);
// instanceof:查看一個實例是否指向某個構造函數(shù)的原型(查看一個實例是否屬于某個類)
console.log(arr instanceof Array); //true
arr.push("hello")
console.log(arr); //[4,5,6,"hello"]
//此處開始轉換
var aliZ = Array.prototype.slice.call(ali)
console.log(aliZ)
console.log(aliZ instanceof Array); //true
// 此時aliZ就是一個真數(shù)組疚膊,可以使用數(shù)組的眾多方法來操作
aliZ.push("world");
console.log(aliZ); //[li,li,li,...,"world"]
使用方式5:優(yōu)化Math對象的方法
Math對象的min和max方法只能接受多個數(shù)據(jù),而不能接受單個數(shù)組虾标。但是我們知道寓盗,函數(shù)內(nèi)的arguments保存所有傳進來的實參,此處利用apply第二個參數(shù)是數(shù)組璧函,并且會覆蓋原函數(shù)arguments的特點傀蚌,將數(shù)組由apply傳進去,交給min或max處理蘸吓,即可快速得到數(shù)組的最大或最小值
var arr = [4,6,2,7,1];
console.log(Math.min(arr)); //NaN
console.log(Math.max(arr)); //NaN
console.log(Math.min.apply(null,arr)) //1
console.log(Math.max.apply(null,arr)) //7
使用方式6:改造系統(tǒng)方法的調用方式
我們知道js中許多實例的方法都是定義在構造函數(shù)的原型對象上善炫,如Array.prototype.push / String.prototype.match / Function.prototype.bind等,當我們通過實例調用這些方法時库继,調用方式為arr.push() / str.match() / fn.bind()箩艺,我們可以利用call或apply函數(shù)改變這些實例方法的調用方式窜醉。
改造之后的調用如:push(arr,"hello")
let arr = [3,4,5];
Array.prototype.push.call(arr,"hello")
console.log(arr); //[3,4,5,"hello"]
// 通過執(zhí)行Function原型上的call方法的bind方法,改變call中原本應指向Function實例的this為Array.prototype.push艺谆,
// 并保存bind的返回值--改造之后的新call函數(shù)榨惰,放在newPush
const newPush = Function.prototype.call.bind(Array.prototype.push);
// 此時,call方法中的this指向為Array原型上的push方法静汤,
// 執(zhí)行newPush琅催,相當于執(zhí)行了Function.prototype.call.call(Array.prototype.push),
// call的第一個參數(shù)用來改變原函數(shù)this的指向撒妈,后call將前call中的this改成Array.prototype.push
// 此時后call執(zhí)行恢暖,得到改變之后的前call
// 也就相當于得到了Array.prototype.push.call()
// 最終執(zhí)行newPush相當于執(zhí)行了Array.prototype.push.call()
newPush(arr,"world");
console.log(arr); //[3,4,5,"hello","world"]
// 此類改造還有:
const slice = Function.prototype.call.bind(Array.prototype.slice);
console.log(slice(arr, 0, 2)); //[3,4]
const match = Function.prototype.call.bind(String.prototype.match);
console.log(match("a1ab12abc123", /\d+/g)); //["1", "12", "123"]
// 練習:使用相同方式嘗試改造數(shù)組或字符的其他方法
三、總結
其實不管在任何地方狰右,只要牢記call和apply方法的功能:修改原函數(shù)的this指向,并執(zhí)行這個新函數(shù)舆床。就可以輕松的駕馭函數(shù)的call和apply方法棋蚌。
臨近結尾順便提一下函數(shù)的另一個方法bind,其實bing和call/apply的功能類似挨队,只不過bind修改this指向之后谷暮,返回的新函數(shù)不會自動執(zhí)行,如果有需要盛垦,需要手動執(zhí)行湿弦;而call和apply改變this之后,返回的新函數(shù)會自動執(zhí)行腾夯。
寫在最后颊埃,文中總結,如有不全或錯誤蝶俱,歡迎留言指出班利,謝謝支持……^?_?^