再談JavaScript中bind、call、apply三個(gè)方法的區(qū)別與使用方式
call的基本使用
var ary = [12, 23, 34]; ary.slice();
以上兩行簡(jiǎn)單的代碼的執(zhí)行過程為:ary
這個(gè)實(shí)例通過原型鏈的查找機(jī)制找到Array.prototype
上的slice
方法,讓找到的slice
方法執(zhí)行,在執(zhí)行slice
方法的過程中才把ary
數(shù)組進(jìn)行了截取。
注意:slice
方法執(zhí)行之前有一個(gè)在原型上查找的過程(當(dāng)前實(shí)例中沒有找到,再根據(jù)原型鏈查找)。
當(dāng)知道了一個(gè)對(duì)象調(diào)用方法會(huì)有一個(gè)查找過程之后,我們?cè)倏矗?/p>
var obj = {name:'iceman'}; function fn() { console.log(this); console.log(this.name); } fn(); // this --> window // obj.fn(); // Uncaught TypeError: obj.fn is not a function fn.call(obj);
call方法的作用:首先尋找call
方法,最后通過原型鏈在Function
的原型中找到call
方法,然后讓call
方法執(zhí)行,在執(zhí)行call
方法的時(shí)候,讓fn
方法中的this
變?yōu)榈谝粋€(gè)參數(shù)值obj
,最后再把fn
這個(gè)函數(shù)執(zhí)行。
知道這個(gè)原型上的原理后,咱們就可以動(dòng)手分析實(shí)現(xiàn)這三個(gè)方法了。
bind、call、apply 區(qū)別
call
和apply
都是為了解決改變this
的指向。作用都是相同的,只是傳參的方式不同。- 除了第一個(gè)參數(shù)外,
call
可以接收一個(gè)參數(shù)列表,apply
只接受一個(gè)參數(shù)數(shù)組
let a = { value: 1 } function getValue(name, age) { console.log(name) console.log(age) console.log(this.value) } getValue.call(a, 'yck', '24') getValue.apply(a, ['yck', '24'])
bind
和其他兩個(gè)方法作用也是一致的,只是該方法會(huì)返回一個(gè)函數(shù)。并且我們可以通過bind
實(shí)現(xiàn)柯里化
如何實(shí)現(xiàn)一個(gè) bind 函數(shù)
對(duì)于實(shí)現(xiàn)以下幾個(gè)函數(shù),可以從幾個(gè)方面思考
- 不傳入第一個(gè)參數(shù),那么默認(rèn)為
window
- 改變了
this
指向,讓新的對(duì)象可以執(zhí)行該函數(shù)。那么思路是否可以變成給新的對(duì)象添加一個(gè)函數(shù),然后在執(zhí)行完以后刪除?
當(dāng)然是肯定的,于是我們可以這樣寫:
Function.prototype.myBind = function (context) { if (typeof this !== 'function') { throw new TypeError('Error') } var _this = this var args = [...arguments].slice(1) // 返回一個(gè)函數(shù) return function F() { // 因?yàn)榉祷亓艘粋€(gè)函數(shù),我們可以 new F(),所以需要判斷 if (this instanceof F) { return new _this(...args, ...arguments) } return _this.apply(context, args.concat(...arguments)) } }
如何實(shí)現(xiàn)一個(gè) call 函數(shù)
Function.prototype.myCall = function (context,...arg) { var context = context || window // 給 context 添加一個(gè)屬性 // getValue.call(a, 'yck', '24') => a.fn = getValue //使用symbol 選擇一個(gè)獨(dú)一無(wú)二的值作為新添加的屬性 let symbol = new Symbol(); context[symbol] = this; let result = context[symbol](...arg) // 刪除添加的函數(shù) delete context[symbol] return result }
如何實(shí)現(xiàn)一個(gè)apply 函數(shù)
apply實(shí)現(xiàn)原理與call實(shí)現(xiàn)基本類似,只有傳值的方式不一樣。
Function.prototype.myApply = function (context,arg) { var context = context || window // 給 context 添加一個(gè)屬性 // getValue.call(a, 'yck', '24') => a.fn = getValue //使用symbol 選擇一個(gè)獨(dú)一無(wú)二的值作為新添加的屬性 let symbol = new Symbol(); context[symbol] = this; let result = context[symbol](arg) // 刪除添加的函數(shù) delete context[symbol] return result }
經(jīng)過對(duì)以上的函數(shù)進(jìn)行檢測(cè) , 完美通過。
const obj = { name : 'xiaoxiao', getName : function (arg) { console.log(`我是${this.name}里面的,我里面有${arg}`); } } obj.getName([0,0,0,0,0]); // 我是xiaoxiao里面的 const obj2 = { name : 'huahua' } //傳值不一樣 obj.getName.myCall(obj2,1,1,1,1,1,1); obj.getName.myBind(obj2)(2,2,2,2,2,2); obj.getName.myApply(obj2,[3,3,3,3,3,3]);
更多關(guān)于JS中call、apply三個(gè)方法的區(qū)別與使用方式請(qǐng)查看下面的相關(guān)鏈接
相關(guān)文章
基于javascript實(shí)現(xiàn)頁(yè)面加載loading效果
這篇文章主要為大家詳細(xì)介紹了基于javascript實(shí)現(xiàn)頁(yè)面加載loading效果的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03JavaScript變量作用域及內(nèi)存問題實(shí)例分析
這篇文章主要介紹了JavaScript變量作用域及內(nèi)存問題,結(jié)合實(shí)例形式分析了javascript全局變量、局部變量、塊級(jí)作用域等概念及內(nèi)存優(yōu)化問題相關(guān)操作技巧,需要的朋友可以參考下2019-06-06基于javascript實(shí)現(xiàn)最簡(jiǎn)單選項(xiàng)卡切換
這篇文章主要為大家詳細(xì)介紹了基于javascript實(shí)現(xiàn)最簡(jiǎn)單選項(xiàng)卡切換,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02JavaScript中關(guān)于class的調(diào)用方法
下面小編就為大家?guī)?lái)一片JavaScript中關(guān)于class的調(diào)用方法。具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-11-11微信小程序?qū)崿F(xiàn)簡(jiǎn)易計(jì)算器
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)簡(jiǎn)易計(jì)算器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07