Notes On <Professional.Javascript.For.Web.Developer> - 1

来源:互联网 发布:起点中文网网络异常 编辑:程序博客网 时间:2024/06/05 10:59

既然原版为英文,我在笔记里就写中文算了,不然基本上和剽窃差不多。


javascript-book-cover


Chapter 2:ECMAScript Basics


语法(略)

变量:

文中除了变量命名和声明的规则外,有提及三种在编程上主流的三个命名变量的法则:

Camel Notation,

Pascal Notation

Hungarian Type Notation


关键字(略)

保留字(略)

基本值和引用值(略)

基本类型:

在Javascript中,基本类型有undefinedbooleannumberstringnull,null有一点特别,它其实表示的是Object类型的变量没有指向任何实际值。


而object是下面会提到的引用类型变量,那么为什么null被放在了基本类型的类别里呢?这要从Javascript里变量在内存的存储方式说起。一个变量可以存储的值有两种情况,一种情况是原始值(primitive values),另一种是引用值(reference values),简单地讲,Javascript运行环境会在内存里划分出两个不同的区域,一个的数据结构是栈(stack),另一个是堆(heap)。栈用来存放各种变量,如果一个变量的值是原始值,那么它的值就直接放在这个栈里;而如果它存储的是一个引用类型(所有的引用类型都是object),那么它在栈里所保存的值实际上是一个指向堆的地址,类似于C语言里的指针,而这个object的实际值则是存放在了堆里面。这样做的原因是栈的查找速度快,可是要求每个元素的大小是预先知道的,而堆则可以灵活地改变存储元素的大小,但是也就因此而查找效率低。



如果一个变量的值是null,也就意味着它是object类型,可是它目前没有指向任何对象,它保存的地址是个空地址,所以这个值是一个长度固定且唯一的值,所以它被算作是原始值,因此null是基本类型。


typeof操作符可能返回的值只有六种可能(这里原著没有提及function):

"undefined"如果变量是Undefined"boolean"如果变量是布尔值"number"如果变量是数字,包括NaN和Infinity"string"如果变量是字符串"object"如果变量是任何引用类型或者是null"function"如果变量是一个function的名字



通常有两种情况会得出undefined值,一种是声明一个变量但始终没有赋值给它,另一种是声明一个函数但没有返回任何值,后者可以见下面代码:

function testFunc() {//leave the function blank}alert(testFunc() == undefined); //outputs “true”



typeof操作符有一个问题,它对没有声明过的变量也会返回undefined值,所以通过它不能够区分一个未声明的变量和一个声明但未赋值的变量。


null被编译器认为是等同于undefined的。


Number类型的变量,有一个特别的值:NaN。但是通常不能够用 ==NaN来判断一个数字变量的值是否无效,而是要使用isNaN()函数,这一点与Actionscript是一样的。另外也还有一个特别的值就是Infinity,虽然不怎么用,不过应该要知道,与之相关的还有几个常量:Number.MAX_VALUENumber.MIN_VALUENumber.POSITIVE_INFINITYNumber.NEGATIVE_INFINITY,和一个函数用来判断一个变量是否是无限的:isFinite()


另外,Number类型的值可以有不同的计数法,十进制是最常用的,还有八进制(octal)和十六进制(hexadecimal),声明的方法分别是:

var iNum = 070;var iNum = 0x1f;


不过要注意,所有的数学运算符号都是将数字转换成十进制进行的,返回的也是十进制


浮点数占用的内存地址是64位,而且可以用科学计数法:

var fNum = 3.125e7;var fNum = 3e-17;


浮点类型的数值在Javascript中是以字符串形式存储的,只有当需要计算时才会再转换成数字。


类型转换:


首先是将其他类型转换为string类型,这在呈现数据的时候常常用到,尤其是将数字按照不同格式输出,其实在Javascript里,所谓的基本类型都是一种“伪引用类型”,因为像string,number这样的类型变量其实也有原型,有属性和方法,在Javascript里,一切都是这样的。所以Javascript为number和boolean提供了重写的toString()方法(下面你会看到,这个方法是多有的对象都有的),现在就看下将数字输出为字符。


number类型的toString()有两种模式:默认模式(default mode)和基数模式(radix mode),默认模式没什么好说的,就把数字按照十进制输出,基数模式下,需要给它传递一个参数来制定基数,2就是二进制,8就是八进制,16就是十六进制。

var iNum = 10;alert(iNum1.toString(2)); //outputs "1010"alert(iNum1.toString(8)); //outputs "12"alert(iNum1.toString(16)); //outputs "A"


将字符串转换为数字则可以有三种办法:parseFloat()parseInt()Number(),三者的工作原理有差别。

parseInt()是一个全局函数,它会处理字符直到遇见一个非数字然后停止,将前面的字符转换成数字,它可以识别并区分十进制,八进制和十六进制:

var iNum1 = parseInt("1234blue"); //returns 1234var iNum3 = parseInt("22.5"); //returns 22var iNum4 = parseInt("blue"); //returns NaNvar iNum2 = parseInt("0xA"); //returns 10var iNum1 = parseInt("010"); //returns 8


但是parseInt()也接受另一个参数,就是指定它的基数是二进制,八进制还是……,而且这个参数的决定权高于第一个参数中的格式:

var iNum3 = parseInt("010", 10); //returns 10


parseFloat()则完全不理会其他禁制的格式,而且遇到任何英文字符都会将其视为非法字符:

var fNum2 = parseFloat("0xA"); //returns NaN

下面要介绍的方法称为强制类型转换(type casting)。做法和高级语言的相似,用类型名作为函数使用:Boolean(value),Number(value)和String(value)。


关于使用Number()进行强制转换,要注意的是,它是整体考虑所有的字符的,一旦有非法字符出现,或者整个字符串不能转换成数字,那就返回NaN。

var n1 = Number(false); //0var n2 = Number(true); //1var n3 = Number(undefined); //NaNvar n4 = Number(null); //0var n5 = Number("5.5"); //5.5var n6 = Number("56"); //56var n7 = Number("5.6.7"); //NaNvar n8 = Number(new Object()); //NaNvar n9 = Number(100); //100


转换为布尔类型时:空字符串""、数字0,还有null都会被转为false

var b1 = Boolean(""); //false – empty stringvar b2 = Boolean("hi"); //true – non-empty stringvar b3 = Boolean(100); //true – non-zero numbervar b4 = Boolean(null); //false - nullvar b5 = Boolean(0); //false - zerovar b6 = Boolean(new Object()); //true – object



操作运算符:

等于运算符(Equality operators):

在进行比较之前,进行的隐性转换:

将布尔型转换为数字型,false为0,true为1。如果是字符串和数字相比较,先将字符串转换为数字。如果是object和字符串相比较,先将object转换成字符串。如果是object和数字相比较,将object转换成数字

比较的规则:

null和undefined等价。null和undefined不能转换成其他任何类型。如果有NaN作为参数,那么'=='将返回false,'!='将返回true。注意:NaN不等于NaN。如果两边都是object类型,那么就比较两者的地址,如果相同,就返回true,也就是意味着两个变量指向同一个对象。


引用类型:

所谓引用类型(reference type)可以说就是类(class)。但是ECMA-262标准里并没有出现任何与class有关的术语,所以官方地说,Javascript没有类这个概念,而只有对象(object),但是,逻辑上讲,它的很多特性使他其实就等价于类,至少差不多。Object类有一些基本的属性:constructorprototype,和基本的方法:hasOwnProperty()isPrototypeOf()propertyIsEnumerable(),toString()和valueOf(),其中propertyIsEnumerable()的意思是该变量是否可以通过for...in来枚举。在Javascript中Object是其他一切引用类型的基类。



布尔类型和数字类型最好不要使用类,而是使用基本类型,不然在转换成布尔类型时,会有问题。


操作符:

为了弥补typeof操作符的能力的局限,Javascript提供了instanceof操作符,。

void操作符,可用于屏蔽返回值。


逻辑操作符!,&&和||的规则:


Logical Not: !

If the operand is an object, false is returned.If the operand is the number 0, true is returned.If the operand is any number other than 0, false is returned.If the operand is null, true is returned.If the operand is NaN, true is returned.If the operand is undefined, an error occurs.

Logical AND: &&

If one operand is an object and one is a Boolean, the object is returned.If both operands are objects, the second operand is returned.If either operand is null, null is returned.If either operand is NaN, NaN is returned.If either operand is undefined, an error occurs.

Logical OR: ||

If one operand is an object and one is a Boolean, the object is returned.If both operands are objects, the first operand is returned.If both operands are null, null is returned.If either operand is NaN, NaN is returned.If either operand is undefined, an error occurs.

语句(略)

函数:

与Actionscript一样,Javascript的函数其实也是用class来实现的,所以可以使用类的方法声明定义函数:

var doAdd = new Function("iNum", "alert(iNum + 10)");

但是这样做的效率非常低,避免使用。


函数对象有预定义的一些属性和方法,属性包括argumentlength

正是因为一个function其实是对一个Function类型对象的引用,重定义只会导致该变量指向后定义的Function对象,所以Javascript不支持函数重写,只能通过argument属性来动态判断需要执行的逻辑,其实这与C++中的函数重写机制完全是两回事,能力也远不及后者。C++等高级语言中,编译器会根据一个函数的函数名,函数返回值和参数列表来生成一个函数签名,因为参数的类型也会影响函数签名,所以同名函数可以具有相同个数参数而只是参数类型不同。Javascript与Actionscript做不到这点。

Javascript也不像高级语言那样,在函数呼叫过程中,会对传入的参数数量进行验证,将它与函数定义中的变量数量进行比配,Javascript并不做这样的事情,它完全接受传入的任意多个参数,如果少于函数定义,那么余下的就被作为undefined,多余的话,就被直接忽略。

因为Function也是继承自Object,所以它也有valueOf()toString()两个方法,它们返回的都是它的函数体,这点在debug时或许会很有用。


闭包:

这里讨论的是函数作用域的问题。

对闭包的一种理解:在函数的定义中出现的文本表达式(比如一个变量名)并不会被替换成它的实际值,这个动作是在函数被执行的时候才会做的。即使在函数中定义一个函数并执行它,也是如此。(Closures are functions whose lexical representation includes variables that aren’t evaluated, meaning that functions are capable of using variables defined outside of the function itself.)

var iBaseNum = 10;function addNumbers(iNum1, iNum2) {function doAddition() {return iNum1 + iNum2 + iBaseNum;}return doAddition();}


Chapter 3: Object Basics


面向对象术语(略)

使用对象:


前绑定与后绑定



对象的类型:

对象分为:Native objectBuilt-in objectHost object

Native object的定义是:被ECMAScript标准实现的那些独立于运行平台环境的Object。有:

ObjectFunctionArrayStringBooleanNumberDateRegExpErrorEvalErrorRangeErrorReferenceErrorSyntaxErrorTypeErrorURIError

其中Array与Date是用的最多的。



Built-in object的定义差不多,只是它们代表的是符合ECMAScript标准的程序的运行实体本身。Javascript只有两个这样的对象:一个是Global,一个是Math。其实所有的全局函数都是Global的方法。在Javascript运行的上下文中,这两个对象就已经存在了。Global有一个特别的函数eval(),它的作用和解释器本身一样,通过这个函数可以动态决定需要被解释的代码。


注意:尽量避免使用BOM的escape()函数或者unescape()函数,因为它们只能够对ASCII编码,应使用URI方法:encodeURI()


而Host object,指的是那些由运行平台环境来实现的object,所有的非Native object都是Host object。虽然作者没明确讲,在Javascript里常用到的Host object应该有documentnavigator,window还有console。


作用域:

很遗憾,Javascript的面向对象机制里,不支持私有属性,所有类的属性方法都是公共的(public)。也不支持静态(static)属性或方法,这一点与Actionscript不同。


类与对象的定义:

文中介绍了几种在Javascript语境下定义类的方法,但实际上Js对于类的实现远不及C++那类高级语言严谨,所以作者在这里提供的办法基本上是一些在Js里面,尽量向高级语言的类定义方式靠拢的做法。


因为对象可以动态增加属性与方法,基于Javascript的这一特性的一个自然的做法是:

var oCar = new Object;oCar.color = "red";oCar.doors = 4;oCar.mpg = 23;oCar.showColor = function () {alert(this.color);};


工厂范式(Factory paradigm):

function createCar(sColor, iDoors, iMpg) {var oTempCar = new Object;oTempCar.color = sColor;oTempCar.doors = iDoors;oTempCar.mpg = iMpg;oTempCar.showColor = function () {alert(this.color)};return oTempCar;}var oCar1 = createCar("red", 4, 23);var oCar1 = createCar("blue", 3, 25);oCar1.showColor(); //outputs "red"oCar2.showColor(); //outputs "blue"

面向对象的思维是,共用逻辑,但数据分开存储。这个做法的缺点就是,每个对象都将拥有自己的showColor方法的定义,而事实上它们是相同的,即是说,内存被浪费了。所以一个解决办法是:



function showColor() {alert(this.color);}function createCar(sColor, iDoors, iMpg) {var oTempCar = new Object;oTempCar.color = sColor;oTempCar.doors = iDoors;oTempCar.mpg = iMpg;oTempCar.showColor = showColor;return oTempCar;}var oCar1 = createCar("red", 4, 23);var oCar2 = createCar("blue", 3, 25);oCar1.showColor(); //outputs "red"oCar2.showColor(); //outputs "blue"

这样就使得方法的定义在内存中只有一份,但是有人认为这样的编码风格在语义上不严谨。


构造函数范式(Constructor paradigm):

function Car(sColor, iDoors, iMpg) {this.color = sColor;this.doors = iDoors;this.mpg = iMpg;this.showColor = function () {alert(this.color)};}var oCar1 = new Car("red", 4, 23);var oCar2 = new Car("blue", 3, 25);

通过使用this关键字,使编码看起来接近高级语言的类定义。缺点同初版的工厂范式一样,方法定义重复。


原型范式:

function Car() {}Car.prototype.color = "red";Car.prototype.doors = 4;Car.prototype.mpg = 23;Car.prototype.showColor = function () {alert(this.color);};var oCar1 = new Car();var oCar2 = new Car();

这个做法有很多缺陷,首先就是属性不能在声明时初始化,只能共用同一套默认值,再有,如果属性是一个引用值(reference),那么不同对象将指向相同地址。


原型做法有一个附带的优点,就是你可以通过instanceof操作符来判定一个对象是否是这个自定义类的实例:

alert(oCar1 instanceof Car); //outputs "true"


关于prototype,这里先做一个小实验来验证上面的结论:

function showColor() {alert(this.color);  }function Car(sColor) { Car.prototype.color = sColor;this.showColor = showColor;}var oCar1 = new Car("red");oCar1.showColor();// redvar oCar2 = new Car("blue");oCar2.showColor();// blueoCar1.showColor();// blue


当第二次在oCar1上运行showColor时,它的颜色属性已经变成blue了,因为在这之前运行Car的构造函数创建了oCar2,而构造函数直接修改prototype,导致所有实例的color值都被改变了。这也就解释了为什么作者说上一个办法只能用同一套值。



构造式与原型混合范式(Hybrid constructor/prototype paradigm):

function Car(sColor, iDoors, iMpg) {this.color = sColor;this.doors = iDoors;this.mpg = iMpg;this.drivers = new Array("Mike", "Sue");}Car.prototype.showColor = function () {alert(this.color);};var oCar1 = new Car("red", 4, 23);var oCar2 = new Car("blue", 3, 25);oCar1.drivers.push("Matt");alert(oCar1.drivers); //outputs "Mike,Sue,Matt"alert(oCar2.drivers); //outputs "Mike,Sue"

这个做法的思路是,用(模拟的)构造函数来赋值属性,方法则通过prototype来定义。


动态原型方法:

function Car(sColor, iDoors, iMpg) {this.color = sColor;this.doors = iDoors;this.mpg = iMpg;this.drivers = new Array("Mike", "Sue");if (typeof Car._initialized == "undefined") {Car.prototype.showColor = function () {alert(this.color);};Car._initialized = true;}}


这个做法的思路其实很简单,就是找一个“公共”的地方来标记类的方法是不是已经创建了,如果已经创建了就不再创建多次。只是这个“公共”的地方一开始看起来有点令人头晕,因为function在Javascript里也是对象,所以上面这段代码是利用了这一点,Car本身是一个function类型的实例,在这个实例上面附着一个属性:_initialized。所以需要注意的是,这个属性与利用Car()创建的Car对象是没有任何关系的。


>>>function Car(sColor, iDoors, iMpg) {
this.color = sColor;
console.log(typeof Car._initialized);
if (typeof Car._initialized == "undefined") {
Car.prototype.showColor = function () {
alert(this.color);
};
Car._initialized = true;
}
else {
console.log(typeof Car._initialized);
console.log(Car._initialized);
}
}


>>>var car1 = new Car('pink');

undefined


>>> var car2 = new Car('brown');
boolean
boolean
true


>>> Car._initialized;
true


>>> car1._initialized;
undefined



混合工厂范式(Hybrid factory paradigm):


这是作者并不推荐的方法,因为这个方法的最终版本也仍然选择将方法定义留在工厂函数内:

function Car() {var oTempCar = new Object;oTempCar.color = "red";oTempCar.doors = 4;oTempCar.mpg = 23;oTempCar.showColor = function () {alert(this.color)};return oTempCar;}var car = new Car();


它不同之处仅仅是使用new操作符来生成对象,关于new到底做了些什么,我需要在另外的文章里探讨下。


修改对象:

prototype这个概念和Actionscript的基本上一致,因为两种语言的语义都是ECMA制定的,它的魅力在于一个类即使已经被实例化,仍然可以在运行中的任何时候给它增加属性与方法,而这会作用到所有它的对象上。这个概念除了用户自定义的类型外,Js内建的所有类型也都适用,而且通过prototype还可以重写内建类型的方法,但在此之前最好保留该方法原定义的引用。在这本书中,作者称为“late binding”。但是作者建议这个特性最好不要轻易使用。


Chapter 4: Inheritance


实践中的继承(略)

如何实现继承:

有一种方法叫对象易容(object masquerading),它利用到call()与apply()函数……


我决定暂时跳过这一章,因为在我的Js代码中,我不太可能会用到复杂的类以及继承。如果我自己需要设计的功能已经有这样的复杂度,我会放弃Javascript而使用Flash。


Chapter 5: JavaScript in the Browser


HTML中的JavaScript:

为了更好的兼容XHTML,在script标签中,通常用type属性取代language属性。


SVG中的JavaScript(略)

浏览器对象模型:

window对象,对于window对象的方法和属性的引用中都可以忽略window关键字:

它有一个对象属性:history,history存储了用户浏览过的链接,它有一些方法:go(),back()和forward()。


document对象:

它其实也是window的一个属性,它可以用来访问页面中的HTML元素。


anchorsCollection of all anchors in the page (represented by <a name="anchorname"></a>) appletsCollection of all applets in the pageembedsCollection of all embedded objects in the page (represented by the <embed/> tag)formsCollection of all forms in the pageimagesCollection of all images in the page linksCollection of all links in the page (represented by <a href="somewhere.htm"></a>)

假设如下HTML代码:

<html><head><title>Document Example</title></head><body><p>Welcome to my <a href="home.htm">home</a> away from home.</p><img src="home.jpg" align="right" name="imgHome" /><form method="post" action="submit.cgi" name="frmSubscribe"><input type="text" name="txtEmail" /><input type="submit" value="Subscribe" /></form></body></html>

那么:document.links[0]可用于访问连接;document.images[0]或者document.images["imgHome"]可用于访问图片;document.forms[0]或者document.forms["frmSubscribe"]可用于访问表单。

通过document输出HTML代码要在该HTML文档并未完全被加载并显示之前,否则浏览器将会打开一个新的页面,其内容为被插入的HTML。

如果要通过document.writeln()添加一段含有<script>标签的内容,那么关闭标签</script>必须要分开:


<html><head><title>Document Example</title><script type="text/javascript">document.write("<script type=\"text/javascript\" src=\"external.js\">"+ "</script>"); //this will cause a problem</script></head><body></body></html>



在新打开的窗口写内容前,要先调用open():

oNewWin.document.open();

oNewWin.document.close();



location对象:

经常用于页面跳转。也可以用来操作与URL相关的数据。

其reload()方法有两种模式,一个是加载被缓存的内容(即传入false参数,也是默认情况),另一种是从server上加载。

navigator对象:

存储关于浏览器信息。

screen对象:

存储关于用户显示器信息。


Chapter 6: DOM Basics


何为DOM(略)

DOM支持(略)

使用DOM:

文档对象模型(DOM)是针对XML解析的一种方法或者说——标准。该标准制定了一些通用的操作XML的方法。而HTML被扩展支持XML标准之后,就可以使用DOM的方法来操作,即XHTML。

DOM Level1提供的方法有:

访问相对节点(firstChild,childNodes,parentNode等);

获得节点类型;

处理属性(attributes.getNamedItem("id").nodeValueattributes.item(1).nodeValuegetAttribute("id")setAttribute("id", "newId")等);

访问指定节点(getElementsByTagName()getElementsByName()getElementById()等);

创建处理节点(createElement()createTextNode()appendChild()removeChild()replaceChild(),andinsertBefore()等)。

当向一个文档添加节点时,该文档需要是已经被加载完毕的。

DOM 的HTML特性:

除了上面提及的DOM自己的标准所规定的功能外,DOM还针对HTML做了扩展,即是说这些功能可以在XHTML上使用(但不一定能够在其他的XML上工作):比如属性可以当成成员变量,还有一些对表格的扩展。

DOM遍历:

因为这个部分涉及的是DOM Level2的标准,并非所有浏览器都支持,当然这本书是06年的书,也就是6年前,现在的情况一定会大不相同,可是以我的经验,在实际中很少见到有这类操作的代码,所以我想跳过这里。


Chapter 7: Regular Expression

这是个很通用同普遍的语言特性,参考Actionscript或者PHP中的即可。(略)


Chapter 8: Browser and Operating System Detection


这个部分的问题虽然很实际,可是由于年代的原因,书中所讲的已经和现在的实际情况有所不同。所以我想暂时忽略这一章,关于这个话题,不如去参考网上的文章。


做个简单的实验:

<html><head></head><body><script type="text/javascript">var str=navigator.userAgent;document.writeln(str);</script></body></html>



chrome-version

Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7


ie-version

Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB7.2; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; BRI/2)


firefox-version

Mozilla/5.0 (Windows NT 5.1; rv:10.0.1) Gecko/20100101 Firefox/10.0.1


下面这个浏览器是我用Flash+AIR做的浏览器,其实这个程序的核心就是HTMLLoader:


air-htmlloader

Mozilla/5.0 (Windows; U; zh-TW) AppleWebKit/531.9 (KHTML, like Gecko) AdobeAIR/2.6 Win32


原创粉丝点击