最后
基础知识是前端一面必问的,如果你在基础知识这一块翻车了,就算你框架玩的再6,webpack、git、node学习的再好也无济于事,因为对方就不会再给你展示的机会,千万不要因为基础错过了自己心怡的公司。前端的基础知识杂且多,并不是理解就ok了,有些是真的要去记。当然了我们是牛x的前端工程师,每天像背英语单词一样去背知识点就没必要了,只要平时工作中多注意总结,面试前端刷下题目就可以了。
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
//流水线的第一个小组默认是dispatchRequest
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());
}
嘿嘿~感觉自己进过厂一样! 原谅我只能以这样的形式来描述了。
在demo中我们可以看到,我们并没有对原材料跟产品做任何处理:
created () {
axios.get('http://localhost:8090/home').then((res) => {
console.log(res)
}).catch((erro) => {
console.log(erro)
})
}
所以在request中不会走:
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);
});
对于interceptors我们之后再研究了,既然拦截器的代码不走了,我们就重点关注一下dispatchRequest函数,不管有没有拦截器,dispatchRequest方法都会默认执行的:
code/dispatchRequest.js
'use strict';
var utils = require('./../utils');
var transformData = require('./transformData');
var isCancel = require('../cancel/isCancel');
var defaults = require('../defaults');
var isAbsoluteURL = require('./../helpers/isAbsoluteURL');
var combineURLs = require('./../helpers/combineURLs');
/**
* Throws a `Cancel` if cancellation has been requested.
*/
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
/**
* Dispatch a request to the server using the configured adapter.
*
* @param {object} config The config that is to be used for the request
* @returns {Promise} The Promise to be fulfilled
*/
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// Support baseURL config
if (config.baseURL && !isAbsoluteURL(config.url)) {
config.url = combineURLs(config.baseURL, config.url);
}
// Ensure headers exist
config.headers = config.headers || {};
// Transform request data
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
// Flatten headers
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers || {}
);
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
// Transform response data
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
};
代码还是有点多,我们看重点:
module.exports = function dispatchRequest(config) {
.....
var adapter = config.adapter || defaults.adapter;
return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
// Transform response data
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
};
可以看到,最后直接执行了adapter(config)方法,然后返回了一个promise对象,那么adapter方法又是哪来的呢?
var adapter = config.adapter || defaults.adapter;
可以看到,我们demo中除了传递了一个url外根本就没有传递其它的参数,所以adapter就直接用的是 defaults.adapter,我们去找到它的源码。
defaults.js
'use strict';
var utils = require('./utils');
var normalizeHeaderName = require('./helpers/normalizeHeaderName');
var DEFAULT_CONTENT_TYPE = {
'Content-Type': 'application/x-www-form-urlencoded'
};
function setContentTypeIfUnset(headers, value) {
if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {
headers['Content-Type'] = value;
}
}
function getDefaultAdapter() {
var adapter;
// Only Node.JS has a process variable that is of [[Class]] process
if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// For node use HTTP adapter
adapter = require('./adapters/http');
} else if (typeof XMLHttpRequest !== 'undefined') {
// For browsers use XHR adapter
adapter = require('./adapters/xhr');
}
return adapter;
}
var defaults = {
adapter: getDefaultAdapter(),
transformRequest: [function transformRequest(data, headers) {
normalizeHeaderName(headers, 'Accept');
normalizeHeaderName(headers, 'Content-Type');
if (utils.isFormData(data) ||
utils.isArrayBuffer(data) ||
utils.isBuffer(data) ||
utils.isStream(data) ||
utils.isFile(data) ||
utils.isBlob(data)
) {
return data;
}
if (utils.isArrayBufferView(data)) {
return data.buffer;
}
if (utils.isURLSearchParams(data)) {
setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
return data.toString();
}
if (utils.isObject(data)) {
setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
return JSON.stringify(data);
}
return data;
}],
transformResponse: [function transformResponse(data) {
/*eslint no-param-reassign:0*/
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore */ }
}
return data;
}],
/**
* A timeout in milliseconds to abort a request. If set to 0 (default) a
* timeout is not created.
*/
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300;
}
};
defaults.headers = {
common: {
'Accept': 'application/json, text/plain, */*'
}
};
utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
defaults.headers[method] = {};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});
module.exports = defaults;
代码还是有点多,我们直接看adapter属性:
var defaults = {
adapter: getDefaultAdapter()
....
function getDefaultAdapter() {
var adapter;
// Only Node.JS has a process variable that is of [[Class]] process
if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// For node use HTTP adapter
adapter = require('./adapters/http');
} else if (typeof XMLHttpRequest !== 'undefined') {
// For browsers use XHR adapter
adapter = require('./adapters/xhr');
}
return adapter;
}
可以看到,在获取adapter的时候还做了环境的判断,如果是node环境的时候adapter=require(’./adapters/http’);如果是浏览器环境的时候adapter = require(’./adapters/xhr’);因为我们走的是浏览器环境,所以我们重点看adapter = require(’./adapters/xhr’);
adapters/xhr.js:
'use strict';
var utils = require('./../utils');
var settle = require('./../core/settle');
var buildURL = require('./../helpers/buildURL');
var parseHeaders = require('./../helpers/parseHeaders');
var isURLSameOrigin = require('./../helpers/isURLSameOrigin');
var createError = require('../core/createError');
module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
var requestData = config.data;
var requestHeaders = config.headers;
if (utils.isFormData(requestData)) {
delete requestHeaders['Content-Type']; // Let the browser set it
}
var request = new XMLHttpRequest();
// HTTP basic authentication
if (config.auth) {
var username = config.auth.username || '';
var password = config.auth.password || '';
requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
}
request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);
// Set the request timeout in MS
request.timeout = config.timeout;
// Listen for ready state
request.onreadystatechange = function handleLoad() {
if (!request || request.readyState !== 4) {
return;
}
// The request errored out and we didn't get a response, this will be
// handled by onerror instead
// With one exception: request that using file: protocol, most browsers
// will return status as 0 even though it's a successful request
if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
return;
}
// Prepare the response
var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
var response = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config: config,
request: request
};
settle(resolve, reject, response);
// Clean up request
request = null;
};
// Handle browser request cancellation (as opposed to a manual cancellation)
request.onabort = function handleAbort() {
if (!request) {
return;
}
reject(createError('Request aborted', config, 'ECONNABORTED', request));
// Clean up request
request = null;
};
// Handle low level network errors
request.onerror = function handleError() {
// Real errors are hidden from us by the browser
// onerror should only fire if it's a network error
reject(createError('Network Error', config, null, request));
// Clean up request
request = null;
};
// Handle timeout
request.ontimeout = function handleTimeout() {
reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',
request));
// Clean up request
request = null;
};
// Add xsrf header
// This is only done if running in a standard browser environment.
// Specifically not if we're in a web worker, or react-native.
if (utils.isStandardBrowserEnv()) {
var cookies = require('./../helpers/cookies');
// Add xsrf header
var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
cookies.read(config.xsrfCookieName) :
undefined;
if (xsrfValue) {
requestHeaders[config.xsrfHeaderName] = xsrfValue;
}
}
// Add headers to the request
if ('setRequestHeader' in request) {
utils.forEach(requestHeaders, function setRequestHeader(val, key) {
if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
// Remove Content-Type if data is undefined
delete requestHeaders[key];
} else {
// Otherwise add header to the request
request.setRequestHeader(key, val);
}
});
}
// Add withCredentials to request if needed
if (config.withCredentials) {
request.withCredentials = true;
}
// Add responseType to request if needed
if (config.responseType) {
try {
request.responseType = config.responseType;
} catch (e) {
// Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
// But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
if (config.responseType !== 'json') {
throw e;
}
}
}
// Handle progress if needed
if (typeof config.onDownloadProgress === 'function') {
request.addEventListener('progress', config.onDownloadProgress);
}
// Not all browsers support upload events
if (typeof config.onUploadProgress === 'function' && request.upload) {
request.upload.addEventListener('progress', config.onUploadProgress);
}
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}
if (requestData === undefined) {
requestData = null;
}
// Send the request
request.send(requestData);
});
};
哈哈~ 看到这里的时候小伙伴有没有很熟悉了呢? 没错!! 经过跋山涉水、历尽千辛万苦我们终于是看到了axios的真面目了,当然!axios不只是生产浏览器的产品,还生产node的产品,所以它的底层工厂还有一个叫http.js的(有兴趣的童鞋直自己去研究一下哈)。
好啦! 找了大半天,我们继续回到dispatchRequest.js中的dispatchRequest方法:
unction dispatchRequest(config) {
...
//获取XMLHttpRequest对象
var adapter = config.adapter || defaults.adapter;
//发送ajax请求,返回promise对象
return adapter(config).then(function onAdapterResolution(response) {
//返回请求数据
return response;
}, function onAdapterRejection(reason) {
//请求异常
return Promise.reject(reason);
});
通过回掉的方式拿到请求的返回值,最后回掉给客户端:
axios.get('http://localhost:8090/home').then((res) => {
console.log(res)
}).catch((erro) => {
console.log(erro)
})
好啦! 整个axios的工作流程我们简单的来回顾一下:
1、axios.get发出一个get请求(产品图纸设计)----->Axios.js
2、图纸给到工厂----->Axios.js(request方法)
3、分配流水线---->Axios.js(request方法)
4、(启动引擎adapter)产品制作---->dispatchRequest.js(dispatchRequest方法)
5、产品生产完毕----->dispatchRequest.js(dispatchRequest方法)
6、交互
到这里axios的主要源码我们算是已经分析完毕了,其它的一些(拦截器、node部分、文件流处理等等)我就不带着一起研究了哈,留个小伙伴自己慢慢看咯,我们接下来看一下axios的cancle机制(终止网络请求)
在官方文档中我们可以看到,如果我们要终止一个网络请求,我们需要执行:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
为什么要重点说一下axios的cancel呢?因为我觉得它这个实现机制还是挺不错的,至少我之前没这么写过(哈哈~~承认自己比较菜)
在说axios的cancel之前如果我们在一个普通的ajax请求,我们需要终止一个请求该这么办呢? 相信接触过ajax的小伙伴应该都知道,我们只需要执行xmlhttprequest对象的abort方法就OK了,所以我们直接反着来分析axios源码,我们直接找到xhr.js中的abort方法:
xhr.js
module.exports = function xhrAdapter(config) {
.....
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}
......
};
果然,跟我们的猜测一样,最后还是调用了xmlhttprequest对象的abort方法。
我们来用一用:
created () {
const CancelToken = axios.CancelToken
const source = CancelToken.source()
axios.get('http://localhost:8090/home',{cancelToken: source.token}).then((res) => {
console.log(res)
}).catch((erro) => {
console.log(erro)
})
source.cancel('看你不爽')
}
可以看到,我们直接在创建了一个网络请求后直接关闭,我们运行看一下结果:
我们一步一步的来分析:
首先我们获取了axios中的CancelToken函数:
const CancelToken = axios.CancelToken
CancelToken.js:
'use strict';
var Cancel = require('./Cancel');
/**
* A `CancelToken` is an object that can be used to request cancellation of an operation.
*
* @class
* @param {Function} executor The executor function.
*/
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
/**
* Throws a `Cancel` if cancellation has been requested.
*/
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
throw this.reason;
}
};
/**
* Returns an object that contains a new `CancelToken` and a function that, when called,
* cancels the `CancelToken`.
*/
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
module.exports = CancelToken;
代码不算多~~
我们接着直接执行了CancelToken的source方法:
created () {
const CancelToken = axios.CancelToken
const source = CancelToken.source()
axios.get('http://localhost:8090/home',{cancelToken: source.token}).then((res) => {
console.log(res)
}).catch((erro) => {
console.log(erro)
})
source.cancel('看你不爽')
}
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token,
cancel: cancel
};
};
可以看到,在CancelToken的source方法中,直接创建了一个CancelToken对象,然后在返回的对象中包含了token对象跟cancel方法:
return {
token: token,
cancel: cancel
};
我们首先看一下CancelToken的构造函数:
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
在CancelToken的构造函数中我们发现,创建一个promise对象,然后把promise对象的resolve方法传给了一个叫cancel的方法,在cancel方法中执行了resolve方法,最后把promise给了当前this(也就是返回的token对象)。
在demo中我们可以看到:
created () {
const CancelToken = axios.CancelToken
const source = CancelToken.source()
axios.get('http://localhost:8090/home',{cancelToken: source.token}).then((res) => {
console.log(res)
}).catch((erro) => {
console.log(erro)
})
source.cancel('看你不爽')
}
我们的cancel方法执行后请求就终止了:
source.cancel('看你不爽')
这里分享一份由字节前端面试官整理的「2021大厂前端面试手册」,内容囊括Html、CSS、Javascript、Vue、HTTP、浏览器面试题、数据结构与算法。全部整理在下方文档中,共计111道
HTML
-
HTML5有哪些新特性?
-
Doctype作⽤? 严格模式与混杂模式如何区分?它们有何意义?
-
如何实现浏览器内多个标签页之间的通信?
-
⾏内元素有哪些?块级元素有哪些? 空(void)元素有那些?⾏内元 素和块级元素有什么区别?
-
简述⼀下src与href的区别?
-
cookies,sessionStorage,localStorage 的区别?
-
HTML5 的离线储存的使用和原理?
-
怎样处理 移动端 1px 被 渲染成 2px 问题?
-
iframe 的优缺点?
-
Canvas 和 SVG 图形的区别是什么?
JavaScript
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
-
问:0.1 + 0.2 === 0.3 嘛?为什么?
-
JS 数据类型
-
写代码:实现函数能够深度克隆基本类型
-
事件流
-
事件是如何实现的?
-
new 一个函数发生了什么
-
什么是作用域?
-
JS 隐式转换,显示转换
-
了解 this 嘛,bind,call,apply 具体指什么
-
手写 bind、apply、call
-
setTimeout(fn, 0)多久才执行,Event Loop
-
手写题:Promise 原理
-
说一下原型链和原型链的继承吧
-
数组能够调用的函数有那些?
-
PWA使用过吗?serviceWorker的使用原理是啥?
-
ES6 之前使用 prototype 实现继承
-
箭头函数和普通函数有啥区别?箭头函数能当构造函数吗?
-
事件循环机制 (Event Loop)