在前端开发过程中,常常遇到各种各样的问题和坑点。尤其是随着技术的不断发展和更新,新的问题也不断涌现。对于初学者而言,这些问题往往让人感到十分困惑和无助。因此,本文将旨在探讨一些前端开发过程中常见的问题和坑点以及解决方法,帮助读者更加深入地了解前端开发,并解决实际工作中遇到的问题。
/**
* 为什么 010 会是 8
*/
const num1 = 09 // 9
const num2 = 010 // 8
这边是因为 0 开始的数字js会尝试先把它转成八进制的数字。如果你出现大于 8 的数字,他知道不是八进制还给你转十进制。纯粹的八进制应该用 0o ,类似的还有 0b 二进制和 0x 十六进制,但是他们写的不符合转换条件的话会直接报错。
0.1+0.2 // 0.30000000000000004
2.55.toFixed(1) // '2.5'
2.45.toFixed(1) // '2.5'
2.toFixed(1) // Uncaught SyntaxError: Invalid or unexpected token
2..toFixed(1) // '2.0'
js 计算有精度问题呢?大家一定都是知道的, 今天就是来简单解释一下为什么会出现丢失精度的问题。这边其实分两部分,存储和展示。存储的时候 JavaScript 是以 64 位二进制补码的方式来存储。由于改方式是以 2 为底进行表示的,所以执行某些运算时容易出现误差、溢出、无限循环等问题。
图片
我们可以发现本来应该是 11001100 无限循环被截断了,尾号 11001 的时候1被舍去了,然后进了一位 最后存储成了上图的 1101 的样子。所以 0.1 其实存的比 0.1 要大一点点,0.2 也是一样,而 0.3 比实际小一些。所以计算 0.1+0.2 的时候其实是拿二进制计算的,两个都偏大的数字相加 误差被近一步的放大了。
下面这张图可以看到他们真实存下来的数据转成十进制的样子。实际显示的时候会做近似处理,js 会判断一个数字特别像 0.1 它就显示 0.1 了。
图片
toFixed 问题也是一样。
图片
还有就是有时候我们对一个数字使用 .toFixed .toString 会报错。
0.toString() // Uncaught SyntaxError: Invalid or unexpected token
// 我们期待的是它会隐性转换让我们调用 Number 构造函数上的方法,
// 但是程序会以为你在写一个小数,小数还不合规,所以报错了,
// 解决方法就是拿变量装一下,或者 0..toString()
在 JavaScript 中,采用 64 位二进制补码表示数值类型,即双精度浮点数。符号位(S)、指数位(E)和尾数位(M)的比特数分别为 1 位、11 位和 52 位。在使用 IEEE 754 标准表示双精度浮点数时,使用一些特殊的位表示:其中一个隐含位表示数字 1,在正常项中省略,因此一共有 53 位表示有效数字。
长度问题:
function fn(a,b,c){
return a+b+c
}
fn.length // 3 一般来说fn的长度是形参的个数 但是形参有默认值就不同
function fn1(a = 1,b,c){
return a+b+c
}
fn1.length // 0
function fn2(a,b=1,c){
return a+b+c
}
fn2.length //1
// 它只会统计首个默认之前的参数
对象排序问题:
a.b=1
a.a=1
a.c=1
a[2]=2
a[12]=2
a[1]=2
// 结果 {1: 2, 2: 2, 12: 2, b: 1, a: 1, c: 1}
// 对象的内部key value的存储顺序是这样的
// 如果属性可以转number,提前上来,按升序排列,其他的字符串属性按添加的先后顺序
赋值中断问题:js 里没有事务的机制,不会恢复到操作之前的状态。如果中途失败了,之前赋值和操作过的数据是保留的,失败后的操作不执行。
定时器不准:这里说的不准还不是说一点小误差。定时器由于渲染主进程阻塞也好,延时任务嵌套过深也好,事件循环优先级被排队到后边也好。这些都可以认为是“误差”,但是如果说你 setIntervel 是 10ms,结果它间隔 n 秒调一次函数,那可不是误差了,可能直接会产生 bug。
这个问题的原因是:用户在使用谷歌浏览器的过程中将窗口最小化或切换到其他应用程序中去,浏览器会将当前标签页和其中的 JavaScript 定时器挂起,这将导致定时器延迟调用。通常情况下,浏览器会尽可能保持定时器的准确性,并在恢复标签页后立即执行延迟的定时器。但是,如果计算机负载过重或其他原因导致 JavaScript 的执行速度变慢,定时器可能会更加延迟。经过测试,新版本的浏览器上基本都是至少 1 秒一次。
图片
详细参考 https://developer.mozilla.org/zh-CN/docs/Web/API/setTimeout
竞态问题:异步的竞态问题也是开发中经常遇到的问题。举个例子用户输入搜索就请求相应的商品,用户很快的输入了“手机壳” 3 个字;“手”“手机”“手机壳” 3 个不同参数的请求几乎同时发了出去,异步请求很难保证哪个请求,先回来哪个请求后回来。那加上防抖呢?其实这也不是防抖该应用的场景,弱网环境下请求 5 秒、10 秒返回都说不准,防抖防几秒都不太合适。我根据个人的经验总结了 3 种方式:
1. 链式调用
// 让要执行的异步函数通过一个链式方式调用
export class SequenceQueue {
promise = Promise.resolve();
excute (promise) {
this.promise = this.promise.then(() => promise);
return this.promise;
}
};
2. 设置一个叠加器,每次调用就累加,回调函数内就可以知道当前是不是“最新”。antd 里有一段例子如下
const fetchRef = useRef(0);
const debounceFetcher = useMemo(() => {
const loadOptions = (value: string) => {
fetchRef.current += 1;
const fetchId = fetchRef.current;
setOptions([]);
setFetching(true);
fetchOptions(value).then((newOptions) => {
if (fetchId !== fetchRef.current) {
// for fetch callback order
return;
}
setOptions(newOptions);
setFetching(false);
});
};
return debounce(loadOptions, debounceTimeout);
}, [fetchOptions, debounceTimeout]);
可以进一步封装,将请求封装成 request( url, [option], [queueName] ), 通过外部传入来指定需要竞态的映射名。也就是将上述的叠加器放在一个 Map 里,使用 queueName 做 Map 的 key。
“
如果作为通用的请求中间件封装,处于内存优化考虑,此处可以将 Map 优化成 weakMap。Map 键值对是强引用,如有一个键被引用,那么GC是无法回收对应的值的,weakmap 不存在这样的问题,但要注意 weakMap 只能使用对象做 key。
3. 新请求发出的时候取消老的请求。一般来说请求发出去了是追不回来的。但是 fetch 和原生 ajax 提供了 abort 之类的取消方法。如果你项目的请求是 fetch 或 XMLHttpRequest 就可以用他们自带的方式取消。需要注意的是,如果请求已经被发送到服务器,并且请求体数据已被上传,那么 abort() 方法就无法中止请求。大多数情况项目用的可能是 axios、uni.request 等其他更热门的请求库,这时候我们可以利用 promise.race 来封装一个可以取消的请求,传一个自定义能带取消方法的 promise 进 promise.race 来控制 真正要执行的 promise 函数提前取消。
// 封装
function cancelableRequest(requestPromise) {
const cancelToken = {};
const cancelablePromise = new Promise((resolve, reject) => {
cancelToken.cancel = () => {
reject(new Error('Request was canceled'));
};
Promise.race([requestPromise, cancelToken])
.then(resolve)
.catch(reject);
});
return { promise: cancelablePromise, cancel: cancelToken.cancel };
}
// 使用
const mockApi= () =>
new Promise(resolve => {
setTimeout(() => {
resolve([{ title: 'Post 1' }, { title: 'Post 2' }, { title: 'Post 3' }]);
}, 3000);
});
const { promise, cancel } = cancelableRequest(mockApi());
promise
.then(posts => console.log(posts))
.catch(error => console.error(error.message));
// 取消请求
cancel();
定位:一般来说写 position: fixed 都是想相对窗口定位实现一些弹窗、抽屉或者浮动组件等效果,但是如果父元素中存在 transform 属性的话,固定效果将直接降级变成 position: absolute 的表现。这可能也是大多数UI库选择将 modal、drawer 之类的 fixd 元素都插入在 body 下,和应用本身分离开,可能就是担心有 transform 来影响定位。究其原因是因为包含块的定义:
如果 position 属性为 static、relative 或 sticky,包含块可能由它的最近的祖先块元素(比如说 inline-block, block 或 list-item 元素)的内容区的边缘组成,也可能会建立格式化上下文 (比如说 a table container, flex container, grid container, 或者是 the block container 自身)。
层叠计算:有的时候,如果引入了很多的库,会发现样式会偶发的发生错误。这是因为样式冲突了,那样式的优先级是什么样子的呢?css 全称为 cascader style sheet, 层叠样式表。其层叠的目的就是为了比对样式冲突后的“胜出者”;mdn 里详细的介绍了其比较计算的规则。https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Cascade_layers
比对分以下步骤进行
分层就是浏览器画制图画的顺序,浏览器会根据一定的规则划分图层,当然代码也能干预图层的划分比如定位、动画等等,不同图层直接不能相互影响,换句话说一个图层在另一个图层下面的话,尽管 z-index 是 0 也能覆盖 z-index: 100 的元素。
11. 特定性就是 4 位数字( 0,0,0,0 )第一位代表是否是行内样式,后面3位就是 id,class,tag 的个数。统计完特定性的 4 个数字后,从前往后比较大小。比较极端的情况下如果你写了足够多的 class 是可以超过 id 选择器的。例如,以下选择器有 11 个 class 选择器组成:
div.navbar ul ul.dropdown-menu li.active > a.btn-primary:hover span.icon {
/* styles */
}
// 虽然其中没有 id 选择器,但它显然比单个 id 选择器的优先级更高。
12. 全部比对完后还是没有“胜出者”的话,我们就会根据源代码书写的顺序,后来的覆盖先来的。言归正传,所以我们样式如果偶发的出现问题,可能是因为网络原因 javascript 下载下来的时间不确定,从而导致执行后插入 css 文件的顺序不一致,最终呈现出一种偶发的现象
造成性能问题的原因是多种多样的,大体可以分为 3 种,一是网络,二是渲染,三是计算
为了避免频繁的页面重排和重绘,我们应该尽量减少对 DOM 的访问和修改。为了控制元素的样式变化,应该尽可能使用 CSS。这样有助于提高页面性能和用户体验。
懒加载和预加载:懒加载可以减少初始页面渲染时间,当用户需要访问到某个资源时,才去加载这个资源,而预加载可以预先加载即将用到的资源,加快用户访问其他页面时的速度。
虚拟 DOM:使用 Virtual DOM,实现局部修改视图而不是整体重新渲染,减少 DOM 的操作。
优化 JavaScript 代码:采用 JavaScript 模块化、使用面试编程思想,减少页面的 JavaScript 代码,从而减少浏览器的工作量。
多线程:使用延时线程、网络线程、Web Workers 等其他不会阻塞渲染的进程来完成工作。
运行时计算的手段:
优化 JavaScript 代码:采用 JavaScript 模块化、使用面试编程思想,减少页面的 JavaScript 代码,从而减少浏览器的工作量。
算法优化:用更加合理的数据结构设计和算法,以更优的方式完成需求。
事件委托:将事件绑定在父元素上,减少事件的处理次数。
函数节流和防抖:使用函数节流技术避免频繁触发事件处理。
合理的事件注册和解绑
合理的释放不使用的内存
一些想当然觉得应该是一致的东西结果不一致,比如前瞻匹配和后瞻匹配的兼容是不一样的。需要兼容IE的话就不能使用后瞻写法
图片
https://developer.mozilla.org/zh-CN/docs/Web/CSS/Containing_block
https://caniuse.com/
https://standards.ieee.org/ieee/754/6210/
分享名称:前端常见问题分析,你学会了吗?
网址分享:http://www.shufengxianlan.com/qtweb/news42/477492.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联