抓些细节bug
2013-05-16
为了避免传达错误的信息,先声明一下:本文没想黑这本书,只是想协助它提高质量。
原本作者在JavaEye连载《JavaScript内核系列》的时候就有读过,大概内容的层次心里已经有数。进化为实体书后质量已经颇有提升。
http://abruzzi.iteye.com/blog/646947
怎么说呢…感觉是粗读感觉良好,细抠直想吐槽 >_<
如果不是特别在意细节问题,这系列文章和这本书不妨一读。
本来很多核心概念直接读ECMAScript规范就能理解清楚了,但喜欢或者说习惯读规范的同学不多;大家似乎更喜欢读别人消化过规范后写的“讲解”。
同类的文章我可能会更推荐的是Dmitry Soshnikov写的ECMA-262-3 in detail 和 ECMA-262-5 in detail系列: http://dmitrysoshnikov.com/ecmascript/
拿到实书后粗略读了一次。赞美之词啥的在作者原本的连载系列里也可以找到,后面肯定也还会有人赞,这里就不多写啥赞了。
有一点得赞一下:作者原本连载的时候这系列叫做《JavaScript内核系列》,而成书后改名为《JavaScript核心概念及实践》。这名字改得好,比原来的名字更符合书中内容。
另外我喜欢这本书的一点是文风朴实,直入主题,读起来很省力。不像某些糟糕的书一堆主观牢骚发完之后没留下啥营养(如《Modern JavaScript》 http://book.douban.com/subject/7070623/ )。
这本书相对其它国内的JavaScript书比较有特点的是第14、15章,讲述如何在自己的应用中嵌入SpiderMonkey、V8、Rhino用作脚本引擎。比较实用。虽然这三个引擎的官方文档都有详细说明如何嵌入它们,但我读过的其它国内出的JavaScript书很少提及这部分话题。
下面贡献个bug报告。针对该书的2013年5月第1版第1次印刷的状态。希望后面的印刷能修正这里提到的部分问题吧。
主要都是些细节而已。取决于读者对细节的认真程度,这些可以算是“无伤大雅”吧…
前言、第一章等地方都没有提到这本书所描述的JavaScript遵循的是哪个版本的ECMAScript规范。从内容可以推断作者想写的是ECMAScript 3的。应该显式说明。
另外在提到某些具体实现,像SpiderMonkey、V8、Rhino之类的时候,应该明确指出其版本。不然读者可能会发现书中所说的跟现在可以获取的最新版不一样,而感到困惑。
前言Page 3
Array不是ECMAScript的保留字,而只是一个内建类型而已。
ECMAScript 5th, 7.6.1规定了保留字的列表。
http://www.ecma-international.org/ecma-262/5.1/#sec-7.6.1
前言Page 4
前Sun的Rhino
=> Mozilla Rhino
在Rhino的开发过程中Sun只提供了少量技术支持;把Rhino整合进JDK6时Sun也只做了少量剪裁。所以Rhino不能算是Sun的。
前言Page 5
JSEvaluator 大小写统一
Page 2
1.1.2
为啥这段看不出浏览器之战跟JavaScript的关系…应该带过一下的。
Page 4
代码例子没有区分代码与运行结果
Page 5
最顶上的代码例子是不是漏了delete obj.property; 之类的?
1.2.2
这小节所讲的都不是“弱类型”而是“动态类型”的特点。作者掉进了一个经典坑里。JavaScript既是动态类型也是弱类型的,但这俩名词指的并不是同一件事。
Page 7
“在JavaScript中一切都是对象”
=> 在JavaScript中一切都可看作对象。
1.2.4
“”……无需解释“与”……无需编译“
=> 这两句话接在一起感觉怪怪的。作者或许应该改改表述方式。
“我们将在第9章和第10章对两种方式进行更深入的讨论”
=> 这句话似乎是删漏了?
Page 12
goodbay
=> goodbye
ThunderBrid
=> ThunderBird
Page 18
Kernal
=> Kernel
Page 19
““重载”了Object的toString()方法”
=> “重载”应该是“覆盖”
Page 21
Wrold
=> World
Page 22
“引用指向的是地址,也就是说,引用不会指向引用本身,而是指向该引用所对应的实际对象。”
这句话读着太别扭了。其实要说的就是:引用里存的值是对象地址而不是对象的实际内容。
Page 23
结合率
=> 结合律
另外书中“运算符”与“操作符”其实说的是同一件事,但前后没统一起来。例如说第33页3.2、第78页的8.1.2写的是“new操作符”。应该统一选其中一种说法。
Page 29
3.1.1 这段描述较接近ECMAScript 3的规定,但现在的主流标准已经是ECMAScript 5.1,属性并不只是个值了。Named property有两种,一个是named data property,就跟ECMAScript 3里的属性一样;另一种是named accessor property,这个是新的。
Page 30
全局变量在创建的时候确实会同时触发创建同名的全局对象的属性,但全局变量与全局对象的属性并不完全等价,有很细微的差别:
用var声明的全局变量,创建出来的全局对象的属性的内部属性[[Configurable]为false,所以这个属性不能delete。
不用var声明就直接赋值创建出来的全局对象的属性的内部属性[[Configurable]为true,所以这个属性可以delete。
以前也有人解释过,参考这里 http://stackoverflow.com/questions/12692887/are-the-terms-global-property-and-global-variable-synonyms
原型不是JavaScript特有的概念。这块设计受Self语言的影响。而Self采用基于原型而不是基于类的面向对象模型又是80年代末的一种学界潮流。
Page 31
“继承及重载”
=> 应该是“继承及覆盖”。重载是overload,覆盖才是override
Page 35
这页的point例子很糟糕诶:
function point(left, top) {
this.left = left;
this.top = top;
// handle the left and top
return { x: this.left, y: this.top };
}
var pos = point(3, 4);
这样就把left和top都写到全局对象上了…是个典型错误。
后面一个代码例子里的json应该改为obj之类的,避开json这个词。漏网之鱼吧?
Page 36
3.4 JSON
“可以表达任意复杂的数据形式”
=> 这里应该提一下JSON无法表达自我引用的对象图。像这样:
var obj = {
self: null,
name: "Me"
}
obj.self = obj;
执行完这句赋值之后的对象图里包含一个自我引用,这种数据就无法用JSON来表示。
类似的,如果要表达一颗树,树的父子节点间要有双向引用,那JSON也无法表达。
回到完整的JavaScript对象字面量(注意不是JSON)语法,SpiderMonkey以前还有过叫做sharp variable的特殊语法用于支持带循环的或者其它非树形对象图的字面量,但在较新版本里已经去除了:http://whereswalden.com/2012/01/25/spidermonkey-no-longer-supports-sharp-variables/
“而值可以是任意的JavaScript对象”
=> 而值可以是除Function以外的任意JavaScript值
书里其它地方也说了基本类型的值不是对象,这个地方应该避开“JavaScript对象”的说法。
“简单对象String, Boolean, Number, Null“
=> 应该写为”基本类型“。基本类型的值不是对象。
Page 39
最底下的结果里function ()应该是function p()才对。这个就算在SpiderMonkey上也应该显示函数名字。
Page 47
curring
=> currying
Page 49
“字符串也可以作为数组的下标”
=> 这段后面的解释不够准确。作者没举个这样的例子:
var a = [1, 2, 3];
var i = a["2"]; //=> 3
Page 52
Cisio
=> Cisco
Page 54
“扩展了JavaScript的内置对象Array”
=> 扩展的是Array.prototype而不是Array。这俩是不同的对象。
Page 55
“但是不影响全局对象”
=> 应该跟前文统一起来,写为“但是不影响Array.prototype,也就不会对Array的实例造成全局影响”之类的。
Page 67
“用简单的语句来描述JavaScript中闭包的概念”后面的那句
=> 这句话感觉完全不对,前后不相关。函数内是否能定义函数,跟函数是对象、对象是属性的集合一点关系都没有。在函数内定义的函数是作为activation的属性,而不是作为函数对象的属性存在的。
Page 72
7.3.1
“一般采用的是引用计数的形式”
=> 正好相反,能叫得上名字的JavaScript引擎都不曾用过引用计数作为自动内存管理的主要实现机制。大家都是一开始就用mark-sweep。请参考这里:
http://hllvm.group.iteye.com/group/topic/37596
实际上后面第178页介绍SpiderMonkey的时候作者也说了它用了标记-清除式GC…前后没对应上。
Page 81
“需要给Base.extend方法传入一个JSON对象”
=> 作者应该在很多地方都改掉了原本误写为“JSON对象”的地方,这是漏网之鱼?
可以参考原本没改掉误用“JSON”一词时的状态:http://hzjavaeyer.group.iteye.com/group/wiki/2272-JavaScript-Core
Page 92
“函数的运算对外部无副作用”
=> 数学上的函数是这样,但JavaScript的不是。其实后面紧接着的例子稍微改改就可以体现出副作用了:
var outter = function () {
var x = 0;
return {
printMe: function () {
print("x = " + x);
},
incr: function () {
return x++;
}
}
}
var a = outter();
print(a.incr()); // 0
a.printMe(); // 1
print(a.incr()); // 1
a.printMe(); // 2
这样修改过的例子里,printMe和incr两个函数捕获的是同一个环境,因而会共享状态。副作用会在共享捕获的环境的函数间可见。这算不算“外部”呢?
(咦?豆瓣的书评里加不了格式…代码缩进全被吞了orz)
Page 93
“这两个语句的意义是一样的”
=> 严谨说是“几乎一样”。function declaration会被提升而var赋值的function expression不会。
例子很简单,像这样的一段顶层代码:
foo(); //=> ok
bar(); //=> TypeError: undefined is not a function
function foo() { }
var bar = function () { }
Page 102
“成为Y-结合子”
=> 称为Y-结合子
Page 104
原型属性是一种内部属性。ECMAScript规范说了内部属性没有名字,在规范中使用一些特殊名字来指定内部属性。原型属性的特殊名字是[[Prototype]。用这个是最准确的。__proto__只是某些JavaScript引擎的实现细节,不过很多书啊啥的都喜欢就直接用__proto__这个名字,也罢。
紧接着后面的例子在对象字面量里用了__proto__属性,这就不太好。ECMAScript 5用Object.create()来指定prototype创建对象是比较靠谱的做法。
Page 107
Obejct
=> Object
Page 109
golbal
=> global
Page 112
[[scope]
=> [[Scope]
另外从2010年中开始,SpiderMonkey也不再支持__parent__属性了
http://whereswalden.com/2010/05/07/spidermonkey-change-du-jour-the-special-__parent__-property-has-been-removed/
Page 165
JavaSccript
=> JavaScript
SpidlerMonkey
=> SpiderMonkey
这页里“解析、执行JavaScript代码”的写法乍看感觉不太好,但想想也没写错。本来是想说“解析”应该是“解释”,但作者在这里的本意似乎就是说parse,用“解析”是对的。
Page 166
alter
=> alert
Page 177
14.2
“SpiderMonkey是一个C语言实现的JavaScript引擎”
=> 作者当时写这段的时候SpiderMonkey或许还是用C实现的,但现在的SpiderMonkey已经整个改为用C++实现了(dtoa.c是少有的还在用.c后缀的例外)。
14.2.1
“一般而言,编程语言的虚拟机是针对某种指令的解释器”
=> 这个…显然虚拟机不一定要用解释方式来实现。紧接着后面介绍的V8里就没解释器啊。
14.2.2
“SpiderMonkey引擎是一个快速的解释器,也就是一个虚拟机”
=> 不知道该如何吐槽好…大概不要“也就是”就好了。
ECMA 262-3
=> ECMA-262 5th
这个大概是作者原文成文的时候SpiderMonkey还没宣称自己实现了第5版规范吧。SpiderMonkey是从2009年底开始逐步向第5版规范靠拢的。
Page 179
提到V8实现ECMA-262-3的地方也是,现在的V8实现的是ECMAScript 5.1,外加少量ECMAScript Harmony的功能。
Page 189
在介绍嵌入Rhino时应该提一下JSR 223。javax.script包下的API都是JSR 223引入的。
Page 191
invoceFunction
=> invokeFunction
Page 232
A.1
提倡用字面量是好的。但要说“new关键字在JavaScript里表示的含义与在传统的面向对象的语言中的含义完全不同”感觉不对。JavaScript的new表达式的抽像意图与Java/C#之类的new一样:
1、创建空的新对象,进行默认初始化
2、调用构造器,进行用户指定的初始化
只是细节不同而已。JavaScript里,能跟在new后面的那个叫做“构造器”。只不过所有在JavaScript里声明的函数都可以当作构造器用,所以看似new是“作用于函数”(书中第78页),但实际上这里应该区分开构造器与函数的概念。JavaScript同时还允许宿主环境提供宿主函数,而这些函数就有可能不能当作构造器使用,此时构造器与函数的区别就表现出来了。
有实现内部属性[[Construct]的对象才可以用作构造器:
http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.2
就算Java与C#的new在细节上也不是完全一样。
不知道书中这个“完全不同”指的是哪个层面上完全不同。
A.2
光介绍eval可以用但不说eval有啥坑略危险…
大概就先这样。