DD每周前端七题详解-第五期

DD每周前端七题详解-第五期

系列介绍

你盼世界,我盼望你无bug。Hello 大家好!我是霖呆呆!

呆呆每周都会分享七道前端题给大家,系列名称就是「DD每周七题」。

系列的形式主要是:3道JavaScript + 2道HTML + 2道CSS,帮助我们大家一起巩固前端基础。

所有题目也都会整合至 LinDaiDai/niubility-coding-js 的issues中,欢迎大家提供更好的解题思路,谢谢大家????。

一起来看看本周的七道题吧。

正题

一、实现mask函数将"123456"转为"##3456",只保留最后四个字符

(题目来源:https://github.com/30-seconds/30-seconds-of-interviews)

首先介绍一下题目的意思吧????,案例????如下:

const mask = (str, maskChar = '#') => {
 // 代码
}
console.log(mask('123456')); // '##3456'
console.log(mask('lindaidai')); // '#####idai'

这道题的难度应该没有那么大,处理方式也有很多。呆呆这边主要是讲解一下如何使用padStart来实现的。

简单介绍一下padStart方法吧,它是ES8新增的实例函数,与它作用差不多的还有一个叫padEnd的函数:

  • String.prototype.padStart

  • String.prototype.padEnd

「作用」:允许将空字符串或其他字符串添加到原始字符串的开头或结尾。

「语法」

padStart(targetLength, [padString])
  • targetLength: 必填,当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。

  • padString: 可选,填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断,此参数的缺省值为" "

例如:

'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'

哈哈,另外想要了解如何实现一个padStart的小伙伴可以看呆呆之前一篇文章哟:DD每周前端七题详解-第二期

「言归正传」,让我们回到这道题目哈,首先让我们来处理一下输入参数边界的情况,例如输入的str不存在或者长度小于4的时候:

const mask = (str, maskChar = '#') => {
 if (!str || str.length <= 4) return str;
}

其次,我们可以只保留住str的末尾四个字符,然后使用padStart将这四个字符填充至str.length即可,如下:

const mask = (str, maskChar = '#') => {
  if (!str || str.length <= 4) return str;
  return str.slice(-4).padStart(str.length, maskChar);
}
  • slice不会影响原本的字符串

  • 使用padStart填充即可

https://github.com/LinDaiDai/niubility-coding-js/issues/30

二、介绍一下NaN并实现一个isNaN

「介绍一下NaN」

  • NaN属性是代表非数字值的特殊值,该属性用于指示某个值不是数字;

  • NaN是不等于NaN的,即NaN === NaN的结果是false

  • 使用Object.is()来比较两个NaN结果是true,即Object.is(NaN, NaN)的结果是true

  • typeof NaN"number"

  • 方法parseInt()parseFloat()在不能解析指定的字符串时就返回这个值;

  • 可以使用isNaN来判断一个变量是不是NaN,它是JS内置对象Number上的静态方法。

(关于第三点,大家可以看一下我之前的一篇文章哟,里面的「第二补:JS类型检测-Object.is()和===的区别」有提到:读《三元-JS灵魂之问》总结,给自己的一份原生JS补给(上))

「实现一个isNaN」:

对于isNaNpolyfill实现起来就比较简单了,只需要利用NaN不等于它自身的这一点即可:

const isNaN = v => v !== v;

https://github.com/LinDaiDai/niubility-coding-js/issues/31

三、按位取反,为什么~2 = -3?

接下来,分享一道与JavaScript原生无关的题目吧,主要也是看到群里有小伙伴问了关于按位取反~的用法,这边统一科普一下,????。

正常一个数字,例如12,或者-1-2

如果我们对它们进行按位取反的话,结果会是这样:

  • ~1 = -2

  • ~2 = -3

  • -1 = 0

  • -2 = 1

看不懂没关系,让我们来一步步看看实现的过程哈。

在这里其实是分了「正数和负数」的,因为符号不同取反的过程也会不同。

1.1 正数按位取反

先让我们来看看正数的按位取反。

比如先看看~1 = -2,过程如下:

「1. 十进制转为二进制原码」

首先将十进制的1转化为二进制原码为:0000 0001

「2. 二进制原码按位取反」

之后将原码按位取反:

也就是将0000 0001 => 1111 1110

(取反应该知道啥意思吧?就是0换成11换成0)

「3. 取反后的二进制转为原码」

再将取反后的二进制码转为原码二进制:

也就是将1111 1110 => 1000 0010

这里你估计看着都点懵了,当我们将「取反后的二进制」转为「原码二进制」的时候,其实是有以下两步的:

  1. 需要判断取反后的二进制的第一个位是不是1,这个第一位我们称之为「符号位」,如果是1的话就表示即将要转成的数是一个负数,如果是0的话表示即将要转的数是一个正数,这个符号位是不能动的;在这里我们可以看到1111 1110的第一位是1,所以表示即将要转的数是一个负数,同时我们不动它。

  2. 然后将除了第一位以外其它位数取反并+1。所以会有这么两个过程:

  • 1111 1110 => 1000 0001

  • 1000 0001 => 1000 0010 (这步是对上一步的结果+1,因为上一步的最后一个数是1,所以它再加上1就需要向前进一位了,因此变成了1000 0010)

「4. 将原码二进制转为十进制」

最后一步就是将我们前面得到的1000 0010这个二进制转化为十进制了。

第一位符号位,是1,则表示是个负数,所以结果为-2

OK????,搞懂了这个步骤之后再让我们自己来转换一下~2 = -3吧:

1. 0000 0010
2. 1111 1101
3. 1000 0011
4. -3

「正数按位取反总结」

  1. 十进制转为二进制原码

  2. 二进制原码按位取反

  3. 符号位保留,其余位取反+1

  4. 二进制原码转为十进制

1.2 负数按位取反

负数的按位取反和上面就有些不一样了,主要是第二步和第三步调换一下顺序:

  1. 十进制转为二进制原码

  2. 符号位保留,其余位取反+1

  3. 二进制原码按位取反

  4. 二进制原码转为十进制

例如:~-1 =0的转换过程:

「1. 十进制转为二进制原码」

这步和正数按位取反是一样的:

-1 => 1000 0001

「2. 符号位保留,其余位取反+1」

转换过程:

  • 1000 0001 => 1111 1110 (取反)

  • 1111 1110 => 1111 1111 (取反后 + 1)

「3. 二进制原码按位取反」

将刚刚得到的再进行按位取反:

1111 1111 => 0000 0000

「4. 二进制原码转为十进制」

0000 0000 => 0

OK????,现在自己来转换一下~-2 = 1吧:

1. 1000 0010
2. 1111 1110
3. 0000 0001
4. 1

这里没啥诀窍,关键就是要记住转换的过程然后不断的练习吧 ????。

另外关于~~的用法还可以看呆呆的另一篇文章哟《JS中按位取反运算符~及其它运算符》

https://github.com/LinDaiDai/niubility-coding-js/issues/32

四、知道insertAdjacentHTML方法吗?

这个方法是呆呆最近在看公司项目代码时了解到的,之前一直没有注意它。

首先对于它的用法:

insertAdjacentHTML() 方法将指定的文本解析为 Element 元素,并将结果节点插入到DOM树中的指定位置。它不会重新解析它正在使用的元素,因此它不会破坏元素内的现有元素。这避免了额外的序列化步骤,使其比直接使用innerHTML操作更快。

其次它的作用对象是一个元素element,例如const container = document.getElementById('container')

语法上呢:

element.insertAdjacentHTML(position, text);
  • position:一个DOMString,也就是表示插入内容相对元素的位置,且必须是下面的字符串之一:

    • 'beforebegin':元素自身的前面。

    • 'afterbegin':插入元素内部的第一个子节点之前。

    • 'beforeend':插入元素内部的最后一个子节点之后。

    • 'afterend':元素自身的后面。

  • text:是要被解析为HTML或XML元素,并插入到DOM树中的 DOMString

「案例」

让我们来看看它的用法,例如????现在有一个HTML的结构为:

<div id="one">我是one</div>

JavaScript代码中加上这段话:

const one = document.getElementById('one');
one.insertAdjacentHTML('afterend', '<div id="two">我是two</div>');

现在最终的渲染结果就变成了这样:

<div id="one">我是one</div><div id="two">我是two</div>

「工作上的用法」

在项目中,主要可以应用于这样的场景:一个空的容器(你可以理解为一个div),开始需要一个loading的效果,在数据加载完毕之后,需要把loading取掉且清空容器内的元素并以其它方式重新渲染出容器的内容。

这里呆呆就以定时器来模拟一下数据加载的过程,实现代码如下:

<body>
 <div id="container"></div>
</body>
<script>
 const container = document.getElementById('container');
  const loading = '<div id="loading">loading</div>'; // loading可能是一个组件
  container.insertAdjacentHTML('beforeend', loading);
  setTimeout(() => {
    container.innerHTML = ''
  }, 2000)
</script>

(当然,我们不要为了刻意用而去用,适合自己的才是最好的)

「安全问题」

  • 使用 insertAdjacentHTML 插入用户输入的HTML内容的时候,需要转义之后才能使用。

    例如:

    const one = document.getElementById('one');
    // 由于 encodeURI('<div id="two">我是two</div>')会被转译为:
    // %3Cdiv%20id=%22two%22%3E%E6%88%91%E6%98%AFtwo%3C/div%3E
    // 因此最终会被当成 "%3Cdiv%20id=%22two%22%3E%E6%88%91%E6%98%AFtwo%3C/div%3E"字符串渲染
    one.insertAdjacentHTML('afterend', encodeURI('<div id="two">我是two</div>'));
    
  • 如果只是为了插入文本内容(而不是HTML节点),不建议使用这个方法,建议使用node.textContent 或者 node.insertAdjacentText()。因为这样不需要经过HTML解释器的转换,性能会好一点。(这里是引用的MDN-insertAdjacentHTML上的内容)

https://github.com/LinDaiDai/niubility-coding-js/issues/33

五、insertAdjacentHTMLinsertAdjacentElement的区别

第二个参数的类型不同, 前者接收的是是要被解析为HTML或XML元素的字符串,而后者接收的是一个element元素。

const one = document.getElementById('one');
one.insertAdjacentHTML('afterend', '<div id="two">我是two</div>');

const one = document.getElementById('one');
const two = document.createElement('div')
two.innerHTML = '我是two';
one.insertAdjacentElement('afterend', two);

https://github.com/LinDaiDai/niubility-coding-js/issues/34

六、实现九宫格布局

实现效果如下:

先来看一下HTML方面的代码:

<div id="container">
    <div class="item item-1">1</div>
    <div class="item item-2">2</div>
    <div class="item item-3">3</div>
    <div class="item item-4">4</div>
    <div class="item item-5">5</div>
    <div class="item item-6">6</div>
    <div class="item item-7">7</div>
    <div class="item item-8">8</div>
    <div class="item item-9">9</div>
</div>

还有一些item上的基础css代码:

#container {
    /* css代码 */
}

.item {
    font-size: 2em;
    text-align: center;
    border: 1px solid #e5e4e9;
}

.item-1 {
    background-color: #ef342a;
}

.item-2 {
    background-color: #f68f26;
}

.item-3 {
    background-color: #4ba946;
}

.item-4 {
    background-color: #0376c2;
}

.item-5 {
    background-color: #c077af;
}

.item-6 {
    background-color: #f8d29d;
}

.item-7 {
    background-color: #b5a87f;
}

.item-8 {
    background-color: #d0e4a9;
}

.item-9 {
    background-color: #4dc7ec;
}

「方案一」

第一种方案可以使用浮动+百分比:

#container {
    width: 150px;
    height: 150px;
}
.item {
    float: left;
    width: 33.33%;
    height: 33.33%;
    box-sizing: border-box;
    font-size: 2em;
    text-align: center;
    border: 1px solid #e5e4e9;
}

「方案二」

还可以使用flex布局的方式:

#container {
    width: 150px;
    height: 150px;
    display: flex;
    flex-wrap: wrap;
}
.item {
    width: 33.33%;
    height: 33.33%;
    box-sizing: border-box;
    font-size: 2em;
    text-align: center;
    border: 1px solid #e5e4e9;
}

「方案三」

另外的话,也许还可以试试grid

#container {
    display: grid;
    grid-template-columns: 50px 50px 50px;
    grid-template-rows: 50px 50px 50px;
}
.item {
    font-size: 2em;
    text-align: center;
    border: 1px solid #e5e4e9;
}

答案参考:https://github.com/haizlin/fe-interview/issues/2270

https://github.com/LinDaiDai/niubility-coding-js/issues/35

七、说说will-change

will-changeCSS3新增的标准属性,它的作用很单纯,就是"增强页面渲染性能",当我们在通过某些行为触发页面进行大面积绘制的时候,浏览器往往是没有准备,只能被动的使用CUP去计算和重绘,由于事先没有准备,对于一些复杂的渲染可能会出现掉帧、卡顿等情况。

will-change则是在真正的行为触发之前告诉浏览器可能要进行重绘了,相当于浏览器把CUP拉上了,能从容的面对接下来的变形。

常用的语法主要有:

  • whil-change: scroll-position; 即将开始滚动

  • will-change: contents; 内容要动画或者变化了

  • will-transform; transform相关的属性要变化了(常用)

注意:

  • will-change虽然可以开启加速,但是一定要适度使用

  • 开启加速的代价为手机的耗电量会增加

  • 使用时遵循最小化影响原则,可以对伪元素开启加速,独立渲染

  • 可以写在伪类中,例如hover中,这样移出元素的时候就会自动removewill-change

  • 如果使用JS添加了will-change,注意要及时remove掉,方式就是style.willChange = 'auto'

https://github.com/LinDaiDai/niubility-coding-js/issues/36

参考文章

知识无价,支持原创。

参考文章:

  • 《浪里行舟-盘点ES7、ES8、ES9、ES10新特性》

  • 《MDN-isNaN》

  • 《使用CSS3 will-change提高页面滚动、动画等渲染性能》

后语

你盼世界,我盼望你无bug。这篇文章就介绍到这里。

您每周也许会花48小时的时间在工作????上,会花49小时的时间在睡觉????上,也许还可以再花20分钟的时间在呆呆的7道题上,日积月累,我相信我们都能见证彼此的成长????。

什么?你问我为什么系列的名字叫DD?因为呆呆呀,哈哈????。

喜欢「霖呆呆」的小伙还希望可以关注霖呆呆的公众号 LinDaiDai 或者扫一扫下面的二维码????????????。

[

我会不定时的更新一些前端方面的知识内容以及自己的原创文章????

你的鼓励就是我持续创作的主要动力 ????。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值