摘要:所以,這篇文章將通過一段非常簡潔的等式,把當中一個相對較難的知識點,,和給串聯起來要理解當中的這三個關鍵字,首先得弄清楚它們是用來干嘛的。方案讓吃掉,直接消化吸收的所有能力。
關于JS當中的call,apply和bind,相信大家和我一樣,已經看過了無數篇相關的文章,都有自己的理解。所以這篇文章并非什么科普類的文章,僅僅是把我自己的理解記錄下來。
我的學習習慣,是喜歡把各種看似孤立的知識點串聯起來,綜合理解并運用,通過最簡單最直觀的思路把它理解透。所以,這篇文章將通過一段非常簡潔的等式,把JS當中一個相對較難的知識點,call,apply和bind給串聯起來:
cat.call(dog, a, b) = cat.apply(dog, [a, b]) = (cat.bind(dog, a, b))() = dog.cat(a, b)
要理解JS當中的這三個關鍵字,首先得弄清楚它們是用來干嘛的。復雜些來說,可以引用MDN文檔的原文:
可以讓call()中的對象調用當前對象所擁有的function。你可以使用call()來實現繼承:寫一個方法,然后讓另外一個新的對象來繼承它(而不是在新對象中再寫一次這個方法)。
簡單些來說,可以引用大家都看過的一句話:
為了動態改變某個函數運行時的上下文(context)。
又或者是
為了改變函數體內部this的指向
上面這些解釋都很正確,說得一點問題都沒有,但是里面卻又引入了繼承,上下文,this這些額外的知識點。如果我只想用最直觀的辦法去理解這三個關鍵字的作用,也許可以這么去理解:
定義一個貓對象:
class Cat { constructor (name) { this.name = name } catchMouse(name1, name2) { console.log(`${this.name} caught 2 mouse! They call ${name1} and ${name2}.`) } }
這個貓對象擁有一個抓老鼠的技能catchMouse()。
然后類似的,定義一個狗對象:
class Dog { constructor (name) { this.name = name } biteCriminals(name1, name2) { console.log(`${this.name} bite 2 criminals! Their name is ${name1} and ${name2}.`) } }
這個狗對象能夠咬壞人biteCriminal()。
接下來,我們實例化兩個對象,分別得到一只叫“Kitty”的貓和叫“Doggy”的狗:
const kitty = new Cat("Kitty") const doggy = new Dog("Doggy")
首先讓它們彼此發揮自己的技能:
kitty.catchMouse("Mickey", "Minnie") // Kitty caught mouse! They call Mickey and Minnie. doggy.biteCriminal("Tom", "Jerry") // Doggy bite a criminal! Their name is Tom and Jerry.
現在,我們希望賦予Doggy抓老鼠的能力,如果不使用這三個關鍵字,應該怎么做呢?
方案A:修改Dog對象,直接為其定義一個和Cat相同的抓老鼠技能。
方案B:讓Doggy吃掉Kitty,直接消化吸收Kitty的所有能力。
其實方案A和方案B的解決辦法是類似的,也是需要修改Dog對象,不過方案B會更簡單粗暴一點:
class Dog { constructor (name, kitty) { this.name = name this.catchMouse = kitty.catchMouse } biteCriminals(name1, name2) { console.log(`${this.name} bite 2 criminals! Their name is ${name1} and ${name2}.`) } } const kitty = new Cat("Kitty") const doggy = new Dog("Doggy", kitty) doggy.catchMouse("Mickey", "Minnie") // Doggy caught 2 mouse! They call Mickey and Minnie.
上面這種方法實在是太不優雅,往往很多時候在定義Dog對像的時候根本就沒有打算過要為它添加抓老鼠的方法。那么有沒有一種辦法能夠在不修改Dog對象內容的前提下,讓Doggy實例也能夠擁有抓老鼠的辦法呢?答案就是使用call,apply或者bind關鍵字:
kitty.catchMouse.call(doggy, "Mickey", "Minnie") kitty.catchMouse.apply(doggy, ["Mickey", "Minnie"]) const doggyCatchMouse = kitty.catchMouse.bind(doggy, "Mickey", "Minnie") doggyCatchMouse() // Doggy caught 2 mouse! They call Mickey and Minnie. // Doggy caught 2 mouse! They call Mickey and Minnie. // Doggy caught 2 mouse! They call Mickey and Minnie.
反過來,讓Kitty擁有咬壞人的能力,也可以通過這種辦法實現,讀者可以自行嘗試。
看到這里,相信讀者已經能夠明白call,apply和bind的區別及作用,反過來再查看各自的概念,應該也能夠更容易理解。
回到文章開頭的等式:
cat.call(dog, a, b) = cat.apply(dog, [a, b]) = (cat.bind(dog, a, b))() = dog.cat(a, b)
這里的“等號”其實并不嚴謹,因為三個關鍵字的區別及背后的原理肯定不是區區一個等號就能夠概括的,但是對于概念的理解以及實際情況下的運用來說,這條等式未必不是一個好的思路。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93964.html
摘要:關于的指向的問題請參照我的學習筆記。那么在這里事實上都改變了函數方法被調用時的指向。那么回調函數在執行的時候指向還是。大家看完之后應該已經懂了把還是不懂的話在評論區留言,我給大家解答。 先從一個小題目開始吧: 要實現一個加法函數,這個時候向函數當中傳遞個數大于0的若干個整形數據,求所有這些數據的和。 Function.prototype.call Function.prototype...
摘要:目錄函數的聲明函數的屬性和方法函數的作用域閉包知識點小結關于函數,可以從以下個方面去理解首先,數據類型上看函數在中是一種數據類型,是對象的一種其次,從功能上看函數本質上是一段反復調用的代碼塊最后,從地位上看函數在中和其他基本數據類型一樣,可 目錄 1.函數的聲明 2.函數的屬性和方法 3.函數的作用域 4.閉包知識點 5.小結 關于函數,可以從以下3個方面去理解:首先,數據類型上看:...
摘要:而當做普通函數調用的話,實際上即第種情況下,對函數普通調用,此時的指向這是正常情況下,會正確返回并且指向該對象,但是在構造函數當中,如果返回了一個對象,那么會指向返回的那個對象。 this應該是一個討論了很久的話題了。其中,關于this的文章,在很多的博客當中也有很多介紹,但是,以前我都是一知半解的去了解它,就是看博客當中,只介紹了一些情況下的 this 的使用方式,但是也并沒有自己去...
摘要:回顧一下關鍵詞的過程創建一個新的對象使得的指向構造函數的原型對象執行構造函數中的,改變的指向為如果結果是對象類型,則返回結果,否則返回指向的是調用時傳遞的第一個參數。 this = ? 在JS中,當一個函數執行時,都會創建一個執行上下文用來確認當前函數的執行環境,執行上下文分為 全局執行上下文和 函數執行上下文。而 this 就是指向這個執行上下文的對象。所以,this是在運行時決定的...
摘要:原文鏈接參考深入理解原型和閉包完結王福朋博客園中的作用域詳解博客園 前言 王福朋老師的 JavaScript原型和閉包系列 文章看了不下三遍了,最為一個初學者,每次看的時候都會有一種 大徹大悟 的感覺,而看完之后卻總是一臉懵逼。原型與閉包 可以說是 JavaScirpt 中理解起來最難的部分了,當然,我也只是了解到了一些皮毛,對于 JavaScript OOP 更是缺乏經驗。這里我想總...
閱讀 647·2021-11-25 09:43
閱讀 1915·2021-11-17 09:33
閱讀 830·2021-09-07 09:58
閱讀 2066·2021-08-16 10:52
閱讀 487·2019-08-30 15:52
閱讀 1728·2019-08-30 15:43
閱讀 987·2019-08-30 15:43
閱讀 2932·2019-08-29 16:41