浏览器眼中的0

0做为一个特殊的符号,常常会跟浏览器打交道,在不一样的场景下,0表明的意思不尽相同,所以浏览器眼中的0不必定就是符合人们感官上的认识,那究竟浏览器会怎么对待它呢,今天咱们就来探究一下各类场景中0的含义及浏览器的处理方式。css

1.setTimeout

setTimeout在js中经常使用来推迟任务的执行,能够经过第二个参数设置延迟的毫秒数(若是不设置,默认为0),在一些代码中,能够看到delay=0的状况,以下:html

window.setTimeout(() => { ...... }, 0);

了解js的同窗应该知道,setTimeout的回调函数不会在定时器超时后当即执行,若是delay大于0,比较好理解,但delay是0的时候呢,浏览器会怎么对待呢,这里要分两种状况: 1.)timer嵌套 'Timers can be nested; after five such nested timers, however, the interval is forced to be at least four milliseconds.'也就是setTimeout嵌套超过5层的,而且延迟不到4ms,才会变成4ms,一样适用于setInterval,所以在这种状况下,delay=0其实会被设置成4; 2.)timer没有嵌套 在没有嵌套状况下,对于chrome来讲,delay=0也会设置成1; 说完浏览器的处理方式以后,咱们来看看网上搬过来的一个例子吧:node

setTimeout(()=>{console.log(5)},5)
setTimeout(()=>{console.log(4)},4)
setTimeout(()=>{console.log(3)},3)
setTimeout(()=>{console.log(2)},2)
setTimeout(()=>{console.log(1)},1)
setTimeout(()=>{console.log(0)},0)

chrome打印结果:1 0 2 3 4 5; firefox打印结果:0 1 2 3 4 5; edge打印结果:1 2 0 3 4 5; qq浏览器打印结果:1 0 2 3 4 5; 360浏览器打印结果:1 0 2 3 4 5; 从上面的打印结果来看,firefox是符合代码预期的,edge打印与chrome稍有不一样,应该是edge处理delay=0状况稍有不一样(设置成了2),qq和360浏览器跟chrome保持一致。 0ms定时器 在MDN文档上,还说到一种实现0ms延时的定时器的实现方案,大致思路是自定义一个setZeroTimeout 方法,经过 postMessage 来触发定时回调的执行,具体可看 https://dbaron.org/log/20100309-faster-timeoutsnode的setTimeout 说完浏览器中的setTimeout,咱们再来看看nodejs中的是否同样呢,能够经过nodejs的源代码窥探一二:git

// https://github.com/nodejs/node/blob/master/lib/internal/timers.js
function Timeout(callback, after, args, isRepeat, isRefed) {
    after *= 1; // Coalesce to number or NaN
    if (!(after >= 1 && after <= TIMEOUT_MAX)) { // const TIMEOUT_MAX = 2 ** 31 - 1;
        if (after > TIMEOUT_MAX) {
            process.emitWarning(`${after} does not fit into` +' a 32-bit signed integer.' + '\nTimeout duration was set t 1.', 'TimeoutOverflowWarning');
}
    after = 1; // Schedule on next tick, follows browser behavior
}

看过源代码后,就知道node的处理策略了:若是delay=0,会设置为1,注释也说得很清楚了,是为了遵循浏览器的行为。es6

2.+0&-0

虽然不多用到,可是js中确是存在+0和-0的,那么有什么区别呢:github

+0 === -0; // true
+0 === 0; // true
-0 === 0; // true

能够看到,经过全等比较,+0,-0和0都是相等的,那是否就能够认为这三者就是同样的呢,还不能这么轻易下结论,有时候颇有必要区分三者,那么如何判断呢,es6新增了一个方法Object.is(value1, value2),能够用来判断,具体效果以下:chrome

Object.is(0, -0); // false
Object.is(+0, -0); // false
Object.is(+0, 0); // true

这里还须要说明的一点就是+0和0其实就是同样的,由于+0等效Number(0),所以Object.is(+0, 0)是符合预期的,这里顺带说一下Object.is的比较逻辑,根据MDN文档描述, Object.is() 方法判断两个值是否为同一个值。若是知足如下条件则两个值相等:浏览器

  • 都是 undefined
  • 都是 null
  • 都是 truefalse
  • 都是相同长度的字符串且相同字符按相同顺序排列
  • 都是相同对象(意味着每一个对象有同一个引用)
  • 都是数字且
    • 都是 +0
    • 都是 -0
    • 都是 NaN
    • 或都是非零并且非 NaN 且为同一个值

== 运算不一样。 == 运算符在判断相等前对两边的变量(若是它们不是同一类型) 进行强制转换 (这种行为的结果会将 "" == false 判断为 true ), 而 Object.is 不会强制转换两边的值。 与=== 运算也不相同。 === 运算符 (也包括 == 运算符) 将数字 -0+0 视为相等 ,而将Number.NaNNaN视为不相等.ide

最后来看下如何生成-0和+0吧:函数

1/-Infinity; // -0
0*-1; // -0
1/+Infinity; // +0
0*+1;// +0

3. +[]

因为存在类型转换,所以在判断相等时,会有这样的状况:

'' == 0; // true
[] == 0; // true
+[] == 0; // true

这里咱们说一下+[]的状况,很容易理解,+至关于Number([]),最终会转换成0,这没什么大不了,若是是下面这段代码呢:

(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+[!+[]+!+[]+!+[]]]+[+!+[]]+([+[]]+![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[!+[]+!+[]+[+[]]]

其实表明的就是alert(1)http://www.jsfuck.com/ 这个奇葩网站专门干这事的,原理就是使用的类型转换,把咱们熟悉的代码变成了这样,真是骚操做。

4.font-size

font-size在css中使用频繁,浏览器一般会默认设置成14px或者16px。若是设置成0呢?

.foo {
    font-size: 0;
}

<span class="foo">foo</span>

文字真的就没有了,这符合预期,但若是设置的值小于12(非负),各浏览器处理稍有不一样,chrome/edge浏览器会设置最小字体为12px,firefox严格按照给定的值来显示。 font-size:0的用处 在布局过程当中,常常会生成空白字符,例如:

<div class="box1">
    <span>a</span>
    <span>b</span>
</div>
<div class="box2">
	<img src="xxx.jpg">
</div>

box1和box2高度实际上是略高于实际的内容的,并且box1的两个span中间有间隙,没有紧挨着,由于空白字符的缘由,引用css规范说明:On a block container element whose content is composed of inline-level elements, 'line-height' specifies the minimal height of line boxes within the element. The minimum height consists of a minimum height above the baseline and a minimum depth below it, exactly as if each line box starts with a zero-width inline box with the element's font and line height properties. We call that imaginary box a "strut.",大意就是一个块级容器元素内容区域由inline-level元素组成的;而这些linline-level元素被放在每一行的line-box里面,line-box高度是由它全部子元素的高度计算得出的。浏览器会计算这一行里每一个子元素的高度,再得出 line-box 的高度(具体来讲就是从子元素的最高点到最低点的高度)。默认状况下,一个 line-box 老是有足够的高度来容纳它的子元素,而每个行框能够想象为默认会有一个宽度为0的空白节点,字体大小和行高会影响该节点,具体规范可查看 https://www.w3.org/TR/CSS2/visudet.html#line-height。 说完规范后,再来分析一下box1和box2的效果,为了便于理解,下面的代码手动加入了一个[x]符号,表明strut:

<div class="box1">
	<span>a</span> <span>b</span>
	[x]
</div>
<div class="box2">
	<img src="xxx.jpg">[x]
</div>

box1的两个span发生了换行,至关于中间有个空格,所以会有间隙,若是span不换行,那么中间的空隙也就没有了。因为存在[x],并且vertical-align:baseline的缘故,span/img和[x]的baseline对齐以后,[x]处于baseline如下的部分会撑开整个line-box的高度,所以会外部块级容器的高度会略高一些,说得不清楚,咱们仍是上一张图来讲明吧: 图片描述 粉色的线是图片的baseline,红色的线是strut的baseline,对齐以后strut的baseline下面还有一部分高度,box的最终高度就是img的顶部到strut底部之间的距离。分析完成因后,终于可使用font-size:0来解决问题了,在box上设置font-size:0后,strut就至关于没有了,所以就不存在高度撑开的问题了,固然这里还能够改变img的vertical-align属性来修复这个问题,好比img{ vertical-align:bottom; }那么img的粉色线就跟strut的底部对齐了,也就不会撑开容器高度了,这里说了box2,box1原理差很少,这里涉及到vertical-align的知识了,可查阅https://www.w3.org/TR/CSS2/visudet.html#vertical-align 进行了解。

5.width&height

width和height也是能够设置成0的,效果也是符合咱们预期的,但若是咱们的意图是想经过设置0把元素隐藏的话,通常状况下会采用以下方案:

.visually-hidden {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  width: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
}

有些屏幕阅读器会忽略width和height等于0的元素,所以这里特地设置成1px,固然关于元素的隐藏还有不少实现方案,有兴趣可参考【1】;

6.line-height

line-height及行高,原意是baseline之间的高度,在css中就是一行的高度。默认状况下,line-height是跟具体字体定义相关联的,通常都是font-size的1.x倍,若是设置成0,在不一样类型元素上的状况是不同的,可分为以下三种状况: 1.)非置换行类元素 line-height定义的是最终参与计算line-box(行盒)的高度的值,而不会影响non-replaced inline element(非置换行类元素)的实际高度;

<style>
    .foo {
        line-height:0;
    }
</style>

<div>
    <span class="foo">a</span> // class为foo的span高度不会受样式影响
</div>

2.)行类块级元素 会影响元素高度,若是line-height设置为0,那么该元素高度就变成了0,若是设置了height,那么height将会起做用; 3.)块级元素 当块级元素包含inline-level(display:inline|inline-block)元素时, 行高定义的每个line-box(行盒)的最小高度,此外height也能影响块级元素的最终高度,height比line-height有更高优先级,当没有height状况下,line-height起做用; 上面提到了line-box,若是有不了解的同窗,能够看一下文末的连接【2】;

7.transform

transform经常使用来作样式变化和动画,在有时候,会设置成以下形式:

transform: translateZ(0);

这实际上是为了启用GPU加速渲染,元素会单独在一个绘制层(Layer)里进行绘制,而不会对其余层产生影响,所以也就少了不少计算和合成的功能,并且不会阻塞主线程,动画会更加流畅,固然元素设置太多会致使性能下降,由于须要内存的维护。

参考资料:

【1】https://css-tricks.com/comparing-various-ways-to-hide-things-in-css/ 【2】https://www.w3.org/TR/CSS2/visuren.html#line-box

相关文章
相关标签/搜索