你有遇見(jiàn)過(guò)給bind返回的函數(shù)做new操作的場(chǎng)景,本篇主要講述的就是實(shí)現(xiàn)一下兼容new操作的bind寫(xiě)法,順便學(xué)習(xí)一下new操作符,為大家提供下參考。
大家可以去看下關(guān)于 JS 中 bind 方法的實(shí)現(xiàn)的文章,并給出了實(shí)現(xiàn):
Function.prototype.myBind = function(thisArg, ...prefixArgs) { const fn = this; return function(...args) { return fn.call(thisArg, ...prefixArgs, ...args); } }
但沒(méi)有處理通過(guò) new 創(chuàng)建實(shí)例的情況。
我們沒(méi)考慮過(guò)給 bind 返回的函數(shù)做 new 操作的場(chǎng)景,主要就是這種情況極少遇見(jiàn)。
但有時(shí)還是會(huì)出現(xiàn),在實(shí)現(xiàn)一下兼容 new 操作的 bind 寫(xiě)法,順便學(xué)習(xí)一下 new 操作符。
這里我推薦先看下前一篇文章:《前端面試題:手寫(xiě) bind》。
new
我們先學(xué)習(xí)一下 new 操作符。
new 用于通過(guò)函數(shù)來(lái)創(chuàng)建一個(gè)對(duì)象實(shí)例,在很多語(yǔ)言中都能看到。
JS 的函數(shù),除了可以是普通函數(shù),比如:
function sum(a, b) { return a + b; }
還可以是構(gòu)造函數(shù),只需要在構(gòu)造時(shí)在它前面加一個(gè) new:
function Person(name, age) { this.name = name; this.age = age; } const person = new Person('前端西瓜哥', 100) // Person {name: '前端西瓜哥', age: 100}
new 創(chuàng)建一個(gè)新對(duì)象,做了下面幾件事:
創(chuàng)建一個(gè)空對(duì)象{};
空對(duì)象的原型屬性__proto__指向構(gòu)造函數(shù)的原型對(duì)象Person.prototype;
函數(shù)中的 this 設(shè)置為這個(gè)空對(duì)象;
如果該函數(shù)不返回一個(gè)對(duì)象,就返回這個(gè) this,否則返回這個(gè)對(duì)象。
判斷函數(shù)是否通過(guò) new 被調(diào)用
怎么判斷一個(gè)函數(shù)正在被 new 操作符調(diào)用?
答案是使用 instanceof 判斷 this 是否為當(dāng)前函數(shù)的實(shí)例,即this instanceof Fn為 true,表示在通過(guò) new 構(gòu)建實(shí)例。
先看下實(shí)例:
function Person() { if (this instanceof Person) { console.log('通過(guò) new 構(gòu)建實(shí)例'); } else { console.log('普通調(diào)用') } } Person() // 輸出:普通調(diào)用 new Person() // 輸出:通過(guò) new 構(gòu)建實(shí)例
在 Vuejs 的源碼,你會(huì)看到下面代碼,這里也用到了這個(gè)技巧。
function Vue(options) { if (__DEV__ && !(this instanceof Vue)) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options) }
你在開(kāi)發(fā)環(huán)境如果不通過(guò) new 來(lái)使用 Vue 對(duì)象,會(huì)在控制臺(tái)提示你要通過(guò) new 來(lái)調(diào)用 Vue。
new 和 bind
如果我們 new 的是 Function.prototype.bind 返回的新函數(shù),會(huì)發(fā)生什么事情?
function Person(name, age) { this.name = name; this.age = age; } const BoundPerson = Person.bind(null, '前端西瓜哥'); const boundPerson = new BoundPerson(100); // Person {name: '前端西瓜哥', age: 100} boundPerson.__proto__ === Person.prototype // true
結(jié)果等價(jià)于直接去 new 原始函數(shù)。
不同的是,仍舊可以進(jìn)行參數(shù)的預(yù)置。可以看到,構(gòu)造函數(shù)的第一個(gè)參數(shù),在調(diào)用 bind 的時(shí)候就提前設(shè)置為 '前端西瓜哥'。
實(shí)現(xiàn)完整的 bind
完整實(shí)現(xiàn)如下:
Function.prototype.myBind = function(thisArg, ...prefixArgs) { const fn = this; const boundFn = function(...args) { // 通過(guò) new 使用當(dāng)前函數(shù) if (this instanceof boundFn) { return new fn(...prefixArgs, ...args); } // 普通的方法調(diào)用當(dāng)前函數(shù) return fn.call(thisArg, ...prefixArgs, ...args); } boundFn.prototype = fn.prototype; return boundFn; }
這里我通過(guò)this instanceof boundFn來(lái)判斷是否用了 new,如果是,就直接 new 原始函數(shù)然后返回,記得帶上 bind 預(yù)置好的參數(shù)。
和原本一樣(參照《前端面試題:手寫(xiě) bind》)。
boundFn.prototype = fn.prototype;這個(gè)可寫(xiě)可不寫(xiě),只是讓 bind 返回的新函數(shù)的 prototype 指向原函數(shù)的 prototype。
如果是原生 bind 返回的函數(shù),它是沒(méi)有 protoype 屬性的,可以認(rèn)為它是一種特別的函數(shù),而我們實(shí)現(xiàn)的 bind 返回的卻是一個(gè)普通函數(shù),所以并不能完全模擬的。
如果你追求完美的實(shí)現(xiàn),可以研讀一下Function.prototype.bind的標(biāo)準(zhǔn):
然后再看看知名的core.js庫(kù)中對(duì) bind 的實(shí)現(xiàn):
其中核心實(shí)現(xiàn)為:
// `Function.prototype.bind` method implementation // https://tc39.es/ecma262/#sec-function.prototype.bind module.exports = Function.bind || function bind(that /* , ...args */) { var F = aCallable(this); var Prototype = F.prototype; var partArgs = arraySlice(arguments, 1); var boundFunction = function bound(/* args... */) { var args = concat(partArgs, arraySlice(arguments)); return this instanceof boundFunction ? construct(F, args.length, args) : F.apply(that, args); }; if (isObject(Prototype)) boundFunction.prototype = Prototype; return boundFunction; };
這里有更多的細(xì)節(jié):
這里判斷了 this 是否為函數(shù)類(lèi)型,不是函數(shù)會(huì)報(bào)錯(cuò);
F.prototype 需要是一個(gè)對(duì)象或函數(shù),才能賦值給新函數(shù);
使用了普通函數(shù)和 arguments,這是為了兼容 ES5。
結(jié)尾
手寫(xiě) bind,需要我們學(xué)習(xí)更多知識(shí):
bind 的詳盡用法:包括改變 this、預(yù)置參數(shù)、new 的表現(xiàn);
閉包的使用:保存一些私有變量;
通過(guò)原型鏈的方式(this instanceof boundFn)判斷是否通過(guò) new 調(diào)用當(dāng)前函數(shù);
使用 call 在執(zhí)行時(shí)改變函數(shù)的 this 指向。
以上就是全部?jī)?nèi)容,歡迎大家繼續(xù)關(guān)注后續(xù)更多精彩內(nèi)容。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/128375.html
JavaScript筆試部分 點(diǎn)擊關(guān)注本公眾號(hào)獲取文檔最新更新,并可以領(lǐng)取配套于本指南的 《前端面試手冊(cè)》 以及最標(biāo)準(zhǔn)的簡(jiǎn)歷模板. 實(shí)現(xiàn)防抖函數(shù)(debounce) 防抖函數(shù)原理:在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計(jì)時(shí)。 那么與節(jié)流函數(shù)的區(qū)別直接看這個(gè)動(dòng)畫(huà)實(shí)現(xiàn)即可。 showImg(https://segmentfault.com/img/remote/146000002...
摘要:第一種直接調(diào)用避免在不必要的情況下使用,是一個(gè)危險(xiǎn)的函數(shù),他執(zhí)行的代碼擁有著執(zhí)行者的權(quán)利。來(lái)自于此外,實(shí)現(xiàn)需要考慮實(shí)例化后對(duì)原型鏈的影響。函數(shù)柯里化的主要作用和特點(diǎn)就是參數(shù)復(fù)用提前返回和延遲執(zhí)行。手寫(xiě)路徑導(dǎo)航 實(shí)現(xiàn)一個(gè)new操作符 實(shí)現(xiàn)一個(gè)JSON.stringify 實(shí)現(xiàn)一個(gè)JSON.parse 實(shí)現(xiàn)一個(gè)call或 apply 實(shí)現(xiàn)一個(gè)Function.bind 實(shí)現(xiàn)一個(gè)繼承 實(shí)現(xiàn)一個(gè)J...
摘要:最近準(zhǔn)備初級(jí)前端面試,發(fā)現(xiàn)有很多手寫(xiě)實(shí)現(xiàn)什么的,例如什么手寫(xiě)實(shí)現(xiàn),。后面以這道題為引線面試官可能會(huì)追問(wèn)什么是執(zhí)行上下文的判斷,的區(qū)別手寫(xiě)一個(gè)函數(shù)實(shí)現(xiàn)斐波那契數(shù)列首先拷一個(gè)阮神在他教程里的一個(gè)寫(xiě)法。 最近準(zhǔn)備初級(jí)前端面試,發(fā)現(xiàn)有很多手寫(xiě)實(shí)現(xiàn)什么的,例如什么手寫(xiě)實(shí)現(xiàn)bind,promise。手寫(xiě)ajax,手寫(xiě)一些算法。翻閱了很多書(shū)籍和博客。 這里做一個(gè)總結(jié)改進(jìn),算是對(duì)我后面大概為期一個(gè)月找...
摘要:先說(shuō)下我面試情況,我一共面試了家公司。篇在我面試的眾多公司里,只有同城的面問(wèn)到相關(guān)問(wèn)題,其他公司壓根沒(méi)問(wèn)。我自己回答的是自己開(kāi)發(fā)組件面臨的問(wèn)題。完全不用擔(dān)心對(duì)方到時(shí)候打電話核對(duì)的問(wèn)題。 2019的5月9號(hào),離發(fā)工資還有1天的時(shí)候,我的領(lǐng)導(dǎo)親切把我叫到辦公室跟我說(shuō):阿郭,我們公司要倒閉了,錢(qián)是沒(méi)有的啦,為了不耽誤你,你趕緊出去找工作吧。聽(tīng)到這話,我虎軀一震,這已經(jīng)是第2個(gè)月沒(méi)工資了。 公...
閱讀 547·2023-03-27 18:33
閱讀 732·2023-03-26 17:27
閱讀 630·2023-03-26 17:14
閱讀 591·2023-03-17 21:13
閱讀 521·2023-03-17 08:28
閱讀 1801·2023-02-27 22:32
閱讀 1292·2023-02-27 22:27
閱讀 2178·2023-01-20 08:28