共计 14888 个字符,预计需要花费 38 分钟才能阅读完成。
“小马同学,背一下《陋室铭》。”“山不在高,有仙则名。水不在深,有龙则灵。斯是陋室,惟馨。”“停,怎么少了俩字?”“年轻人不讲吾德。”
系列文章汇总:
- 前端装逼技巧 108 式(一)—— 打工人
- 前端装逼技巧 108 式(二)—— 不讲武德
文章格调所限,援用材料局部,将在对应大节开端标出。
第十九式:西施宜笑复宜颦,丑女效之徒累身 —— window.btoa
、window.atob
,命名这么随便的 API 能够用来干什么?
单从命名来看,齐全让人摸不着头脑的两个 API,咱们到底能够用他们来干些什么呢?(我甚至狐疑,如果在我的项目中应用这样的命名,齐全可能被共事打,哈哈)
window.atob()
函数用来解码一个曾经被 base-64 编码过的数据。你能够应用window.btoa()
办法来 编码一个可能在传输过程中呈现问题的数据,并且在承受数据之后,应用window.atob()
办法来将数据解码。例如:你能够把 ASCII 外面数值 0 到 31 的控制字符进行编码,传输和解码。window.btoa()
:将 ASCII 字符串或二进制数据转换成一个 base64 编码过的字符串,该办法不能间接作用于 Unicode 字符串。- 在各浏览器中, 应用
window.btoa
对 Unicode 字符串进行编码都会触发一个字符越界的异样。 - 前端能够应用这两个 API 对 URL 路由参数、敏感信息等进行转码,避免明文裸露。
let encodedData = window.btoa("Hello, world"); // 编码
console.log(encodedData); // SGVsbG8sIHdvcmxk
let decodedData = window.atob(encodedData); // 解码
console.log(decodedData); // Hello, world
let encodeUTF = window.btoa(encodeURIComponent('啊'));
console.log(encodeUTF); // JUU1JTk1JThB
let decodedUTF = decodeURIComponent(atob(encodeUTF));
console.log(decodedUTF); // 啊
材料参考:window.atob()与 window.btoa()办法实现编码与解码 | WindowOrWorkerGlobalScope.atob() | WindowOrWorkerGlobalScope.btoa()
第二十式:escape
、encodeURI
、encodeURIComponent
,这些编码 API 有什么区别?
escape
是对字符串 (string) 进行编码 (而另外两种是对 URL),作用是让它们在所有电脑上可读。编码之后的成果是%XX
或者%uXXXX
这种模式。其中 ASCII 字母、数字、@*/+
,这几个字符不会被编码,其余的都会。最要害的是,当你须要对 URL 编码时,请遗记这个办法,这个办法是针对字符串应用的,不适用于 URL;encodeURI
和encodeURIComponent
都是编码 URL,惟一区别就是编码的字符范畴;encodeURI
办法不会对下列字符编码:ASCII 字母、数字、~!@#$&*()=:/,;?+'
;encodeURIComponent
办法不会对下列字符编码:ASCII 字母、数字、~!*()'
;- 也就是
encodeURIComponent
编码的范畴更广 ,会将http://XXX
中的//
也编码,会导致 URL 不可用。(其实 java 中的URLEncoder.encode(str,char)
也相似于这个办法,会导致 URL 不可用)。 -
应用场景:
- 如果只是编码字符串,不和 URL 有半毛钱关系,那么用
escape
,而且这个办法个别不会用到; - 如果你须要编码整个 URL,而后须要应用这个 URL,那么用
encodeURI
; - 当你须要编码 URL 中的参数的时候,那么
encodeURIComponent
是最好办法; - 某些场景下,编码之后导致 URL 不可用(比方笔者曾遇到预览附件时某些附件 URL 无奈关上的问题),可尝试思考是否是因为特殊字符导致的。
- 如果只是编码字符串,不和 URL 有半毛钱关系,那么用
- 如果不失效能够用两次编码:对于两次编码的起因
材料参考:escape、encodeURI 和 encodeURIComponent 的区别
第二十一式:这不是我拜访的页面 —— 常常碰到挪动端 DNS 域名劫持问题?来一起理解下 HTTPDNS 是什么,解决了什么问题吧
对于互联网,域名是拜访的第一跳,而这一跳很多时候会“失足”(尤其是挪动端网络),导致拜访谬误内容、失败连贯等,让用户在互联网上畅游的痛快霎时隐没。凡是应用域名来给用户提供服务的互联网企业,都或多或少地无奈防止在有中国特色的互联网环境中遭逢到各种域名被缓存、用户跨网拜访迟缓等问题。
- DNS 解析过程:
- 什么 HttpDNS:
HTTPDNS 利用 HTTP 协定与 DNS 服务器交互,代替了传统的基于 UDP 协定的 DNS 交互,绕开了运营商的 Local DNS,无效避免了域名劫持,进步域名解析效率 。另外,因为 DNS 服务器端获取的是实在客户端 IP 而非 Local DNS 的 IP, 可能精确定位客户端地理位置、运营商信息,从而无效改良调度精确性。
-
HttpDNS 次要解决的问题:
- Local DNS 劫持:因为 HttpDns 是通过 IP 间接申请 HTTP 获取服务器 A 记录地址,不存在向本地运营商询问 domain 解析过程,所以从基本防止了劫持问题。
- 均匀拜访提早降落:因为是 IP 间接拜访省掉了一次 domain 解析过程,通过智能算法排序后找到最快节点进行拜访。
- 用户连贯失败率降落:通过算法升高以往失败率过高的服务器排序,通过工夫近期拜访过的数据进步服务器排序,通过历史拜访胜利记录进步服务器排序。
-
HttpDNS 的原理
- 客户端 间接拜访 HttpDNS 接口,获取业务在域名配置管理系统上配置的拜访提早最优的 IP。(基于容灾思考,还是保留次选应用运营商 LocalDNS 解析域名的形式);
- 客户端获取到 IP 后就间接向此 IP 发送业务协定申请。以 Http 申请为例,通过在 header 中指定 host 字段,向 HttpDNS 返回的 IP 发送规范的 Http 申请即可。
详细资料参考:全面理解挪动端 DNS 域名劫持等杂症:原理、本源、HttpDNS 解决方案等
第二十二式:depcheck
一下你的前端我的项目中是否存在未应用的依赖包
很多时候,兴许咱们的前端我的项目是基于某个已有的我的项目进行”复制搭建“,或者间接应用 UmiJS 这样的企业级 react 利用框架,又或者基于 Ant Design Pro 等开源我的项目进行删改,难免会存在未应用到的依赖包被装置,连累我的项目装置速度,增大我的项目打包体积等,这时,咱们就能够思考应用 depcheck
找出那些未应用的依赖包进行移除。
npm install depcheck -g
-
cd 到要查看的我的项目目录,运行 depcheck
D:\project>depcheck Unused devDependencies #未应用的依赖 * @antv/data-set * echarts * echarts-for-react * qs * Unused devDependencies #未应用的 devDependencies * chalk * enzyme * express Missing dependencies #短少的 dependencies * immutability-helper: .\src\components\EditColums\EditColumnsTable.js * slash2: .\config\config.js
UmiJS 学习参考:UmiJS | [react]初识 Umi.JS
第二十三式:避免误操作,如何在组件卸载、路由跳转、页面敞开(刷新)之前进行提醒
工作中常常会有大表单填写、提交这样的需要,如果用户写了大量内容,因为误操作,刷新或者敞开了页面,填写信息用没有做缓存,此时用户的心田应该是奔溃的。
React 组件卸载、路由跳转、页面敞开(刷新)之前进行提醒(如果是 AntD Modal 弹窗外面的表单,也可视情思考将 maskClosable
属性设置为 false,避免正点蒙层导致弹窗敞开):
// 监听窗口事件
useEffect(() => {const listener = (ev) => {ev.preventDefault();
ev.returnValue = '确定来到吗?';
};
window.addEventListener('beforeunload', listener);
return () => {
// 在开端处返回一个函数
// React 在该函数组件卸载前调用该办法(实现 componentWillUnmount)window.removeEventListener('beforeunload', listener);
};
}, []);
第二十四式:不带括号也能执行函数调用?console.log\`hello world\` 会打印出什么
- 间接看后果:
console.log`hello world` // 打印出一个数组:["hello world", raw: Array(1)]
- 再看看以下代码:
const name = 'jack'
const gender = false
// 带括号
console.log(`hey, ${name} is a ${gender ? 'girl' : 'boy'}.`) // hey, jack is a boy.
// 不带括号
console.log`hey, ${name} is a ${gender ? 'girl' : 'boy'}.` // ["hey,", "is a", ".", raw: Array(3)] 'jack' 'boy'
从最初一行打印能够看出数组中的项是以 ’ 插入表达式 ’ 作为宰割生成的,并且插入表答式中的内容参数,也会顺次打印进去。这就是 带标签的模板字符串。
- 模板字符串的语法:
// 一般
`string text`
// 换行
`string text line 1
string text line 2`
// 插值
`string text ${expression} string text`
// 带标签的模板字符串
tag `string text ${expression} string text`
- 能够做什么:
const name = 'jack'
const gender = false
function myTagFunc(strings, name, gender) {
const sex = gender ? 'girl' : 'boy'
// return 'hello world'
return strings[0] + name + strings[1] + sex + strings[2]
}
// result 的值是 myTagFunc 函数的返回值
// 如果 myTagFunc 返回 hello world,result 就是 hello world
// 这样可在肯定水平上防止在模板字符串内写简单的逻辑
const result = myTagFunc`hey, ${name} is a ${gender}.`
console.log(result) // hey, jack is a boy.
在标签函数的第一个参数中,存在一个非凡的属性 raw,咱们能够通过它来拜访模板字符串的原始字符串,而不通过特殊字符的替换。
function tag(strings) {console.log(strings.raw[0]);
}
tag`string text line 1 \n string text line 2`;// "string text line 1 \n string text line 2"
console.log`string text line 1 \n string text line 2` // ["string text line 1 ↵ string text line 2", raw: Array(1)]
参考资料:MDN- 带标签的模板字符串 | 带标签的模板字符串
第二十五式:还在用闭包实现自增 Id?何不试试优雅大气的 Generator?
- 闭包
function createIdMaker(){
let id = 1;
return function (){return id ++;}
}
const idMaker = createIdMaker();
console.log(idMaker()) // 1
console.log(idMaker()) // 2
console.log(idMaker()) // 3
- Generator
function * createIdMaker() {
let id = 1
while(true) yield id ++;
}
const idMaker = createIdMaker()
console.log(idMaker.next().value) // 1
console.log(idMaker.next().value) // 2
console.log(idMaker.next().value) // 3
第二十六式:年轻人不讲武德,谁动了我的对象 —— 对象属性会本人偷偷排队?
程序员眼里只有女神,对象是不会有的,就算有,还能无可挑剔、唯命是从不成?缺对象那就 new 一个咯,个性化定制,相对的现实型。控制不了事实里的对象还能控制不了 new 进去的对象了?事实上,你,真的不能。
- 试想一下,上面的代码会依照什么程序输入:
function Foo() {this[200] = 'test-200';
this[1] = 'test-1';
this[100] = 'test-100';
this['B'] = 'bar-B';
this[50] = 'test-50';
this[9] = 'test-9';
this[8] = 'test-8';
this[3] = 'test-3';
this[5] = 'test-5';
this['D'] = 'bar-D';
this['C'] = 'bar-C';
}
var bar = new Foo();
for (key in bar) {console.log(`index:${key} value:${bar[key]}`);
}
在 ECMAScript 标准中定义了 数字属性应该依照索引值大小升序排列,字符串属性依据创立时的程序升序排列 。咱们把对象中的数字属性称为 排序属性 ,在 Chrome V8 引擎 中被称为 elements,字符串属性就被称为 惯例属性 ,在 V8 中被称为 properties。在 V8 外部,为了无效地晋升存储和拜访这两种属性的性能,别离应用了两个线性数据结构来别离保留排序属性和惯例属性。同时 v8 将局部惯例属性间接存储到对象自身,咱们把这称为 对象内属性 (in-object properties),不过对象内属性的数量是固定的,默认是 10 个。更多详情可参考笔者之前的一篇文章浏览器是如何工作的:Chrome V8 让你更懂 JavaScript ——【V8 外部是如何存储对象的:快属性和慢属性】一节。
- 后果揭晓
// 输入:// index:1 value:test-1
// index:3 value:test-3
// index:5 value:test-5
// index:8 value:test-8
// index:9 value:test-9
// index:50 value:test-50
// index:100 value:test-100
// index:200 value:test-200
// index:B value:bar-B
// index:D value:bar-D
// index:C value:bar-C
材料参考:浏览器是如何工作的:Chrome V8 让你更懂 JavaScript
第二十七式:VS Code 里居然有谷歌开发者工具面板?它 和 Chrome 有什么关系?
如下图所示,咱们常常用的开发工具 VSCode 竟与浏览器如此相像,莫非他们是失散多年的兄弟?诶,你还别说,还真有那么点意思。(点击帮忙【Help】下的 切换开发人员工具即可关上以下面板)
VS Code 是基于 Electron (原来叫 Atom Shell) 进行开发的。Electron 基于 Node.js(作为后端运行时)和 Chromium(作为前端渲染),使得开发者能够应用 HTML, CSS 和 JavaScript 等前端技术来开发跨平台桌面 GUI 应用程序。Atom, GitHub Desktop, Slack, Microsoft Teams, WordPress Desktop 等出名软件都是基于 Electron 开发的。Electron 比你设想的更简略,如果你能够建一个网站,你就能够建一个桌面应用程序。
VS Code 的其余的次要组件有:
- 壳:Monaco Editor
- 内核:Language Server Protocol(一个代码编辑器)
- Debug Adapter Protocol
- Xterm.js
参考资料:vs code 的界面是用的什么技术?| Electron | Electron 疾速入门
第二十八式:”★★★★★☆☆☆☆☆”.slice(5 – rate, 10 – rate) —— 一个正经又有点正气的组件封装
开始看到这行评级 rate 组件的代码,是在一篇充斥正气的文章信条|手撕吊打面试官系列面试题里,总感觉这个组件与那篇文章的文风不对应,甚至感觉这个实现还足够机智,值得借鉴,我是不是没救了,哈哈。
{
let rate = 3;
"★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate);
}
参考资料:信条|手撕吊打面试官系列面试题
第二十九式:Uncaught TypeError: obj is not iterable
,for of
遍历一般对象报错,如何疾速使一般对象可被 for of
遍历?
for of
能够迭代 Arrays(数组), Maps(映射), Sets(汇合)、NodeList 对象、Generator 等,甚至连 Strings(字符串)都能够迭代,却不能遍历一般对象?
// 字符串
const iterable = 'ES6';
for (const value of iterable) {console.log(value);
}
// Output:
// "E"
// "S"
// "6"
// 一般对象
const obj = {
foo: 'value1',
bar: 'value2'
}
for(const item of obj){console.log(item)
}
// Uncaught TypeError: obj is not iterable
咱们先从对象的几个办法 Object.values()
、Object.keys()
、Object.entries()
看起吧:
const obj = {
foo: 'value1',
bar: 'value2'
}
// 打印由 value 组成的数组
console.log(Object.values(obj))
// 打印由 key 组成的数组
console.log(Object.keys(obj))
// 打印由 [key, value] 组成的二维数组
console.log(Object.entries(obj))
// 办法一:应用 of 遍历一般对象的办法
for(const [, value] of Object.entries(obj)){console.log(value)
}
// 一般对象转 Map
console.log(new Map(Object.entries(obj)))
// 办法二:遍历一般对象生成的 Map
for(const [, value] of new Map(Object.entries(obj))){console.log(value)
}
一般对象为何不可被 for of
迭代请参考下一式。
第三十式:能够遍历绝大部分数据类型的 for of
为什么不能遍历一般对象?
- 一般对象为何不可被
for of
迭代
{
// 数组
const iterable = ['a', 'b'];
for (const value of iterable) {console.log(value);
}
// Output:
// a
// b
}
{// Set(汇合)
const iterable = new Set([1, 2, 2, 1]);
for (const value of iterable) {console.log(value);
}
// Output:
// 1
// 2
}
{// Arguments Object(参数对象)
function args() {for (const arg of arguments) {console.log(arg);
}
}
args('a', 'b');
// Output:
// a
// b
}
能够看到,这些可被 for of
迭代的对象,都实现了一个 Symbol(Symbol.iterator)
办法,而一般对象没有这个办法。
简略来说,for of
语句创立一个循环来迭代可迭代的对象,可迭代的对象外部实现了 Symbol.iterator
办法,而一般对象没有实现这一办法,所以一般对象是不可迭代的。
- 如何实现
Symbol.iterator
办法,使一般对象可被for of
迭代
// 一般对象
const obj = {
foo: 'value1',
bar: 'value2',
[Symbol.iterator]() {// 不必放心 [Symbol.iterator] 属性会被 Object.keys()获取到,// Symbol.iterator 须要通过 Object.getOwnPropertySymbols(obj)获取,// Object.getOwnPropertySymbols() 办法返回一个给定对象本身的所有 Symbol 属性的数组。const keys = Object.keys(obj);
let index = 0;
return {next: () => {if (index < keys.length) {
return {value: this[keys[index++]],
done: false
};
} else {return { value: undefined, done: true};
}
}
};
}
}
for (const value of obj) {console.log(value); // value1 value2
}
下面给 obj 实现了 Symbol.iterator
接口后,咱们甚至还能够像上面这样把对象转换成数组:
console.log([...obj]); // ["value1", "value2"]
console.log([...{}]); // console.log is not iterable (cannot read property Symbol(Symbol.iterator))
更多对于 for...of
的探讨,可参考笔者的一文带你了解:能够迭代大部分数据类型的 for…of 为什么不能遍历一般对象?。
参考资料:MDN:for…of | Understanding the JavaScript For…of Loop(【译文】)| Iterator 和 for…of 循环
第三十一式:position 定位只晓得 absolute
、fixed
、relative
和 static
?,sticky
其实能够很惊艳
- absolute:生成相对定位的元素,绝对于 static 定位以外的第一个父元素进行定位。元素的地位通过 “left”, “top”, “right” 以及 “bottom” 属性进行规定。
- fixed:生成相对定位的元素,绝对于浏览器窗口进行定位。元素的地位通过 “left”, “top”, “right” 以及 “bottom” 属性进行规定。
- relative:生成绝对定位的元素,绝对于其失常地位进行定位。因而,”left:20″ 会向元素的 LEFT 地位增加 20 像素。
- static:默认值。没有定位,元素呈现在失常的流中。
- sticky:粘性定位 ,该定位基于用户滚动的地位。当元素在屏幕内,它的行为就像
position:relative;
,而 当页面滚动超出指标区域时,它的体现就像position:fixed;
,它会固定在指标地位。
position:sticky
实现的惊艳吸顶成果可点击这里。
// 用法:nav 元素实现粘性定位
nav {
position: -webkit-sticky;
position: sticky;
top: 0;
}
应用 position:sticky
的过程中,兴许会有一些坑,比方要想 sticky 失效,top 属性或则 left 属性(看滚动方向)是必须要有明确的计算值的,否则 fixed 的体现不会呈现。详情可参考《CSS 世界》作者张鑫旭大佬的杀了个回马枪,还是说说 position:sticky 吧。
参考资料:CSS position 属性 | 杀了个回马枪,还是说说 position:sticky 吧
第三十二式:傍能行仁义,莫若妾自知 —— getBoundingClientRect
让你找准定位不迷失自我
- 什么是
getBoundingClientRect
Element.getBoundingClientRect()
办法,用来形容一个元素的具体位置,该地位的四个属性都是绝对于视口左上角的地位而言的。对某一节点执行该办法,它的返回值是一个 DOMRect 类型的对象。这个对象示意一个矩形盒子,它含有:left、top、right 和 bottom 等只读属性,具体含意如下图所示:
-
offset 和 getBoundingClientRect() 区别
- offset 指偏移,包含这个元素在文档中占用的所有显示宽度,包含滚动条、padding、border,不包含 overflow 暗藏的局部;
- offset 的方向值须要思考到父级,如果父级是定位元素,那么子元素的 offset 值绝对于父元素;如果父元素不是定位元素,那么子元素的 offset 值绝对于可视区窗口;
-
offsetParent
:获取以后元素的 定位父元素:- 如果以后元素的父元素,有 CSS 定位 (position 为 absolute、relative、fixed),那么
offsetParent
获取的是 最近的 那个父元素。 - 如果以后元素的父元素,没有 CSS 定位(position 为 absolute、relative、fixed),那么
offsetParent
获取的是body。
- 如果以后元素的父元素,有 CSS 定位 (position 为 absolute、relative、fixed),那么
- getBoundingClientRect() 的值只绝对于可视区窗口,所以在很多场景下更容易“找准定位”。
- 能做什么:滚动吸顶成果
笔者写此节之前有做过一个表格分页器固定在浏览器底部、表头滚动吸顶的成果,次要参考了 position:sticky
属性和 getBoundingClientRect
。写此节查阅材料时有发现【前端词典】5 种滚动吸顶实现形式的比拟(性能升级版)这篇文章,对五种吸顶形式做了详尽的剖析和比照,大家有趣味能够看看。同时,《CSS 世界》作者张鑫旭大佬在杀了个回马枪,还是说说 position:sticky 吧对sticky
定位也有详尽的介绍。原本还想在后续的章节谈谈吸顶,当初可能须要从新评估了,哈哈。
滚动吸顶表格示例:
【前端词典】5 种滚动吸顶实现形式的比拟(性能升级版)一文中的 getBoundingClientRect
吸顶实现:
// html
<div class="pride_tab_fixed" ref="pride_tab_fixed">
<div class="pride_tab" :class="titleFixed == true ?'isFixed':''">
// some code
</div>
</div>
// vue
export default {data(){
return{titleFixed: false}
},
activated(){
this.titleFixed = false;
window.addEventListener('scroll', this.handleScroll);
},
methods: {
// 滚动监听,头部固定
handleScroll: function () {let offsetTop = this.$refs.pride_tab_fixed.getBoundingClientRect().top;
this.titleFixed = offsetTop < 0;
// some code
}
}
}
参考资料:getBoundingClientRect 办法 | 杀了个回马枪,还是说说 position:sticky 吧 |【前端词典】5 种滚动吸顶实现形式的比拟【性能升级版】| JS 中的 offset、scroll、client 总结
第三十三式:Console Importer
让你的浏览器控制台成为更弱小的实验场
平时开发中,咱们常常会在控制台尝试一些操作,Console Importer 是一个能够在 Chrome Console 面板装置(引入)loadsh、moment、jQuery、React 等资源的插件,语法也很简略,比方 $i('moment')
即可引入 moment 库,而后即可在控制台间接验证、应用这些库:
- 应用示例:
- 效果图:
- 引入资源办法:
$i('jquery') // 间接引入
$i('jquery@2') // 指定版本
$i('https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js') // cdn 地址
$i('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css') // 引入 CSS
链接:Console Importer | Chrome Web Store 地址
第三十四式:误用git reset --hard
,我真的没救了吗?—— 认识一下 git reflog
时光穿梭机
- 咱们直奔主题,先看上面的问题:
糊涂的小白破费一周工夫做了 git log 如下所示的 6 个性能,每个性能对应一个 commit 的提交,别离是 feature-1 到 feature-6”:
而后谬误的执行了强制回滚,git reset --hard 2216d4e
,回滚到了 feature- 1 上,并且回滚的时候加了 –hard,导致之前 feature-2 到 feature- 6 的所有代码全副弄丢了,当初 git log 上显示如下:
而后,又在此基础上新增加了一个 commit 提交,信息叫 feature-7:
请问:如何把失落的代码 feature-2 到 feature- 6 全副复原回来,并且 feature- 7 的代码也要保留
-
接下来,咱们先回顾几个 git 命令:
git reset --hard
撤销工作区中所有未提交的批改内容,将暂存区与工作区都回到上一次版本,并删除之前的所有信息提交,审慎应用 –hard 参数,它会删除回退点之前的所有信息;git log
命令能够显示所有提交过的版本信息;git reflog
能够查看所有分支的所有操作记录(包含曾经被删除的 commit 记录和 reset 的操作);git cherry-pick
命令的作用,就是将指定的提交(commit)利用于其余分支。
-
最初,给出解答:
git reflog
和git cherry-pick
- 首先,应用
git reflog
查看所以 git 操作记录,记下 feature- 7 和 feature- 6 的 hash 码。
- 其次,
git reset --hard cd52afc
回滚到 feature-6。此时咱们曾经实现了要求的一半:胜利回到了 feature- 6 上,然而 feature- 7 没了。 - 最初,
git cherry-pick 4c97ff3
,执行实现之后,feature- 7 的代码就回来了,功败垂成。
- 首先,应用
更多 git 知识点举荐浏览 GitHub 联结创始人 Scott Chacon 和 Ben Straub 的开源巨作《Pro Git》
参考资料:git 时光穿梭机 – 女神的侧颜 | git cherry-pick 教程 | Git Reset 三种模式
第三十五式:文件上传只会应用 form 和 Ant Design Upload 组件?
最近有做一个由其余部门提供接口的需要,上传文件的接口文档如下图所示,文件内容是 base64 格局,且要和其余参数一起传递。笔者以前做的需要,上传文件个别是通过 form、Ant Design Upload 组件、FormData 等形式,上传胜利失去一个 URL,表单提交时将失去的 URL 传给后端;下载通过 Blob、后端返回 URL、发送邮件、或者前端生成 Excel 等形式。这次的上传应用了FileReader,简略记录相干实现。对于大文件的上传和下载,之后的章节会进行探讨。
- FileReader 上传代码实现
// DOM
<input type='file' id='file' onChange={(e) => this.uploadFile(e)} />
// js
uploadFile(e) {const file = e.target.files[0];
const reader = new FileReader();
// 解决 loadend 事件。该事件在读取操作完结时(要么胜利,要么失败)触发。reader.onloadend = () => {
this.setState({
// 存储
XXXFile: {
// 除了 name 外,file 中可被读取的属性还包含 size、type、lastModifiedDate
Name: file.name,
// base64 格式文件数据
// 一次性发送大量的 base64 数据会导致浏览器卡顿,服务器端接管这样的数据可能也会呈现问题。Buffer: reader.result.replace(/data.*base64[,]/, '')
}
})
}
reader.readAsDataURL(file);
}
-
FileReader 办法拓展:
FileReader.abort()
:停止读取操作。在返回时,readyState 属性为 DONE。FileReader.readAsArrayBuffer()
:开始读取指定的 Blob 中的内容, 一旦实现, result 属性中保留的将是被读取文件的 ArrayBuffer 数据对象.FileReader.readAsBinaryString()
:开始读取指定的 Blob 中的内容。一旦实现,result 属性中将蕴含所读取文件的原始二进制数据。FileReader.readAsDataURL()
:开始读取指定的 Blob 中的内容。一旦实现,result 属性中将蕴含一个data: URL
格局的 Base64 字符串以示意所读取文件的内容。FileReader.readAsText()
:开始读取指定的 Blob 中的内容。一旦实现,result 属性中将蕴含一个字符串以示意所读取的文件内容。
-
其余文件上传参考资料:
- 【MDN】FileReader
- 【MDN】在 web 应用程序中应用文件
- 李银城 - 前端本地文件操作与上传
- 前端图片上传解决方案
第三十六式:2**53 === 2 ** 53 + 1
?如果没有 BigInt,该如何进行大数求和?
Number.MAX_SAFE_INTEGER
:值为9007199254740991
,即2 ** 53 - 1
,小于该值能准确示意。而后咱们会发现2**53 === 2 ** 53 + 1
为true
。Number.MAX_VALUE
::值为1.7976931348623157e+308
,大于该值得到的是Infinity
,介于Infinity
和安全值之间的无奈准确示意。
既然咱们不能实现间接相加,咱们能够利用字符串宰割成字符串数组的形式来对每一位进行相加。
- 大数相加实现
function add (str1, str2) {
// 转为单字符串数组
str1=(str1+'').split('');
str2=(str2+'').split('');
let result='';// 存储后果
let flag=0; // 存储进位
while(str1.length || str2.length || flag){// 是否还有没有相加的位或者大于 0 的进位
// ~~str1.pop()失去最左边一位,并转成数字(~为按位取反运算符,详见第十四式)// 对应位数字相加,再加上进位
flag += ~~str1.pop() + ~~str2.pop();
// 去除进位,而后进行字符串拼接
result = flag%10 + result;
// 进位,0 或 1
flag = +(flag>9);
}
// 去除结尾(高位)的 0
return result.replace(/^0+/, '');
};
// 2 ** 53:9007199254740992
// add(2**53, 1):"9007199254740993"
// 2**53+1:9007199254740992
// BigInt 后果
// 2n**53n+1n:9007199254740993n
- 加减乘除:
对于加减乘除的实现可参考大数运算 js 实现,基本思路:
1、大数加法和减法是一个情理,既然咱们不能实现间接相加减,咱们能够利用字符串宰割成字符串数组的形式。2、乘法:每个位数两两相乘,最初错位相加。
参考资料:JS 大数相加 | 前端应该晓得的 JavaScript 浮点数和大数的原理
本文首发于集体博客,欢送斧正和 star。