犀牛书读书笔记-01
第一章,概述
这章是综述性质的,作者随意的给出了一些例子。
1. visibility = hidden, display = none。
function hide (e, reflow) { if (reflow) { e.target.style.display = "none"; } else { e.target.style.visibility = "hidden"; } }
这段代码展示了两种隐藏节点的方式,之前在项目中接触最多的是display=none,对visibility=hidden的方式没用过,还不知道这种方式和display = none有什么区别。
visibility = "hidden",元素会隐藏,但是不会把元素从渲染树种拿掉,它依然会部分渲染,只是不可见而已(变成完全透明,只会触发repaint)。
display = "none",那么元素就不会渲染,整个从渲染树中拿掉了。会触发reflow与repaint。
比如
中间的元素visibility = hidden
中间的元素display = none
其实我想问为什么中间那个所谓只会触发repaint的隐藏方式会傲娇的多出来一截。。。。。。显然这种情况下浏览器会reflow的。想想可能是因为我用了自动宽度。果然,这样就和谐多了。
使用固定宽度
2. Bind events with DOM node's "on" attributes。
给DOM元素定义事件最简单的方法是,给DOM节点以"on"为前缀的属性绑定一个回调。出于页面维护性的考虑,这种代码和HTML节点写一块儿玩法正常情况下很少会用到。
<div style = "border: 1px solid gray" onmousedown="(function(node){node.textContent = new Date().toString();})(this);">点击我文字会改变</div> <div style = "border: 1px solid gray" onmouseup="(function(event){event.target.textContent = new Date().toString();})(event);">点击我文字会改变</div>
3. addEventListener。
玩的更多的应该还是addEventListener。
<div id = "click_node">点击我看看</div> <script> window.onload = function() { var node = document.getElementById('click_node'); if (node && node.addEventListener) { node.addEventListener('mousedown', function(){ node.textContent = "mouse down on node"; }, false); node.addEventListener('mouseup', function(event){ node.textContent = "mouse up on node"; }, false); } } </script>
注意几个小问题,比如addEventListener的第一个参数是需要绑定的事件名,全小写的,而且没有”on”前缀。(如果兼容老版IE用attachEvent的话则需要加上on前缀)。第三个参数是用来指定是否把linstener注册在事件的capture阶段,一般都是false,也是使用element.onclick = function(){}这种绑定方式的的默认行为。
关于DOM事件的生命周期,可以参考一下这篇文章。这里插一句,文章中提到通过阻止没必要的事件冒泡可以提升性能,不过作者提到的event.cancelBubble这种方式并不被提倡(参考),应该使用event.stopPropagation()来替代这种方式。
第二章,词法结构
这章主要内容是JS程序的词法构成,并没有什么需要特别注意的。
1. JavaScript会认为它正在解析的程序代码已经是这种标准格式,不会再对标示符、字符串、正则表达式做标准化处理。
关于JavaScript与Unicode,我准备专门再写一篇总结文章。这个Normalization问题有时候挺烦人的,特别是要对字符串做细致处理的时候。ES6已经添加一个新方法String.prototype.normalize来提供这个功能,在此之前可以使用一些库来完成,比如unorm (https://github.com/walling/unorm)(by Bjarke Walling).
2. 在return、break、continue语句和随后的表达式之间不能有换行。
否则会出现难以调试的程序问题。正常人应该都不会这么写的。
3. “++”与“--”作为后缀的时候要与表达式在同一行。
同上,正常人不会另起一行写++--的。
第三章,类型、值和变量
1. JavaScript的数据类型分为两类:原始类型(primitive type)与对象类型。简单(原始)类型(simple types)有numbers、strings、booleans、null 和 undefined。剩下的其他的都是对象类型。
注意一下number、string、boolean是类对象(object-like)因为他们有一些预定义的方法,但它们是immutable的。
2. JS不区分整数和浮点数。所有的数字都是双精度浮点数,采用IEEE 754标准。JS中实际的操作(比如数组索引、位操作)则是基于32位整数。
由于是双精度浮点数,所以有喜闻乐见的 0.1 + 0.2 != 0.3。任何采用二进制浮点数的编程语言都会有这个问题。
关于实际操作,比如位操作时使用32位整数, 写代码时可以利用这个特性来把小数转换成整数,参考我这篇文章,《javascript中小数转换为整数》。
IEEE 754 中关于双精度浮点数的规定是1位数符、11位阶码再加上52位的有效数位。由于规范要求小数必须是标准形式,即小数点左边为1,所以用52位有效数字表示了53位的 信息。其所能精确表示的整数范围就是 2^53 ~
-2^53。
3. JS中的NaN有一点特殊:它和任何值都不相等,包括自身。
所以看到dojo库中有这样的equals,也是为了让两个NaN能够“相等”。
function equals(a, b){ return (a === b) || ( a !== a && b !== b); }
其实前面第一条讲类型时已经说过了。
5. JS中有“包装对象”的存在,前面提到的使用简单类型的方法实际上是通过创建临时的包装对象,然后再使用包装对象的方法。null和undefined没有包装对象,试图引用他们的任何属性都会抛错。
简单对象虽然看起来有一些属性,比如"123".length, 但是试图给它赋值属性(如"123".attr = 4)会被忽略掉 ("123".attr is still undefined)。
6. JS会在必要的时候把包装对象转换成原始值。如“==”认为原始值与其包装对象相等(包装对象会向原始值转换),但是“===”不会进行任何转换,所以不等。
对象原始值的比较(==)会试图把对象的原始值求出来再比较,如果是两个对象比较,则比较他们是否引用同一个对象。
var a = 'a', b = 'a', c = new String('a'); d = new String('a'); a == b // true
a === b // true a == c // true, 会转换
a === c // false, 全等不转换 c == d // false, 两个对象的比较
c === d // false, 没悬念
7. 对象向原始值的转换有些复杂。显示转换最简单,比如 Number("3"), Object(3)。
有一些常用的简单写法
+x //把x转换成Number, 相当于Number(x), 也可以写成 x - 0,不过没+x看起来好看 !!x //估计大家见得多了,相当于Boolean(x)
8. Object to Boolean 所有的对象类型都会转成true。
所以别太惊讶,new Boolean(false)实际上是会转换成true。
if(new Boolean(false)) console.info("yes, new Boolean(false) is truthy");
9. Object to Number/String, 涉及到toString(), valueOf()这两个方法,一般情况下转成string,toString会先调用,不行就valueOf;转成number则顺序颠倒。
10. “-“会把它的两个操作数都转换成数字。而对于”+“,很多时候+的操作数会被转换成字符串。
[1] + 1; // ”11“ [1] - 1; //0 new Date() + 1; //"Sun Aug 10 2014 00:20:41 GMT+0800 (China Standard Time)1" new Date() - 1; //1407601216775
11. 关于变量,JS中没有块级作用域,JS是函数作用域。所以有一种”声明提前“的非正式称谓(hoisting)。
实际上JS脚本虽然是解释执行,但是在逐行执行之前还有一个”预编译“的过程,这个过程会把context中声明的变量放入”栈“中,并未他们赋值为undefined,然后再开始逐条执行语句。
体会一下这个例子
a(); function a() { console.info(1);} a(); function a() {console.info(2);} a(); a = function () { console.info(3);} a(); //输出: //2 2 2 3
12. 使用var 声明的变量无法被delete删除,而作为对象的属性可以。
删除一个你自己声明的变量确实没有什么必要。
(function() { var a = '123'; console.info(a); delete a; console.info(a); })(); //输出两个”123“
13. 作用域链与闭包。
这个我决定再单独开一个坑。