JavaScript等同(==)与恒等(===)运算符
作者:nunumick 发布时间:07 Mar 2010 分类: front-end
Javascript开发中,需要与0,undefined,null,false进行等同比较时,我们知道,用’===’(恒等)比较靠谱,我是在第一次使用jslint时知道这点的,例如在Jslint中验证
会得到提示:
看看ECMA规范中是如何对==和===操作符进行定义的,了解其深层的规则和jslint提示的缘由
首先介绍==
11.9.1 等同运算符( == )
运算符规则如下所示:
1. 计算运算符左侧表达式;
2. 对第1步的结果调用GetValue;
3. 计算运算符右侧表达式;
4. 对第1步的结果调用GetValue;
5. 对第4步的结果与第2步结果执行比对(参考 11.9.3);
6. 返回第5步结果;
再来详细了解比对过程(11.9.3)
11.9.3 抽象的等同比对算法
假设有 x,y 进行比较 ,则有 x == y;
1. 如果xy类型不同,转至第14步;
2. 如果xy类型均为Undefined,返回 true;
3. 如果xy类型均为Null,返回 true;
4. 如果xy类型均不是Number(数值类型),转至第11步;
5. 如果x的值为NaN,返回 false;
6. 如果y的值为NaN,返回 false;
7. 如果x与y的数值相同,返回 true;
8. 如果x是+0并且y是−0,返回 true;
9. 如果x是−0并且y是+0,返回 true;
10. 返回 false.
11. 如果xy类型均为String(字符串类型),判断x与y是否有相同的字符(对应位置字符相同),是则返回 true,否则返回 false;
12. 如果xy类型均为Boolean(布尔类型),xy均为true或均为false则返回 true,否则返回 false;
13. 如果x与y引用同一个对象(object)或者xy引用的对象是Joined关系(参考13.1.2)则返回 true,否则返回 false;
14. 如果x为null且y为undefined,返回 true;
15. 如果x为undefined且y为null,返回 true;
16. 如果x类型为Number,y类型为String,先将y转换为Number类型,再进行比对,返回结果;
17. 如果x类型为String,y类型为Number,先将x转换为Number类型,在进行比对,返回结果;
18. 如果x类型为Boolean,先将x转换为Number类型,再进行比对,返回结果;
19. 如果y类型为Boolean,先将y转换为Number类型,再进行比对,返回结果;
20. 如果x类型是String或者Number且y类型为Object,先将y转换为基本类型(ToPrimitive),再进行比对,返回结果。
21. 如果x类型为Object且y类型为String或者Number,先将x转换为基本类型(ToPrimitive),再进行比对,返回结果。
22. 返回 false.
接着看恒等运算符(===)
11.9.4 严格等同运算符( === )
运算符规则如下所示:
1. 计算运算符左侧表达式;
2. 对第1步的结果调用GetValue;
3. 计算运算符右侧表达式;
4. 对第1步的结果调用GetValue;
5. 对第4步的结果与第2步结果执行比对(参考 11.9.6);
6. 返回第5步结果;
这几步和==运算符是一样的,我们着重来看第5步的比对过程:
11.9.6 严格性等同运算比对算法
假设有 x,y 进行比较 ,则有 x === y;
1.如果xy类型不相同,返回 false;
2. 如果xy类型均为Undefined,返回 true;
3. 如果xy类型均为Null,返回 true;
4. 如果xy类型均不是Number(数值类型),转至第11步;
5. 如果x的值为NaN,返回 false;
6. 如果y的值为NaN,返回 false;
7. 如果x与y的数值相同,返回 true;
8. 如果x是+0并且y是−0,返回 true;
9. 如果x是−0并且y是+0,返回 true;
10. 返回 false.
11. 如果xy类型均为String(字符串类型),判断x与y是否有相同的字符(对应位置字符相同),是则返回 true,否则返回 false;
12. 如果xy类型均为Boolean(布尔类型),xy均为true或均为false则返回 true,否则返回 false;
13. 如果x与y引用同一个对象(object)或者xy引用的对象是Joined关系(参考13.1.2)则返回 true,否则返回 false;
可以做如下概括:
==运算符在做比对时存在类型转换的可能,而===运算符只在同类型之间比对,是==的严格模式。
- 类型相同:进行===比对。
- 类型不同:基本类型Boolean、Number、String这三者之间做比较时,总是向Number进行类型转换,然后再比较;如果类型有Object,那么将Object转化成基本类型,再进行比较;null仅和undefined匹配;其他都为false。
根据规范和概括,我们不难明白:
- undefined只等于(==)undefined或null,null亦然
- 空字串(”) == 0 == false ,因为Number(”),Number(false) : 0
- true == 1 ,因为Number(true) : 1
- false===0 一定返回flase ,因为类型不同
恒等必定等同,等同未必恒等,需择之而用!
延伸阅读
标签:
javascript
,
operator
<<< EOF
有趣的兼容性测试-iframe文档对象获取
作者:nunumick 发布时间:04 Mar 2010 分类: front-end
前日对iframe的几种文档对象获取方式做了测试,发现一些有趣现象!
假设在页面嵌入如下iframe:
众所周知,iframe是内嵌窗口,我们可以通过多种方式获取iframe对象及其window\document对象(同域前提),不过哪些是哪些有时会搞不清,测试目的也是为了加深记忆。
比较常见的方法有以下几种,分别测试:
- A:document.getElementById(‘testFrame’)
- B:window.frames[‘testFrame’];
- C:document.getElementById(‘testFrame’).contentWindow
测试结果(非IE浏览器及IE8)
从测试结果及其比对结果可以看出,A得到的是iframe这个html标签对象,B和C得到的是iframe浏览器对象(window),有意思的是IE7及以下版本浏览器认为这两者是不恒等的
测试结果(IE7&IE6-)
有趣吧,从B==C可以看出,证明两者是同一类型及同一引用,参考设计规范,理应恒等(===)。只能说,M$遵循的不是规范,是寂寞!好在IE8现在已经玩不起寂寞了。
接着测试浏览器对contentDocument的支持情况:
- D:window.frames[‘testFrame’].document
- E:document.getElementById(‘testFrame’).contentWindow.document
- F:document.getElementById(‘testFrame’).contentDocument
测试结果(非IE浏览器及IE8):
测试结果表明:D和E得到的是同一对象,IE7及以下版本浏览器不支持contentDocument属性
测试结果(IE7&IE6-)
在使用contentDocument属性时需要考虑兼容性:
调整后的测试结果(IE7&IE6-):
附:测试页面
标签:
iframe
,
browser
,
compatibility
,
html
,
javascript
<<< EOF
条件注释区分非IE浏览器
作者:nunumick 发布时间:01 Mar 2010 分类: front-end
IE浏览器的条件注释虽不太常用,却异常强大,不仅可以用来区分IE浏览器版本
仅IE6:
仅IE7:
还可以牛13滴用来区分非IE浏览器:
猜想原理是条件注释后头的 <–> 在IE中被当作内部注释,而在非IE浏览器中会闭合之前的注释,从而起到区分非IE浏览器的作用,一般常用<!–>。
标签:
comment
,
compatibility
,
html
<<< EOF
DOM 操作的性能优化
作者:nunumick 发布时间:27 Apr 2009 分类: front-end
我们都知道,DOM操作的效率是很低的,尤其是用JS操作DOM的时候,性能的优劣更是引发问题和争论的焦点。
这里我们先分析一个很简单的例子:
假设我们要对上面的1000个或者更多的 li 元素进行抽样显示(隐藏)的控制,照常理来讲,我们会习惯性地去遍历这些元素,加上相应class,或者直接写上内联样式。至少在我看来这是最简单最高效的操作。然而 试验 结果却出乎我的意料,IE和firefox的显示结果都出奇的慢,去网上找了答案:这其实和文档的回流(reflow)很有关系,Nicholas总结了引起reflow的一些因素,其结论就是:
当对DOM节点执行新增或者删除操作时。 动态设置一个样式时(比如element.style.width=”10px”)。 当获取一个必须经过计算的尺寸值时,比如访问offsetWidth、clientHeight或者其他需要经过计算的CSS值(在兼容DOM的浏览器中,可以通过getComputedStyle函数获取;在IE中,可以通过currentStyle属性获取)。文章地址是:www.nczonline.net/blog/2009/02/03/speed-up-your-javascript-part-4/
怿飞师父在他的 blog 里也有提到更详细的因素,有兴趣的可以再去深入研究。
当然我写这篇文章的目的不是去翻译 Nicholas 的文章或是照搬他们的理论,而是想分享下我测试的心得:
- 现有主流浏览器对DOM的操作是各自为政的,IE&Chrome reflow过程很短或者说没有reflow,Firefox的reflow现象非常显著(脚本执行之后有很长时间去刷新DOM结构,而且是硬直时间),Opera次之。
- reflow过程中浏览器无法响应用户的操作。
- IE耗费的大部分时间是去获取DOM元素,比如在这个例子中我使用的是常规的getElementById方法,遍历获取元素的时候很耗费时间。而其他浏览器多半时间花在后续的DOM操作中。
- 只添加class不做样式处理不会触发reflow,只更改背景样式也不会触发reflow。
- Nicholas文中提到循环中的语句是会实时触发reflow的,但实际测试的结果是 reflow在执行环境结束之后才被触发, Dean Edwards 在其回复中也证实了这一点。
- 同数量的DOM操作,appendChild方法比更改样式的方法高效很多,也就是说display:none引起的reflow消耗比DOM节点的新增或删除要严重(firefox中尤其明显)
综上几点,再回顾刚才的例子,更高效的方法是用appendChild方法去增加需显示的DOM节点,于是便有了最后的 优化措施:
- 预存储UL下的所有LI元素
- 获取需显示的LI列表
- 删除整个UL元素,这里用的是UL父元素.removeChild(UL),千万不要用innerHTML=’‘,火狐里会挂的很惨。
- 重建UL元素
- 逐个添加步骤2获得的LI元素到UL
- 添加UL到UL父元素。
这其中又有Fragment的问题,究竟什么时候该用Fragment?因为fragment仅仅是一个过渡容器,在本质上不能提升代码的效率。所以很简单,当你需要临时容器的时候就用fragment,当你不需要临时容器的时候就可以不用。上面的过程中UL的重建等于是一个fragment,直到所有的LI都添加进去,才append到文档中。上面的例子中,如果用ul.innerHTML = ‘‘,则可以创建一个fragment作为替身……
另:例子 中用到的是原生的DOM操作方法,如果用集成库的话性能上还可再优化一些。
相关问题
一、appendChild和innerHTML的效率谁更高
appendChild和innerHTML的效率也是要分浏览器来考虑到,IE浏览器操作innerHTML的效率非常高,而FF和Opera会慢些,尤其是FF,当innerHTML内部元素很多的时候效率极低,毕竟innerHTML是IE首创并发扬光大的。所以可以这么讲:IE的innerHTML效率优于appendChild,而Firefox则是相反。使用的时候请权衡利弊。
二、display:none和fragment孰优孰劣
当display:none的元素较多时,用append方法的效率会更高。
标签:
dom
,
javascript
<<< EOF
文档类型对盒子模型的影响
作者:nunumick 发布时间:06 Nov 2008 分类: front-end
在设计网页时,文档类型(DOCTYPE)的重要性经常被人忽略,以至于页面在不同浏览器中的表现不一致。文档类型的出现就是为了规范现有的浏览器表现,在不使用文档类型的情况下,IE会用原始的模式去渲染整个HTML文档,盒子模型在这种模式下会有很大的变化。
我们可以从下面的例子看到DOCTYPE对盒子模型的影响,可以说文档类型是一个HTML文档的基石,如果没有基石或者基石不稳,建立再宏伟的HTML大厦也是徒然。
设定 div 容器的样式如下:
我们期望的结果是:容器总宽度在 5x2 + 10x2 + 200 = 230px;
如果未设置文档类型,在IE浏览器下的效果为:
DEMO: 怪癖模式下的IE盒模型
容器的总宽度为200px,显然并不是我们所期望的。
再看下设置了文档类型的情况:
DEMO: 设置文档类型后的IE盒模型
容器的总宽度为230px,这才是我们所期望的。
再使用IE内核之外的浏览器,比如FF查看这两个例子,可以发现总宽都是230像素。
为了盒子模型在各浏览器的表现一致,文档类型的声明在现代页面开发中是必需的。
—— 2014-09-12 更新 ——
IE9以上浏览器已经修复这个问题。