call和apply的使用-擴展篇

一、回顧

在上篇文章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í)行腾夯。


寫在最后颊埃,文中總結,如有不全或錯誤蝶俱,歡迎留言指出班利,謝謝支持……^?_?^

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市榨呆,隨后出現(xiàn)的幾起案子罗标,更是在濱河造成了極大的恐慌,老刑警劉巖积蜻,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闯割,死亡現(xiàn)場離奇詭異,居然都是意外死亡竿拆,警方通過查閱死者的電腦和手機宙拉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來如输,“玉大人鼓黔,你說我怎么就攤上這事央勒。” “怎么了澳化?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵崔步,是天一觀的道長。 經(jīng)常有香客問我缎谷,道長井濒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任列林,我火速辦了婚禮瑞你,結果婚禮上,老公的妹妹穿的比我還像新娘希痴。我一直安慰自己者甲,他們只是感情好,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布砌创。 她就那樣靜靜地躺著虏缸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嫩实。 梳的紋絲不亂的頭發(fā)上刽辙,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機與錄音甲献,去河邊找鬼宰缤。 笑死,一個胖子當著我的面吹牛晃洒,可吹牛的內(nèi)容都是我干的慨灭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锥累,長吁一口氣:“原來是場噩夢啊……” “哼缘挑!你這毒婦竟也來了?” 一聲冷哼從身側響起桶略,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎际歼,沒想到半個月后惶翻,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鹅心,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年旭愧,在試婚紗的時候發(fā)現(xiàn)自己被綠了颅筋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡议泵,死狀恐怖占贫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情先口,我是刑警寧澤型奥,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站碉京,受9級特大地震影響厢汹,放射性物質發(fā)生泄漏。R本人自食惡果不足惜谐宙,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一烫葬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凡蜻,春花似錦厘灼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舰讹。三九已至茅姜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間月匣,已是汗流浹背钻洒。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锄开,地道東北人素标。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像萍悴,于是被迫代替她去往敵國和親头遭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354