axios源碼中的10多個工具函數(shù),值得一學~
本文來自讀者
Ethan01投稿,寫了axios源碼中的工具函數(shù)~非常值得一學。原文鏈接:https://juejin.cn/post/7042610679815241758
1.前言
歌德說過:讀一本好書,就是在和高尚的人談話。
同理,讀優(yōu)秀的開源項目的源碼,就是在和牛逼的大佬交流。
之前總覺得閱讀源碼是一件了不起的事情,是只有大佬才會去做的事。其實源碼也不是想象的那么難,至少有很多看得懂。 比如源碼中的工具函數(shù),就算是初級的前端開發(fā)也是能夠看懂的。重要的是,要邁出這一步,閱讀源碼沒什么的。
閱讀本文,你將學到:
1、javascript、nodejs調(diào)試技巧及調(diào)試工具;
2、如何學習調(diào)試axios源碼;
3、如何學習優(yōu)秀開源項目的代碼,應用到自己的項目;
4、axios源碼中實用的工具函數(shù);
2.環(huán)境準備
2.1 讀開源項目的貢獻指南
打開 axios[1] , 你會驚奇的發(fā)現(xiàn),這不是在瀏覽器中打開了一個vscode嗎?你沒有看錯,確實是在瀏覽器中打開了vscode,而且還打開了axios的源碼。如果你仔細看了瀏覽器地址欄里的url, 你會發(fā)現(xiàn)github后多了1s,顧名思義,就是1s打開github上的項目。一個小擴展:在每一個github項目中的url里直接加上1s,就能在網(wǎng)頁版vscode中查看源碼了(不過貌似現(xiàn)在只能查看,不能調(diào)試,調(diào)試的話還是要把源碼clone到本地)。
開源項目一般能在根目錄下的README.md文件或CONTRIBUTING.md中找到貢獻指南。貢獻指南中說明了參與貢獻代碼的一些注意事項,比如:代碼風格、代碼提交注釋格式、開發(fā)、調(diào)試等。
打開CONTRIBUTING.md[2],可以看到在54行的內(nèi)容:
Running?sandbox?in?browser
```bash
$?npm?start
#?Open?127.0.0.1:3000
這里就是告訴我們在如何在瀏覽器中運行項目的。
2.2 克隆項目并運行
這里使用axios的版本是v0.24.0;
git?clone?https://github.com/axios/axios.git
cd?axios
npm?start
打開?http://localhost:3000/
這時候可以看到這么一個頁面:

打開瀏覽器的控制臺,選中source選項,然后在axios目錄中可以找到源碼,如下圖:

這個axios.js就是入口文件,這時候就可以隨意打斷點進行調(diào)試了。
其實,閱讀所有源碼的流程都類似,之所以說的這么詳細,是為了能夠讓沒有閱讀過源碼的同學也能夠跟著一步一步的閱讀起來。當你讀完之后,肯定會有不少的收獲,把這個過程和收獲記錄下來,慢慢的提升自己,早晚會成為大佬。
3. 工具函數(shù)
今天的主角是`utils.js`[3]文件, 以下列出了文件中的工具函數(shù):
3.1 isArray 判斷數(shù)組
var?toString?=?Object.prototype.toString;
//?可以通過?`toString()`?來獲取每個對象的類型
//?一般返回值是?Boolean?類型的函數(shù),命名都以?is?開頭
function?isArray(val)?{
??return?toString.call(val)?===?'[object?Array]';
}
3.2 isUndefined 判斷Undefined
//?直接用`typeof`判斷
//?注意?typeof?null?===?'object'
function?isUndefined(val)?{
??return?typeof?val?===?'undefined';
}
3.3 isBuffer 判斷 buffer
//?先判斷不是?`undefined`和`null`
//?再判斷?`val`存在構(gòu)造函數(shù),因為`Buffer`本身是一個類
//?最后通過自身的`isBuffer`方法判斷
function?isBuffer(val)?{
??return?val?!==?null?
??????????&&?!isUndefined(val)?
??????????&&?val.constructor?!==?null?
??????????&&?!isUndefined(val.constructor)
??????????&&?typeof?val.constructor.isBuffer?===?'function'?
??????????&&?val.constructor.isBuffer(val);
}
什么是Buffer?
JavaScript 語言自身只有字符串數(shù)據(jù)類型,沒有二進制數(shù)據(jù)類型。
但在處理像TCP流或文件流時,必須使用到二進制數(shù)據(jù)。因此在 Node.js中,定義了一個Buffer 類,該類用來創(chuàng)建一個專門存放二進制數(shù)據(jù)的緩存區(qū)。詳細可以看 官方文檔[4] 或 更通俗易懂的解釋[5]。
因為axios可以運行在瀏覽器和node環(huán)境中,所以內(nèi)部會用到nodejs相關的知識。
3.4 isFormData 判斷FormData
//?`instanceof`?運算符用于檢測構(gòu)造函數(shù)的?`prototype`?屬性是否出現(xiàn)在某個實例對象的原型鏈上
function?isFormData(val)?{
??return?(typeof?FormData?!==?'undefined')?&&?(val?instanceof?FormData);
}
//?instanceof?用法
function?C()?{}
function?D()?{}
const?c?=?new?C()
c?instanceof?C?//?output:?true???因為?Object.getPrototypeOf(c)?===?C.prototype
c?instanceof?Object?//?output:?true???因為?Object.prototype.isPrototypeOf(c)
c?instanceof?D?//?output:?false???因為?D.prototype?不在?c?的原型鏈上
3.5 isObject 判斷對象
//?排除?`null`的情況
function?isObject(val)?{
??return?val?!==?null?&&?typeof?val?===?'object';
}
3.6 isPlainObject 判斷 純對象
純對象:用{}或new Object()創(chuàng)建的對象。
function?isPlainObject(val)?{
??if?(Object.prototype.toString.call(val)?!==?'[object?Object]')?{
????return?false;
??}
??var?prototype?=?Object.getPrototypeOf(val);
??return?prototype?===?null?||?prototype?===?Object.prototype;
}
//?例子1
const?o?=?{name:?'jay}
isPlainObject(o)?//?true
//?例子2
const?o?=?new?Object()
o.name?=?'jay'
isPlainObject(o)???//?true
//?例子3
function?C()?{}
const?c?=?new?C()
isPlainObject(c);??//?false
//?其實就是判斷目標對象的原型是不是`null`?或?`Object.prototype`
3.7 isDate 判斷Date
function?isDate(val)?{
??return?Object.prototype.toString.call(val)?===?'[object?Date]';
}
3.8 isFile 判斷文件類型
function?isFile(val)?{
??return?Object.prototype.toString.call(val)?===?'[object?File]';
}
3.9 isBlob 判斷Blob
function?isBlob(val)?{
??return?Object.prototype.toString.call(val)?===?'[object?Blob]';
}
Blob 對象表示一個不可變、原始數(shù)據(jù)的類文件對象。它的數(shù)據(jù)可以按文本或二進制的格式進行讀取。
3.10 isFunction 判斷函數(shù)
function?isFunction(val)?{
??return?Object.prototype.toString.call(val)?===?'[object?Function]';
}
3.11 isStream 判斷是否是流
//?這里`isObject`、`isFunction`為上文提到的方法
function?isStream(val)?{
??return?isObject(val)?&&?isFunction(val.pipe);
}
3.12 isURLSearchParams 判斷URLSearchParams
function?isURLSearchParams(val)?{
??return?typeof?URLSearchParams?!==?'undefined'?&&?val?instanceof?URLSearchParams;
}
//?例子
const?paramsString?=?"q=URLUtils.searchParams&topic=api"
const?searchParams?=?new?URLSearchParams(paramsString);
isURLSearchParams(searchParams)?//?true
URLSearchParams 接口定義了一些實用的方法來處理 URL 的查詢字符串,詳情可看 MDN[6]:
var?paramsString?=?"q=URLUtils.searchParams&topic=api"
var?searchParams?=?new?URLSearchParams(paramsString);
for?(let?p?of?searchParams)?{
??console.log(p);
}
//?輸出?
[?'q',?'URLUtils.searchParams'?]
[?'topic',?'api'?]
searchParams.has("topic")?===?true;?//?true
searchParams.get("topic")?===?"api";?//?true
searchParams.getAll("topic");?//?["api"]
searchParams.get("foo")?===?null;?//?true
searchParams.append("topic",?"webdev");
searchParams.toString();?//?"q=URLUtils.searchParams&topic=api&topic=webdev"
searchParams.set("topic",?"More?webdev");
searchParams.toString();?//?"q=URLUtils.searchParams&topic=More+webdev"
searchParams.delete("topic");
searchParams.toString();?//?"q=URLUtils.searchParams"
3.13 trim 去除首尾空格
//?`trim`方法不存在的話,用正則
function?trim(str)?{
??return?str.trim???str.trim()?:?str.replace(/^\s+|\s+$/g,?'');
}
3.14 isStandardBrowserEnv 判斷標準瀏覽器環(huán)境
function?isStandardBrowserEnv()?{
??if?(typeof?navigator?!==?'undefined'?&&?(navigator.product?===?'ReactNative'?||
???????????????????????????????????????????navigator.product?===?'NativeScript'?||
???????????????????????????????????????????navigator.product?===?'NS'))?{
????return?false;
??}
??return?(
????typeof?window?!==?'undefined'?&&
????typeof?document?!==?'undefined'
??);
}
但是官方已經(jīng)不推薦使用這個屬性navigator.product。

3.15 forEach 遍歷對象或數(shù)組
保留了英文注釋,提升大家的英文閱讀能力。
/**
?*?Iterate?over?an?Array?or?an?Object?invoking?a?function?for?each?item.
?*??用一個函數(shù)去迭代數(shù)組或?qū)ο?br>?*
?*?If?`obj`?is?an?Array?callback?will?be?called?passing
?*?the?value,?index,?and?complete?array?for?each?item.
?*?如果是數(shù)組,回調(diào)將會調(diào)用value,?index,?和整個數(shù)組
?*
?*?If?'obj'?is?an?Object?callback?will?be?called?passing
?*?the?value,?key,?and?complete?object?for?each?property.
?*?如果是對象,回調(diào)將會調(diào)用value,?key,?和整個對象
?*
?*?@param?{Object|Array}?obj?The?object?to?iterate
?*?@param?{Function}?fn?The?callback?to?invoke?for?each?item
?*/
?
function?forEach(obj,?fn)?{
??//?Don't?bother?if?no?value?provided
??//?如果值不存在,無需處理
??if?(obj?===?null?||?typeof?obj?===?'undefined')?{
????return;
??}
??//?Force?an?array?if?not?already?something?iterable
??//?如果不是對象類型,強制轉(zhuǎn)成數(shù)組類型
??if?(typeof?obj?!==?'object')?{
????obj?=?[obj];
??}
??if?(isArray(obj))?{
????//?Iterate?over?array?values
????//?是數(shù)組,for循環(huán)執(zhí)行回調(diào)fn
????for?(var?i?=?0,?l?=?obj.length;?i???????fn.call(null,?obj[i],?i,?obj);
????}
??}?else?{
????//?Iterate?over?object?keys
????//?是對象,for循環(huán)執(zhí)行回調(diào)fn
????for?(var?key?in?obj)?{
???????//?只遍歷可枚舉屬性
??????if?(Object.prototype.hasOwnProperty.call(obj,?key))?{
????????fn.call(null,?obj[key],?key,?obj);
??????}
????}
??}
}
所以,源碼為什么不用forEach和for...in...呢???????
3.16 stripBOM刪除UTF-8編碼中BOM
/**
?*?Remove?byte?order?marker.?This?catches?EF?BB?BF?(the?UTF-8?BOM)
?*
?*?@param?{string}?content?with?BOM
?*?@return?{string}?content?value?without?BOM
?*/
?
function?stripBOM(content)?{
??if?(content.charCodeAt(0)?===?0xFEFF)?{
????content?=?content.slice(1);
??}
??return?content;
}
所謂 BOM,全稱是Byte Order Mark,它是一個Unicode字符,通常出現(xiàn)在文本的開頭,用來標識字節(jié)序。UTF-8主要的優(yōu)點是可以兼容ASCII,但如果使用BOM的話,這個好處就蕩然無存了。
4.總結(jié)
本文主要介紹了axios源碼的調(diào)試過程,以及介紹了一些utils.js中的非常實用的工具函數(shù);相信通過閱讀源碼,日積月累,并把這些代碼或思想應用的自己項目中去,相信能夠很好的提升自己的編碼能力。
come on! worker!
同時也推薦一些好用的工具:
瀏覽器中運行`vscode`, 查看源碼[7]
代碼沙盒,能運行多種語言,且可以添加依賴[8]
vs code 的 code Runner插件[9]
參考資料
axios: https://github1s.com/axios/axios
[2]CONTRIBUTING.md: https://github1s.com/axios/axios/blob/HEAD/CONTRIBUTING.md
[3]utils.js: https://github.com/axios/axios/blob/master/lib/utils.js
官方文檔: http://nodejs.cn/api/buffer.html#buffer
[5]更通俗易懂的解釋: https://www.runoob.com/nodejs/nodejs-buffer.html
[6]MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/URLSearchParams
[7]瀏覽器中運行vscode, 查看源碼: https://github1s.com/axios/axios
代碼沙盒,能運行多種語言,且可以添加依賴: https://codesandbox.io/
[9]vs code 的 code Runner插件: https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner
往期干貨:
?26個經(jīng)典微信小程序+35套微信小程序源碼+微信小程序合集源碼下載(免費)
?干貨~~~2021最新前端學習視頻~~速度領取
?前端書籍-前端290本高清pdf電子書打包下載
點贊和在看就是最大的支持??
