前言
在项目开发中,我们经常要和bug打交道,会遇到很多各种各样的问题,然后花了九牛二虎之力解决了,这时候应该要总结下是怎么解决的,这样下次遇到可能会想起之前总结过,然后看下总结就知道怎么解决了,极大提高我们的开发效率。
下面我总结下在项目开发中学到的知识。
正文
块级作用域
块级作用域是es6新增的,一般使用大括号,在里面使用let,const声明的变量不会提升,区别于var声明。
比如for循环,每一次循环都是块级作用域,外界无法访问。
const arr = [1, 2, 3]
for (let i = 0; i < arr.length; i++) {
setTimeout(() => {
console.log(i)
}, 0)
}
上面的输出应该很容易知道,按顺序输出0,1,2。如果我再改造一下,你看看输出是什么?
const arr = [1, 2, 3]
let i = 0
for (; i < arr.length; i++) {
setTimeout(() => {
console.log(i)
}, 0)
}
没错,是输出了3个3。
你会不会有疑问,为什么我把for循环的开始声明移到外面就不一样了?
这是因为for循环的开始声明移到外面后,它就不属于块级作用域了,而是属于全局作用域。
当遍历结束后,i 已经变成3了
因此,执行setTimeout的时候输出3个3。你如果这时候在外面打印i,也是打印的3。
setTimeout的第三个参数
在js里面,我们要延时的时候,一般都会用setTimeout函数。第一个参数是执行函数,第二个参数是延时的时长,毫秒为单位。
setTimeout(() =>{
console.log('hello world')
}, 1000)
这个例子很普通也很常见,延时1秒打印hello world。
可是你知道setTimeout还支持第三个参数吗?
我们可以通过第三个参数来给执行函数传参。
setTimeout((text) =>{
console.log(text)
}, 1000, 'hello world')
结果也和上面一样,延时1秒打印hello world。
但是这个是通过传参实现的。
上面块级作用域输出3个3的例子,稍微再改造一下就可以输出0,1,2。
const arr = [1, 2, 3]
let i = 0
for (; i < arr.length; i++) {
setTimeout((j) => {
console.log(j)
}, 0, i)
}
虽然i是全局作用域,但是每次遍历都绑定了参数,不受全局作用域影响,按顺序输出0,1,2。
base64转成File对象
我们有时候会拿到图片的base64编码,这时候需要上传到后台该怎么办?不能直接传base64,后台需要的是File对象。
别急,这时候我们可以把我们的base64转成File对象。
/**
* 把base64转成File对象
* @param {string} base64Data base64数据
* @param {name} name File对象的名称
* @returns File对象
*/
function base64ToFile (base64Data, name) {
const dataArr = base64Data.split(',')
const type = dataArr[0].match(/:(.*);/)[1]
const byteString = window.atob(dataArr[1])
const buffer = new ArrayBuffer(byteString.length)
const arr = new Uint8Array(buffer)
for (let i = 0; i < byteString.length; i++) {
arr[i] = byteString.charCodeAt(i)
}
return new File([buffer], name, { type })
}
我们先来看看base64的格式
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA1kAAAFzCAYAAADMqcNKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAA...省略
我们需要把把逗号分隔,前面那部分提取出文件的类型type,后面那部分转成buffer,然后再把buffer转成File对象。
这里用到了window.atob,这个方法是用来解码base64的数据
与之对应的还有一个window.btoa,这个方法是编码base64的数据
window.btoa('cp3') // 'Y3Az'
window.atob('Y3Az') // 'cp3'
解码后的字符串拿到码点转成buffer,这里要调用charCodeAt方法,拿到对应的码点,赋值给buffer数组。
最后调用new File生成File对象。
axios的超时全局设置和单个设置
axios我们一般会全局统一设置超时
// 统一设置超时10s
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000
});
// 发起get请求
instance.get('/xxx',{
params:{name: 'xxx'}
})
// 发起post请求
instance.post('/xxx',{name: 'xxx'})
如果我们需要在某个请求改成20s,那该怎么改?
axios早就帮我们想到了,我们可以在配置项加上超时的时间,就会覆盖统一设置的超时。
get是第二个参数,post是第三个参数,要看你们请求方式的config对应位置。
instance.get('/xxx',{
params:{name: 'xxx'},
timeout: 20000
})
instance.post('/xxx', {
name: 'xxx'
}, {
timeout: 20000
})
各个请求对应的config位置:
部分源码展示
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, config) {
return this.request(mergeConfig(config || {}, {
method,
url,
data: (config || {}).data
}));
};
});
request(configOrUrl, config) {
/*eslint no-param-reassign:0*/
// Allow for axios('example/url'[, config]) a la fetch API
if (typeof configOrUrl === 'string') {
config = config || {};
config.url = configOrUrl;
} else {
config = configOrUrl || {};
}
config = mergeConfig(this.defaults, config); // 合并
// 省略代码
}
可以看到,'delete', 'get', 'head', 'options' 方法内部都是调用request方法去实现的,其他方法也是如此。
request方法内部是调用mergeConfig方法合并参数,会用config的timeout覆盖defaults里面的timeout。
总结
上面就是最近项目开发中总结的知识,小知识慢慢积累,也会变成大知识,继续加油!