DD每周前端七题详解-第六期
系列介绍
你盼世界,我盼望你无bug。Hello 大家好!我是霖呆呆!
呆呆每周都会分享七道前端题给大家,系列名称就是「DD每周七题」。
系列的形式主要是:3道JavaScript + 2道HTML + 2道CSS,帮助我们大家一起巩固前端基础。
所有题目也都会整合至 LinDaiDai/niubility-coding-js 的issues中,欢迎大家提供更好的解题思路,谢谢大家😁。
一起来看看本周的七道题吧。
正题
一、[,,,]的长度
(题目来源:https://github.com/CavsZhouyou/Front-End-Interview-Notebook)
咋了小伙伴们,感觉这道题目很简单是吗?哈哈,数一数逗号的间隙好像就能得出答案了,比如这样:
但是这道题的答案并不是4哟,而是3。
console.log([,,,].length) // 3
所以最终我们是需要把它想象成这样的:
也就是最后一个逗号的后面是不算一项的。
这里其实涉及到了一个名为:尾后逗号的概念,或者说是叫做终止逗号。上面👆这道题好像看不出它有什么作用,让我来看看实际上为什么会有这个用法。
比如现在你的项目中这么一个文件:
config.js:
const types = [
{
name: '帅'
},
{
name: '阳光'
},
]
export { types };
大家可以看到,我在阳光这一项的的后面是多加了一个","的,此时我将这个代码提交到git上并标记为版本1。
如果这时候types中又要添加一项名为"可爱"的配置项,我只需要在它下面再加上就行了,不需要去改动到原来代码,此时你提交的代码的diff是长这样的:
const types = [
{
name: '帅'
},
{
name: '阳光'
},
+ {
+ name: '可爱'
+ },
]
export { types };
大家可以看到,只有简单的三行增量代码,如果你没有使用尾后逗号的话,你提交的代码的diff会是这样:
const types = [
{
name: '帅'
},
{
name: '阳光'
- }
+ },
+ {
+ name: '可爱'
+ }
]
export { types };
因此我们可以得出尾后逗号它的作用:
「使得版本控制更加清晰,以及代码维护麻烦更少。」
(当然,这种用法在.json后缀的文件中是不能用的哈,因为JSON它严格遵循它自己的语法要求)
所以回归到这道题中来,像这种使用了多于一个尾后逗号的数组,我们就称之为稀疏数组,稀疏数组它的长度是等于逗号的数量的。
因此:
console.log([,,,].length) // 3
二、如何判断当前脚本运行在浏览器还是 node 环境中?
这道题呆呆其实在很多地方都看到了,但是有的回答好像并不那么靠谱。
回答这道题首先我们需要知道一个概念:
浏览器环境:全局对象为window;而在node环境下,是有一个名为global的对象,它的内部Class属性是为"global"。
内部Class属性也就是我们通过Object.prototype.call(obj)这种方式来获取到的内容,比如:
console.log(Object.prototype.toString.call([1, 2, 3])); // "[object Array]"
(关于它的用法呆呆在《【精】从206个console.log()完全弄懂数据类型转换的前世今生(上)》中的toString用法时说的也很详细咯)
因此我们可以得出这种判断方式:
var isBrowser = typeof window !== 'undefined'
&& ({}).toString.call(window) === '[object Window]';
var isNode = typeof global !== "undefined"
&& ({}).toString.call(global) == '[object global]';
({}).toString.call()和Object.prototype.toString.call()用法一致,只不过在{}的外面最好加上一个(),也是为了预防JS将大括号{}认为是一个空的代码块(额,呆呆试了一下貌似也没有这方面的问题)。
三、reduce方法有初始值和没有初始值的区别?
reduce函数的第一个参数是一个回调函数,第二个参数为可选的初始值。
如果有初始值的话,回调函数就会从数组的第0项开始执行,也就是会执行arr.length次;
但是如果没有初始值的话,会默认取数组的第0项为初始值,回调函数会从数组的第1项开始执行,也就是会执行arr.length - 1次。
这点从我们手写一个reduce的实现就可以看出来,代码如下:
Array.prototype.MyReduce = function (fn, initialValue) {
var arr = Array.prototype.slice.call(this);
var pre, startIndex;
pre = initialValue ? initialValue : arr[0];
startIndex = initialValue ? 0 : 1;
for (var i = startIndex; i < arr.length; i++) {
pre = fn.call(null, pre, arr[i], i, this)
}
return pre
}
过程分析:
- 首先,
map、reduce这种方法都是数组原型对象上的方法,所以我将MyReduce定义在Array.prototype上,这样你就可以直接使用ary.MyReduce()这样的方式调用它了(ary是一个类似于这样的数组[1, 2, 3])。 - 对于参数,我们参考原生
reduce,它接收的第一个参数是一个回调函数,第二个是初始值 - 而
var arr = ...的作用是获取调用MyReduce函数的那个变量,也就是说this会指向那个变量,例如ary.MyReduce(),那么此时this就为ary。 - 至于为什么不使用
var arr = this;的方式而是使用Array.prototype.slice.call(this),算是实现一个浅拷贝吧,因为reduce是不会改变原数组的。 - 然后就是定义传入
reduce中的回调函数的第一个参数pre,也就是上一次运行结果的返回值,可以看到这里就用到了初始值initialValue,如果存在初始值就取初始值,不存在则默认取数组第0项。(当然这里直接用initialValue ?来判断存不存在并不准确,因为我们知道0也会被判断为false) - 接着是定义循环开始的下标
startIndex,若是不存在初始值,则初始值是会取数组中的第0项的,相当于第0项并不需要运行,所以startIndex会是1,而如果有初始值的话则需要将数组的每一项都经过fn运行一下。 - 最后,
for循环中使用fn.call()来调用fn函数,并且最后一个参数是要把原来的数组传递到回调函数中,也就是这里的this。
四、form表单中的label标签的作用?
label标签不会向用户呈现任何特殊效果,它的作用是为鼠标用户改进了可用性。
也就是说当你使用了一个label标签和一个input绑定起来之后,点击label标签上的文字就会自动聚焦到input上。
如下:
绑定的方式:
label标签上设置for属性input标签上设置和for属性一样的id
例如:
<label for="username">username:</label>
<input type="text" name="username" id="username"/>
这里有两点需要注意的:
- 这两个标签不一定非要在
form标签内才会生效 for是和id对应的,并不是和name
五、HTML5中的自动完成功能autocomplete是做什么的?
大家在听到自动完成这个词,会有一点迷糊,自动完成什么?
其实这个功能的作用是这样的:
首先,autocomplete是一个属性,这个属性可以设置在form标签上,也可以设置在其它的input标签上。
被设置了自动完成的标签,会允许浏览器预测对字段的输入。也就是说浏览器会根据你在这个输入框中已经输入过的值,留有一个"历史记录",方便我们下次还想要输入同样的值。
例如下面这段代码:
<form autocomplete="on">
testaccount: <input type="text" name="testaccount" /><br />
testpassword: <input type="text" name="testpassword" autocomplete="off" /><br />
<input type="submit" />
</form>
- 我把
form标签的autocomplete打开,那么这个表单下的所有元素都开启了autocomplete - 接着我把
testpassword这一项的autocomplete关闭。
此时testaccout是有自动完成功能的,而testpassword没有。那么当我们第一次在这两个输入框中输入了内容并提交后。再次点击testaccout输入框,就会出现我们上一次输入并提交的那个值,而点击testpassword时却没有。
效果如下:
所以我们来做下总结吧😊:
autocomplete属性规定输入字段是否应该启用自动完成功能;- 它的默认值是启用,也就是
"on",另一个值是"off"关闭; - 作用是:允许浏览器预测对字段的输入。当用户在字段开始键入时,浏览器基于之前键入过的值,应该显示出在字段中填写的选项。
autocomplete属性适用于<form>,以及下面的<input>类型:text, search, url, telephone, email, password, datepickers, range , color。
六、CSS中的visibility有个collapse属性值是干嘛用的?
visibility会有这么几个个属性值:
visiblehiddencollapseinherit
比较常用的可能是前面两个,用于控制元素的显示隐藏。且我们知道,设置为hidden是会隐藏元素,但是其他元素的布局不改变,相当于此元素变成透明。
inherit则是从父元素继承visibility属性的值。
而对于collapse,可能用的不是特别多,我们先来看下它的介绍:
- 对于一般的元素,它的表现跟
hidden是一样的; - 如果这个元素是
table相关的元素,例如table行,table group,table列,table column group,它的表现却跟display: none一样,也就是说,它们占用的空间也会释放。
下面一起来看看这个案例😊:
html代码:
<table>
<tr class="tr1">
<td>
tr1
</td>
<td>
tr1
</td>
</tr>
<tr>
<td>
tr2
</td>
<td>
tr2
</td>
</tr>
</table>
css代码:
table {
border: 1px solid red;
}
td {
border: 1px solid blue;
}
.tr1 {
}
我在没给.tr1设置任何属性的时候,页面呈现的效果是这样的,很正常:
如果设置了visibility: hidden:
.tr1 {
visibility: hidden;
}
效果如下:
虽然第一行被隐藏了,但是它的空间还是在的,就像是"隐身"了一样。
而如果设置了visibility: collapse:
.tr1 {
visibility: collapse;
}
效果如下:
最终的效果会和display: none;一样。
如果你问呆呆这属性有什么实际的用处没有...咳咳,抱歉,好像还真没有😅。也可能是呆呆不知道,知道的小伙伴还请提出哟,一起学习一哈。
七、块状元素width:100%与width:auto的区别?
这两个属性值相信大家都不陌生了,先让我们来看个案例理解一下:
html代码:
<div class="super">
<div class="sub1">
我是呆呆的第一个崽
</div>
<div class="sub2">
我是呆呆的第二个崽
</div>
<div class="sub3">
我是呆呆的第三个崽
</div>
</div>
css代码:
.super {
width: 200px;
height: 100px;
background: skyblue;
}
.sub1 {
background: #d0e4a9;
}
.sub2 {
width: 100%;
background: #c077af;
}
.sub3 {
width: auto;
background: #f8d29d;
}
最开始时,三个崽表现的效果是一样的,并无很大差别:
此时如果我们对后面两个崽做一下改动:
.sub2 {
width: 100%;
padding-left: 10px;
margin-left: 10px;
background: #c077af;
}
.sub3 {
width: auto;
padding-left: 10px;
margin-left: 10px;
background: #f8d29d;
}
给他们都加上左内边距和左外边距,此时的效果就变成了这样:
大家会发现,设置了width: 100%的崽,它的宽度会和父级的一样,此时如果再给他设置了额外的padding,那它就会比父级还要宽了,也就是会超出父级容器。而因为还设置了margin-left,所以左边也会有一段距离。
但是设置了width: auto的崽就比较乖了,无论怎样它都不会超出父级,而是选择压缩自己的宽度。
因此我们可以得出结论:
- 两者的计算方式不同(这里的计算规则都是基于
box-sizing: content-box;的情况):- 对于
width: auto;它的总宽度是等于父宽度的(这里的父宽度是指父级内容的宽度不包括padding、border、margin),即使给元素设置了padding、border、margin等属性,它也会自动分配水平空间。 - 对于
width: 100%;它表示的是元素内容的宽度等于父宽度,所以它的总宽度是有可能超过父级的,因为如果设置了额外的padding、border,就可能比父级宽了。
- 对于
- 无论是
width:100%还是auto,其计算的参照都是父级内容区width值,而非总宽度值,也就是不包括padding、border、margin。 - 一般
width:auto使用的多一些,因为这样灵活;而width:100%使用比较少,因为在增加padding或者margin的时候,容易使其突破父级框,破坏布局。
参考文章
知识无价,支持原创。
参考文章:
后语
你盼世界,我盼望你无bug。这篇文章就介绍到这里。
您每周也许会花48小时的时间在工作💻上,会花49小时的时间在睡觉😴上,也许还可以再花20分钟的时间在呆呆的7道题上,日积月累,我相信我们都能见证彼此的成长😊。
什么?你问我为什么系列的名字叫DD?因为呆呆呀,哈哈😄。
喜欢「霖呆呆」的小伙还希望可以关注霖呆呆的公众号 LinDaiDai 或者扫一扫下面的二维码👇👇👇。
我会不定时的更新一些前端方面的知识内容以及自己的原创文章🎉
你的鼓励就是我持续创作的主要动力 😊。
往期题目可以戳下面👇:
或者你也可以查看github上的issues:「我是issues」
本文使用 mdnice 排版