前言
上次發了一下手動實現Promise https://segmentfault.com/a/11... 之後有猿友問我私聊我其它的像bind, call這些有沒有標準的寫法。我的回答是沒有,像這些api我們只能仿照它的功能,只能做得很像很像,但是絕對標準的答案是沒有的,就像我之前的那篇文章,也只是在參照PromiseA+規範去仿寫。好多Promise特有的API和特性也沒有寫進去,因爲我的目的是通過手寫來讓大家學習promise,現在的自動化構建工具及其插件這麼多,功能這麼強大我們沒有那麼多的要求去爲了實現兼容性而自己手動實現一遍。
包括我自己也是,面試的時候確實會問一些能不能手動實現一個什麼什麼的,但是這個目的並不是招你過來手寫API的,因爲我認爲手動造輪子或造框架要求你的js基本功必須紮實,想去實現一個東西,必須要完完全全地瞭解它纔可以。手動實現代碼的過程中往往也能看出一個人的編程思想。 當然,能寫出來或者模擬地很像那肯定是非常好的。
這篇文章我建議大家不要刻意去收藏,去記從而去應付面試, 這樣的學習方法不推薦(當然以前我也這樣),這種文章讀幾遍就行了,最主要地是之後去看這些API的用法以及特點,以及發掘每個人的編程思想, 然後自己寫下來,甚至寫的更好,記憶更加深刻。並且我也建議大家項目中遇到問題之後,第一時間不要去想着 複製錯誤信息然後 百度一下,必應一下,google一下,這樣意義不大,正確的做法我覺得是先通過錯誤信息先自己找到錯誤根源,想一想爲什麼會出錯,爲什麼達不到你想要的結果,然後總結經驗, 如果是一些框架API或者插件,遇到問題最好先去文檔裏面認真的看一下,然後學着自己手動解決。之前的我學新東西新技術很愛看視頻,百度搜。但是現在的話我更願意去看它的文檔,有時間去逛它的社區,當然了, 新東西出來不要去盲目地學 學技術要本着一精多專的目的去學, 不過不論是什麼方向, js的基礎是非常非常重要的。
好了,廢話太多了,進入正題。。。
手動實現Call
這些API的特點以及具體用法我就不介紹了,相信你如果奔着手動實現的方向來看,那麼你一定對它很瞭解
核心: this永遠指向最後調用它的對象
代碼:
Function.prototype.myCall = function(context, ...args) {
context['key'] = this;
context.key(...args);
delete context.key;
}
寫到這裏,call基本的特點已經實現,其實面試中一般寫到這裏也就通過了,但是實際上卻還差很遠。之前我的那篇文章也是如此,只是實現了基本的功能。因爲完完全全的實現確實會花費很多時間.
還是簡單說一下吧,我們知道call中是可以傳入一些基本類型的,並且我們現在的這種實現方式增加了一個顯示的key屬性,實際上你在調用真正的call的時候去打印this是無法發現這些額外屬性的,那麼我們就仿照這些特性再去優化,接近一下
Function.prototype.myCall = function(context, ...args) {
let newContext = context;
if ([null, undefined].includes(context) ) {
newContext = window || {};
}
switch (typeof context) {
case 'number': {
newContext = new Number(context);
break;
}
case 'boolean': {
newContext = new Boolean(context);
break;
}
case 'string': {
newContext = new String(context);
break;
}
}
Object.defineProperty(newContext, 'key', {
value: this,
configurable: true,
enumerable: false,
});
newContext.key(...args);
delete newContext.key;
}
這裏就差不多了,後面的我就直接上代碼了
實現apply
Function.prototype.myApply = function(context, obj) {
if (typeof obj !== 'object') {
throw new TypeError('CreateListFromArrayLike called on non-object')
}
let newContext = context;
if ([null, undefined].includes(context)) {
newContext = window;
}
switch (typeof context) {
case 'number': {
newContext = new Number(context);
break;
}
case 'boolean': {
newContext = new Boolean(context);
break;
}
case 'string': {
newContext = new String(context);
break;
}
}
Object.defineProperty(newContext, 'key', {
value: this,
configurable: true,
enumerable: false,
});
newContext.key(...obj);
delete newContext.key;
};
實現bind
Function.prototype.myBind = function(context, ...args) {
let _this = this;
let newFun = (...args2) => {
_this.call(context, ...args, ...args2)
};
newFun.prototype = Object.create(_this.prototype)
return newFun
};
實現Promise
https://segmentfault.com/a/11...
實現Object.create
Object.create = Object.create || function(obj){
var F = function(){};
F.prototype = obj;
return new F();
}
實現new
function myNew(fun) {
if (typeof fun !== 'function') throw new TypeError('fun is not a constructor')
return function() {
let obj = { '__proto__': fun.prototype }
fun.call(obj, ...arguments)
return obj
}
}
實現reduce
Array.prototype.myReduce = function(fn, orginal = '__orginal') {
let copy = [...this]
if (orginal !== '__orginal') {
copy.unshift(orginal)
}
while (copy.length > 1) {
let prev = copy.shift()
let next = copy.shift()
copy.unshift(fn(prev, next))
}
return copy[0]
}
總結
還是之前那句話,這些東西不推薦去硬記(如果你爲了應付面試當我沒說),目的是掌握它然後能夠自己寫下來。最後再說一下,雖然我們現在都是站在巨人的肩旁上,很多東西我們拿來即用就可以,但是是否還要有這種需求呢?有,當然有,任何東西都不是絕對完美的,react好用,但是你知道setState到底應該同步還是異步的口水大戰嗎? 完美是因爲適應,你能手寫是因爲我知道你絕對理解,好多公司有自己的框架,也都是會借鑑,仿寫甚至在源碼的基礎上去優化成爲適應自身公司業務的框架。你想想,對API的核心原理以及用法都不理解,不知道JS線程與GUI渲染線程互斥,不理解模塊化的意義, 不知道設計原則,不清楚何爲設計何爲模式,你又怎麼進階到手寫源碼,架構底層或是對前端性能進行優化呢? 所以,不要着急去背,哪裏不會哪裏沒有理解就認真地去理解它。接下來我應該會發一系列的算法文章,如果有興趣的可以提前關注一下, 當然了,如果你覺得對你有幫助, 麻煩點個贊,如果覺得哪裏寫的不嚴謹麻煩指出,共勉!