经常调接口的同学一定非常熟悉aixos下面的两种使用方式:
axios(config)
// 配置式请求``axios({`` ``method: ``'post'``,`` ``url: ``'/user/12345'``,``});
axios.post(url, config)
// 简洁的写法``axios.post(``'/user/12345'``)
不知道各位大佬有没有思考过这样的问题:
axios到底是个什么东西?我们为什么可以使用这两种方式请求接口呢?axios是怎么设计的?
axios原理简析
为了搞明白上面的问题,我们先按照传统思路仿照axios源码实现一个简单的axios。
手写一个简单的axios
创建一个构造函数
function Axios(config){`` ``this``.defaults = config; ``// 配置对象`` ``this``.interceptors = { ``// 拦截器对象`` ``request:{},`` ``response:{}`` ``}``}
上面的代码中,我们实现了一个基本的Axios类,但它还不具备任何功能。我们现在给它添加功能。
原型上添加方法
Axios.prototype.request = function(config){`` ``console.log(``'发送Ajax 请求 type:'` `+config.method)``}``Axios.prototype.``get` `= function(){`` ``return` `this``.request({method:``'GET'``})``}``Axios.prototype.post = function(){`` ``return` `this``.request({method: ``'POST'``})``}
上面的代码中,我们在request属性上创建了一个通用的接口请求方法,get和post实际都调用了request,但内部传递了不同的参数,这和axios(config)、axios.post()有异曲同工之妙。
参考aixos的用法, 现在,我们需要创建实例对象
let` `aixos = ``new` `Axios(config)
创建后的axios包含defaults
和interceptors
属性,其对象原型__proto__
上(指向Axios的prototype)包含request、get及post方法,因此,我们现在可以使用aixos.post()
的方法模拟调用接口了。
但注意,此时aixos只是一个实例对象,不是一个函数!我们似乎也没办法做到改造代码使用aixos(config)
的形式调用接口!
aixos是如何实现的呢?
aixos中的天才想法
为了即能使用axios(config)又能使用axios.get(),axios的核心伪逻辑如下
function Axios(config){`` ``this``.defaults = config; ``// 配置对象`` ``this``.interceptors = { ``// 拦截器对象`` ``request:{},`` ``response:{}`` ``}``}` `Axios.prototype.request = function(config){`` ``console.log(``'发送Ajax 请求 type:'` `+config.method)``}``Axios.prototype.``get` `= function(){`` ``return` `this``.request({method:``'GET'``})``}``Axios.prototype.post = function(){`` ``return` `this``.request({method: ``'POST'``})``}` `function createInstance(config) {`` ``//注意instance是函数`` ``const` `instance = Axios.prototype.request;`` ``instance.``get` `= Axios.prototype.``get`` ``instance.post = Axios.prototype.post`` ``return` `instance;`` ``}` `let` `axios = createInstance();
通过上述的伪代码,我们可以知道axios是createInstance()函数的返回值instance。
instance 是一个函数,因此,axios也是一个函数,可以使用axios(config); instance也是一个对象(js万物皆对象),其原型上有get方法和post方法,因此,我们可以使用axios.post()。
我们看看aixos的源码
aixos的源码实现
function createInstance(config) {`` ``//实例化一个对象`` ``var` `context = ``new` `Axios(config); ``//但是不能直接当函数使用`` ` ` ``var` `instance = Axios.prototype.request.bind(context);`` ``//instance 是一个函数,并且可以 instance({}),` ` ``//将Axios.prototype 对象中的方法添加到instance函数中,让instance拥有get、post、request等方法属性`` ``Object.keys(Axios.prototype).forEach(key => {`` ``// console.log(key); //修改this指向context`` ``instance[key] = Axios.prototype[key].bind(context);`` ``})`` ``//总结一下,到此instance自身即相当于Axios原型的request方法,`` ``//然后又给instance的属性添加了上了Axios原型的request、get、post方法属性`` ``//然后调用instance自身或instance的方法属性时,修改了this指向context这个Axios实例对象` ` ``//为instance函数对象添加属性 default 与 intercetors`` ``Object.keys(context).forEach(key => {`` ``instance[key] = context[key];`` ``})` ` ``return` `instance;``}
可以说,上面的代码真的写的精妙绝伦啊!
注意这里,为什么要修改this的指向
var` `instance = Axios.prototype.request.bind(context);
首先,requset 是Axios原型对象上的方法,其方法内部的this指向的是其实例化对象context!
Axios.prototype.request = function request(config) {`` ``/*eslint no-param-reassign:0*/`` ``// Allow for axios('example/url'[, config]) a la fetch API`` ``if` `(``typeof` `config === ``'string'``) {`` ``config = arguments[1] || {};`` ``config.url = arguments[0];`` ``} ``else` `{`` ``config = config || {};`` ``}` ` ``config = mergeConfig(``this``.defaults, config);` ` ``// Set config.method`` ``if` `(config.method) {`` ``config.method = config.method.toLowerCase();`` ``} ``else` `if` `(``this``.defaults.method) {`` ``config.method = ``this``.defaults.method.toLowerCase();`` ``} ``else` `{`` ``config.method = ``'get'``;`` ``}` ` ``// Hook up interceptors middleware`` ``var` `chain = [dispatchRequest, undefined];`` ``var` `promise = Promise.resolve(config);` ` ``this``.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {`` ``chain.unshift(interceptor.fulfilled, interceptor.rejected);`` ``});` ` ``this``.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {`` ``chain.push(interceptor.fulfilled, interceptor.rejected);`` ``});` ` ``while` `(chain.length) {`` ``promise = promise.then(chain.shift(), chain.shift());`` ``}` ` ``return` `promise;``};
因此,如果我们直接使用Axios.prototype.request()
就会出现问题,因为这事reques方法内部的this会指向错误,导致函数不能运行,因此,我们必须将this重新指向其实例化对象。
作者:林恒
https://www.cnblogs.com/smileZAZ/p/18330636
加我微信,拉你进前端进阶、面试交流群,互相监督学习进步等!
❤️ 看完三件事
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)
关注我的博客 https://github.com/qappleh/Interview,让我们成为长期关系
关注公众号「深圳湾码农」,持续为你推送精选好文,回复「加群」加入面试互助交流群
点一下,代码无 Bug