javascript--19E4X

e4x本质上是一种全新的语言,同正则表达式(Regular Expression)一样,它有自己完整的语法,但它们都是as3语法的一部分.
注:作者貌似对e4x很不满意,具体请看原文.
先准备要用的xml:
var thePeople:XML =<people>
<person name="Mims Wright" suffix="III">
<age>27</age>
<aka>Mims H Wright</aka>
<aka>Teh AWesoeomes!</aka>
<bio><!--[CDATA[This guy <b>rulz<b>!]]--></bio>
</person>
<person name="Roger Braunstein">     
<age>26</age>
<aka>Rog</aka>
<aka>That guy</aka>
<bio><!--[CDATA[Likes food.]]--></bio>
</person>
 </people>;
xmllist 对比 xml

首先,和AS2时候不同的是,在编辑器输入的XML可以作为一个XML类型的变量.然后, 需要了解这两个类: XML和xmllist .了解E4X xmllist是XML节点的列表。
差别是, XML必须有唯一一个根结点;xmllists则比较灵活. 可以有多个根节点。
比如, 上面的例子是XML ,因为它只有一个根结点. .而一个xmllist是由零或更多的根结点组成,如:

<age>27</age>
<age>26</age>
因为有两个根节点.所以它不能作为XML, 不过, 由于每个节点本身是一个根结点,可以认为它是一份XML列表。需要注意, xmllist也可以有一个(同xml )或零个节点.不过,大部份的基本e4x操作都是筛选操作(filter operations).分析xml和查找它的结点。通常,你要找到某个节点. 要做的操作是从很多结点得到这个结点。所以你会发觉,当做这些操作时,XML总是转化为xmllist,且大部分的时间是在处理xmllists .
基本操作
1.

thepeople.person.age
 

使用dot syntax按节点的名字选择子节点. 变量已经与根结点绑定,所以不用在表达式写 了. 上面的表达式连续进行了两次筛选(filter).
第一,得到根节点下的一切 节点,也就是说, 生成一个xmllist .
第二,在上面的基础上得到所有的节点.同样,也是一个xmllist对象.
两次操作的结果:

<age>27</age>
<age>26</age>
2.

thepeople.person.@name
 

用@符号来选择属性.首先查找根结点下所有 节点;然后查找这些结点下面所有的属性.这次得到不再是子结点的列表,而是结点属性的列表。但返回的仍然是一个xmlList对象,当然,不再是合法的xml。
3.

thePeople.person.(age >= 21)
 

这个操作很强,你可基于任意的标准来筛选要得到的结点. 再一个例子:

thePeople.person.(@name.charAt(0) == "R"); //Roger's <person> node
4.

thepeople.person [1]
 

使用括号来获取xmllists对象中的结点 ,同数组的索引类似. 这个办法在XML和XMLList都适用。结果:

<person name="Roger Braunstein">     
<age>26</age>
<aka>Rog</aka>
<aka>That guy</aka>
<bio><!--[CDATA[Likes food.]]-->
</bio>
</person>
当查找一个具体的节点,适当的操作可以返回所有匹配的结果. 例如,你可以建立一个筛选(filter)操作,它的返回应该只有一个匹配, 但你可能会发现每次测试都会返回第一个结果
例如,

thePeople.person.(@name == "Roger Braunstein").age; //XMLList with one node
thePeople.person.(@name == "Roger Braunstein")[0].age[0]; //XML
由于复杂e4x表达式会被自动添加[0],造成污染. 第二个表达式表现为XML→XMLList→XMLList→XML→XMLList→XML
5.

thePeople..age
 

用两个圆点表示在任何层级(而不只是下一层级)查找所有适用的结点, 例如,你可以在一个xhtml文件找到所有div标签,这样. 结果是:

<age>27</age>
<age>26</age>
6.

thePeople.*.age
 

* 操作符会选择所有子节点. 例如有一个结点中并非 , 但它仍有节点.那么这个结点也将被返回,此表达式并不关心第一代结点的名称.只是返回匹配第二代结点的所有第一代结点.

字符串和XML
当你对xml元素进行输出操作时,如果没有指明转换函数,那么将会调用toString()函数,你可输出像XML这样的”复杂内容(complex content)”或者字符串那样的”简单内容(simple content)”. 你可以使用hassimplecontent ( )和hascomplexcontent ( )来测试:简单内容是文本节点,属性节点或一个单一XML元素没有xml子节点(例如26) .

trace(thePeople.person.(@name =="Roger Braunstein").age); //26
上面表达式返回XMLList对象 ,但只有一个元素, 一个文本节点,然后XMLList.toString()打印出文本值26 . 同样地,

trace(thePeople.person[0].@name); //Mims Wright
用toString()作为一个简单的方式来获取属性或者单一XML中(如26)的文本的内容.如果你想打印出XML形式的内容,可以使用toXMLString() .

trace(thePeople.person.(@name == "Roger Braunstein").age.toXMLString());//<age>26</age>
可以用text()获得XML中文本节点内容 . 举例来说,thePeople.person[0].@name,得到文本节点值.

trace(thePeople.person[0].age.text()[0]); //27
这个表达式将返回一个XMLList对象中所有文本节点.我们可以像操作数组一样,我们可以得到所有的文本节点值:

trace(thePeople..age.text()); //2726

toString()输出没有增加空格. toXMLString()会增加,但由于他们都是文本节点,所以都同样输出:
27
26

函数形式属性(Function-style Properties)
(注 : 这一段实在是不怎么看的懂,附上原文,能看懂的指点一下。)
【In E4X, using a property name in an expression finds child nodes with the specified name, like thePeople.person. We want to be able to name XML nodes anything we want, so all the filters, calculated properties, and functions of XML nodes are implemented as functions. In other words, thePeople.person.length would look for nodes in the XML; but we can find the number of nodes with:
thePeople.length(); //2
All the tests and filters are implemented as functions, even if corresponding properties in other classes are implemented as implicit accessors.】

在e4x中 , 用特定的节点名称来表示子结点,如thepeople.person.但是, thepeople.person.length表示将在XML中查找节点,而我们可以用这样形式:

thePeople.length(); //2
来得到 节点的个数.

 

同义表达式(Synonyms) , 轴(Axis)
“轴” 是指运动的方向,如立体空间中x,y和z轴,但在XML中我们可
“轴” 是指运动的方向. 在空间中,我们可以在沿着x , y ,和Z轴移动(travel). 但是,在XML中,我们只可以纵向遍历子节点,以及兄弟节点,等等.
对比E4X 和 XPath ,E4X中的轴远不及(lose out)XPath中的强大,E4X中少了很多重要的东西.

一些基本”轴(Axis)”:
1.

attribute(name), attributes()
 

这些”轴”可以找到XML中相应的属性 . 第一个查找名称为name的属性,而第二个返回所有属性. 这两个表达式的功能可以简单的用@name和@ *实现 .

重要! “轴” attribute(name)效率比@name好很多! @name只能在所在查找的属性存在于所有节点中时有效. 这样会有很大的局限,
所以最好还是使用前者.

trace(thePeople.person.@suffix); //OK, list of all suffix attributes
 trace(thePeople.person.(@suffix == "III"));
 //ReferenceError: Error #1065: 变量 @suffix 未定义.at Untitled_fla::MainTimeline/Untitled_fla::frame1()
 trace(thePeople.person.(attribute("suffix") == "III")); //OK, Mims' node
2.

child(name), children()
 

这些轴用来查找给定节点的子节点, 返回第一代后代的节点. 第一个”轴”用来查找一个具体的名字的子节点.第二个”轴”返回所有的子节点,
和前的用的*运算符效果一样.
下面几个”轴”是等价的.

thePeople.child("*") == thePeople.children() == thePeople.*
我们可以使用一个变量名作为参数传递给一个”轴”函数.

var interestedNode:String = "age";
    trace(thePeople.person[0].child(interestedNode)); //27
你也可以像访问数组一样来使用child(),作者的意见是这样做会造成误解.
3.

descendants(name)
 

此”轴”返回一个名称与参数相匹配节点的所有子孙(descendants)(子,孙子等),和child()以及children()不相同 ,当没有参数传递的时候,它将返回所有的子孙(descendants). 也就是说,此”轴”默认情况等价于* . 它的简单实现为(..).

thePeople..age == thePeople.descendants("age")
    thePeople..* == thePeople.descendants() == thePeople.descendants("*")
4.

parent()
 

此”轴”返回节点的父级,它没有简单实现.一个例子:

var age0:XML = thePeople..age[0]; //<age>27</age>
   trace(age0.parent().@name); //Mims Wright
到这里,你可能注意到少说了兄弟节点(siblings)的查找,在AS2中可以用nextSibling和previousSibling很容易的实现,而在as3变得比较麻烦,你需要先找到父级节点,再取得父级节点的所有子节点,然后按索引查找兄弟节点.作者的一个方法:

var node:XML = thePeople.person[0];
//in general, for any node:
node.parent().children(); //all siblings
node.parent().*[node.childIndex() + 1]; //next sibling
node.parent().*[node.childIndex() - 1]; //previous sibling
The ancestor axis (parent, grandparent, great-grandparent, etc.) is also missing from E4X.

节点类型(nody types)

除了上面所说的,有些”轴”只能操作特定类型的节点.实际上,XML中的节点类型有:元素(element)、注释(comment),文本(text),或者处理指令(processing instruction)。这意谓着那些输入的XML变量有时候可能并不是有效的XML文档(documents),例如只有一个文本节点的XML变量就不是有效的XML文档.你可以用nodeKind()函数来获得节点的类型,此函数返回”element”,”comment”,”text”,”processing-instruction”,注释(comment)和处理指令(processing instruction)会被忽略(ignore)。

轴elements(), comments(), text(), 和processingInstructions()等同于Children().

创建或者更新值

E4X中XML变量是可写的(writeable),你可以修改已经的属性(attributes)和元素(elements)的值.如:

thePeople.person[1].age = 80; //sets my age to 80
thePeople.person.age = 80; //ERROR! you can't set multiple elements with one assignment
thePeople.person[1].@suffix = "Sr."; //set my suffix to Sr.
当然,你也可以创建新的XML节点,在原来的变量基础上再敲几下键盘而已,不过,e4x 充许像下面这样嵌入变量来定义XML变量:

var names:Array = ["Alice", "Bob", "Ivan"];
var newPerson:XML = <person name={names[int(Math.random() * names.length)]}></person>;
不仅仅在属性中可以使用事先定义的变量,节点名称,整个节点都可以预先定义。

var nodeName:String = "age";
var newAge:XML = <{nodeName}>{Math.round(Math.random() * 100)}</{nodeName}>;
var names:Array = ["Alice", "Bob", "Ivan"];
var newPerson:XML =
<person name={names[int(Math.random() * names.length)]}>
    {newAge}
  </person>;
你可以定义一个没有名称的根节点的XMLList对象。

var aliases:XMLList = <>
    <aka>Elmo</aka>
    <aka>The Fonz</aka>
    <aka>Peanut Butter</aka>
</>;
添加值
有很多方法可以给现有的XML添加值.
操作符”+=”

thePeople.person[1].aka += <aka>Rog</aka>;
thePeople.person[1].* += <eyes>Brown</eyes>;
thePeople.person[1].children() += <test>hi</test>; //ERROR!
注:作者也不知道最后那个表达式为什么不能正确的执行。me too !

也可以像操作数组那样添加节点。如:

thePeople.person[0].eyes[0] = "Green"; //adds <eyes>Green</eyes> to Mims' node.
appendchild ( ) , insertchildbefore ( ) 和insertchildafter ( )方法:

thePeople.person.(@name == "Mims Wright").appendChild(<aka>Von Kaiser</aka>);
删除值
删除值的操作用 “delete 节点/属性” 来实现,没有removeChild()函数。。

delete thePeople.person.bio; //delete all <bio> tags
delete thePeople..bio; //Doesn't work but no error. Why?!?
delete thePeople.person.@suffix; //deletes all suffix attributes of <person> tags
delete thePeople.person.(@name == "Roger Braunstein").*; //clears out children of my node
命名空间

注:作者以后会再写一关于命名空间的详细教程。
在E4X中充许构造一个默认的命名空间,这个命名空间会被应用到你创建的XML节点中。

var xhtml:Namespace = new Namespace("http://www.w3.org/1999/xhtml");
default xml namespace = xhtml;
var xml:XML = <html/>; //notice we didn't set a namespace manually
trace(xml.toXMLString()); //<html xmlns="http://www.w3.org/1999/xhtml"/>

19.1  E4X的类型 作为对ECMAScript的扩展,E4X定义了如下几个新的全局类型。   XML:XML结构中的任何一个独立的部分。   XMLList:XML对象的集合。   Namespace:命名空间前缀与命名空间URI之间的映射。   QName:由内部名称和命名空间URI组成的一个限定名。 E4X定义的这个4个类型可以表现XML文档中的所有部分,其内部机制是将每一种类型(特别是XML 和XMLList)都映射为多个DOM类型。

19.1.1  XML类型 XML 类型是E4X中定义的一个重要的新类型,可以用它来表现XML结构中任何独立的部分。XML的实例可以表现元素、特性、注释、处理指令或文本节点。XML类型继承自Object类型,因此它也继承了所有对象默认的所有属性和方法。创建XML对象的方式不止一种,第一种方式是像下面这样调用其构造函数: var x = new XML(); 这行代码会创建一个空的XML对象,我们能够向其中添加数据。另外,也可以向构造函数中传入一个XML字符串,如下面的例子所示:

var x = new XML("<employee position=\"Software Engineer\"><name>Nicholas " +                 "Zakas</name></employee>");  传入到构造函数中的XML字符串会被解析为分层的XML对象。除此之外,还可以向构造函数中传入DOM文档或节点,以便它们的数据可以通过E4X来表现,语法如下: var x = new XML(xmldom); 虽然这些创建XML对象的方式都还不错,但最强大也最吸引人的方法,则是使用XML字面量将XML数据直接指定给一个变量。XML字面量就是嵌入到JavaScript代码中的XML代码。下面来看一个例子。 var employee = <employee position="Software Engineer">                     <name>Nicholas C. Zakas</name>                </employee>; XMLTypeExample01.htm 在这个例子中,我们将一个XML数据结构直接指定给了一个变量。这种简洁的语法同样可以创建一个XML对象,并将它赋值给employee变量。 Firefox 对 E4X 的实现不支持解析 XML 的开头代码(prolog)。无论<?xml version="1.0"  ?>出现在传递给XML构造函数的文本中,还是出现在XML字面量中,都会导致语法错误。 XML 类型的toXMLString()方法会返回 XML 对象及其子节点的XML字符串表示。另一方面,该类型的toString()方法则会基于不同XML 对象的内容返回不同的字符串。如果内容简单(纯文本),则返回文本;否则,toString()方法与toXMLString()方法返回的字符串一样。来看下面的例子。 var data = <name>Nicholas C. Zakas</name>; alert(data.toString());      //"Nicholas C. Zakas" alert(data.toXMLString());    //"<name>Nicholas C. Zakas</name>" 使用这两个方法,几乎可以满足所有序列化XML的需求。

19.1.2  XMLList类型 XMLList 类型表现XML对象的有序集合。XMLList的DOM对等类型是NodeList,但与 Node和NodeList 之间的区别相比,XML 和 XMLList 之间的区别是有意设计得比较小的。要显式地创建一个XMLList对象,可以像下面这样使用XMLList构造函数: var list = new XMLList(); 与XML构造函数一样,也可以向其中传入一个待解析的XML字符串。这个字符串可以不止包含一个文档元素,如下面的例子所示: var list = new XMLList("<item/><item/>"); XMLListTypeExample01.htm 结果,保存在这个list变量中的XMLList就包含了两个XML对象,分别是两个<item/>元素。

还可以使用加号(+)操作符来组合两个或多个XML 对象,从而创建XMLList 对象。加号操作符在E4X中已经被重载,可以用于创建XMLList,如下所示: var list = <item/> + <item/> ; 这个例子使用加号操作符组合了两个XML 字面量,结果得到一个XMLList。同样的组合操作也可以使用特殊的<>和</>语法来完成,此时不使用加号操作符,例如: var list = <><item/><item/></>; 尽管可以创建独立的XMLList对象,但是这类对象通常是在解析较大的XML结构的过程中捎带着被创建出来的。来看下面的例子: var employees = <employees>     <employee position="Software Engineer">         <name>Nicholas C. Zakas</name>     </employee>     <employee position="Salesperson">         <name>Jim Smith</name>     </employee> </employees>; XMLListTypeExample02.htm 以上代码定义的employees 变量中包含着一个XML对象,表示<employees/>元素。由于这个元素又包含两个<employee/>元素,因而就会创建相应的 XMLList 对象,并将其保存在 employees. employee中。然后,可以使用方括号语法及位置来访问每个元素: var firstEmployee = employees.employee[0]; var secondEmployee = employees.employee[1]; 每个XMLList对象都有length()方法,用于返回对象中包含的元素数量。例如: alert(employees.employee.length()); //2 注意,length()是方法,不是属性。这一点是故意与数组和NodeList相区别的。 E4X有意模糊XML和XMLList类型之间的区别,这一点很值得关注。实际上,一个XML对象与一个只包含一个XML对象的XMLList之间,并没有显而易见的区别。为了减少两者之间的区别,每个XML对象也同样有一个length()方法和一个由[0]引用的属性(返回XML对象自身)。 XML 与XMLList 之间的这种兼容性可以简化E4X的使用,因为有些方法可以返回任意一个类型。 XMLList 对象的 toString()和 toXMLString()方法返回相同的字符串值,也就是将其包含的XML对象序列化之后再拼接起来的结果。

19.1.3  Namespace类型 E4X中使用Namespace 对象来表现命名空间。通常,Namespace对象是用来映射命名空间前缀和命名空间URI的,不过有时候并不需要前缀。要创建Namespace对象,可以像下面这样使用Namespace构造函数: var ns = new Namespace(); 而传入URI或前缀加URI,就可以初始化Namespace对象,如下所示:

var ns = new Namespace("http://www.wrox.com/");              var wrox = new Namespace("wrox", "http://www.wrox.com/");   可以使用prefix和uri属性来取得Namespace对象中的信息: alert(ns.uri);          alert(ns.prefix);      alert(wrox.uri);       alert(wrox.prefix);      //"http://www.wrox.com/"  //undefined //"http://www.wrox.com/" //"wrox" //没有前缀的命名空间 //wrox命名空间 NamespaceTypeExample01.htm NamespaceTypeExample01.htm 在没有给Namespace 对象指定前缀的情况下,prefix属性会返回undefined。要想创建默认的命名空间,应该将前缀设置为空字符串。 如果XML字面量中包含命名空间,或者通过XML构造函数解析的XML字符串中包含命名空间信息,那么就会自动创建Namespace对象。然后,就可以通过前缀和namespace()方法来取得对Namespace对象的引用。来看下面的例子: var xml = <wrox:root xmlns:wrox="http://www.wrox.com/">             <wrox:message>Hello World!</wrox:message>           </wrox:root>; var wrox = xml.namespace("wrox"); alert(wrox.uri); alert(wrox.prefix); NamespaceTypeExample02.htm 在这个例子中,我们以XML 字面量的形式创建了一个包含命名空间的XML。而表现wrox 命名空间的Namespace 对象可以通过namespace("wrox")  取得,然后就可以访问这个对象的uri 和prefix 属性了。如果XML 中有默认的命名空间,那么向namespace()中传入空字符串,即可取得相应的Namespace对象。 Namespace 对象的toString()方法始终会返回命名空间URI。

19.1.4  QName类型 QName 类型表现的是XML 对象的限定名,即命名空间与内部名称的组合。向QName构造函数中传入名称或Namespace对象和名称,可以手工创建新的QName对象,如下所示: var wrox = new Namespace("wrox", "http://www.wrox.com/"); var wroxMessage = new QName(wrox, "message");     //表示"wrox:message" QNameTypeExample01.htm 创建了QName 对象之后,可以访问它的两个属性:uri和localName。其中,uri属性返回在创建对象时指定的命名空间的URI(如果未指定命名空间,则返回空字符串),而localName属性返回限定名中的内部名称,如下面的例子所示:

alert(wroxMessage.uri);             //"http://www.wrox.com/" alert(wroxMessage.localName);       //"message" QNameTypeExample01.htm 这两个属性是只读的,如果你想修改它们的值,会导致错误发生。QName对象重写了toString()方法,会以uri::localName 形式返回一个字符串,对于前面的例子来说,就是"http://www.wrox. com/::message"。 在解析XML结构时,会为表示相应元素或特性的XML对象自动创建QName对象。可以使用这个XML对象的name()方法取得与该XML对象关联的QName对象,如下面的例子所示: var xml = < wrox:root xmlns:wrox="http://www.wrox.com/">             <wrox:message>Hello World!</wrox:message>           </wrox:root> ; var wroxRoot = xml.name(); alert(wroxRoot.uri);             //"http://www.wrox.com/" alert(wroxRoot.localName);       //"root" QNameTypeExample02.htm 这样,即便没有指定命名空间信息,也会根据XML结构中的元素和特性创建一个QName对象。 使用setName()方法并传入一个新QName对象,可以修改XML对象的限定名,如下所示: xml.setName(new QName("newroot")); 通常,这个方法会在修改相应命名空间下的元素标签名或特性名时用到。如果该名称不属于任何命名空间,则可以像下面这样使用setLocalName()方法来修改内部名称: xml.setLocalName("newtagname");

19.2  一般用法 在将XML对象、元素、特性和文本集合到一个层次化对象之后,就可以使用点号加特性或标签名的方式来访问其中不同的层次和结构。每个子元素都是父元素的一个属性,而属性名与元素的内部名称相同。如果子元素只包含文本,则相应的属性只返回文本,如下面的例子所示。 var employee = <employee position="Software Engineer">                      <name>Nicholas C. Zakas</name>                </employee>; alert(employee.name); //"Nicholas C. Zakas" 以上代码中的<name/>元素只包含文本。访问employee.name即可取得该文本,而在内部需要定位到<name/>元素,然后返回相应文本。由于传入到alert()时,会隐式调用toString()方法,因此显示的是<name/>中包含的文本。这就使得访问XML文档中包含的文本数据非常方便。如果有多个元素具有相同的标签名,则会返回XMLList。下面再看一个例子。 var employees = <employees>     <employee position="Software Engineer">         <name>Nicholas C. Zakas</name>     </employee>     <employee position="Salesperson">

<name>Jim Smith</name>     </employee> </employees>; alert(employees.employee[0].name);    //"Nicholas C. Zakas" alert(employees.employee[1].name);    //"Jim Smith" 这个例子访问了每个<employee/>元素并返回了它们<name/>元素的值。如果你不确定子元素的内部名称,或者你想访问所有子元素,不管其名称是什么,也可以像下面这样使用星号(*)。 var allChildren = employees.*;     //返回所有子元素,不管其名称是什么 alert(employees.*[0].name);        //"Nicholas C. Zakas" UsageExample01.htm 与其他属性一样,星号也可能返回XML对象,或返回XMLList对象,这要取决于XML结构。 要达到同样的目的,除了属性之外,还可以使用child()方法。将属性名或索引值传递给child()方法,也会得到相同的值。来看下面的例子。 var firstChild = employees.child(0);                 var employeeList = employees.child("employee");      var allChildren = employees.child("*");             //与employees.*[0]相同 //与employees.employee 相同 //与employees.*相同 为了再方便一些,还有一个children()方法始终返回所有子元素。例如: var allChildren = employees.children();              //与employees.*相同 而另一个方法elements()的行为与child()类似,区别仅在于它只返回表示元素的XML对象。例如: var employeeList = employees.elements("employee");    //与 employees.employee 相同 var allChildren = employees.elements("*");          //与employees.*相同 这些方法为JavaScript开发人员提供了访问XML数据的较为熟悉的语法。 要删除子元素,可以使用delete操作符,如下所示: delete employees.employee[0]; alert(employees.employee.length());     //1 显然,这也正是将子节点看成属性的一个主要的优点。

19.2.1  访问特性 访问特性也可以使用点语法,不过其语法稍有扩充。为了区分特性名与子元素的标签名,必须在名称前面加上一个@字符。这是从XPath中借鉴的语法;XPath也是使用@来区分特性和标签的名称。不过,结果可能就是这种语法看起来比较奇怪,例如: var employees = <employees>     <employee position="Software Engineer">         <name>Nicholas C. Zakas</name>     </employee>     <employee position="Salesperson">         <name>Jim Smith</name>     </employee> </employees>; alert(employees.employee[0].@position); //"Software Engineer"

与元素一样,每个特性都由一个属性来表示,而且可以通过这种简写语法来访问。以这种语法访问特性会得到一个表示特性的XML对象,对象的toString()方法始终会返回特性的值。要取得特性的名称,可以使用对象的name()方法。 另外,也可以使用child()方法来访问特性,只要传入带有@前缀的特性的名称即可。 alert(employees.employee[0].child("@position"));     //"Software Engineer" AttributesExample01.htm 由于访问XML对象的属性时也可以使用child(),因此必须使用@字符来区分标签名和特性名。 使用attribute()方法并传入特性名,可以只访问 XML对象的特性。与 child()方法不同,使用attribute()方法时,不需要传入带@字符的特性名。下面是一个例子。 alert(employees.employee[0].attribute("position")); //"Software Engineer" AttributesExample01.htm 这三种访问特性的方式同时适用于XML和XMLList类型。对于XML对象来说,会返回一个表示相应特性的XML 对象;对XMLList 对象来说,会返回一个XMLList 对象,其中包含列表中所有元素的特性XML对象。对于前面的例子而言,employees.employee.@position返回的XMLList将包含两个对象:一个对象表示第一个<employee/>元素中的 position 特性,另一个对象表示第二个元素中的同一特性。 要取得XML或XMLList对象中的所有特性,可以使用attributes()方法。这个方法会返回一个表示所有特性的XMLList对象。使用这个方法与使用@*的结果相同,如下面的例子所示。 //下面两种方式都会取得所有特性 var atts1 = employees.employee[0].@*; var atts2 = employees.employee[0].attributes(); 在E4X中修改特性的值与修改属性的值一样非常简单,只要像下面这样为特性指定一个新值即可。 employees.employee[0].@position = "Author";        //修改position特性 修改的特性会在内部反映出来,换句话说,此后再序列化XML对象,就会使用新的特性值。同样,为特性赋值的语法也可以用来添加新特性,如下面的例子所示。 employees.employee[0].@experience = "8 years";     //添加 experience 特性 employees.employee[0].@manager = "Jim Smith";      //添加manager特性 由于特性与其他ECMAScript属性类似,因此也可以使用delete操作符来删除特性,如下所示。 delete employees.employee[0].@position;            //删除 position特性 通过属性来访问特性极大地简化了与底层XML结构交互的操作。

19.2.2  其他节点类型 E4X定义了表现XML文档中所有部分的类型,包括注释和处理指令。在默认情况上,E4X不会解析注释或处理指令,因此这些部分不会出现在最终的对象层次中。如果想让解析器解析这些部分,可以像下面这样设置XML构造函数的下列两个属性。

XML.ignoreComments = false; XML.ignoreProcessingInstructions = false; 在设置了这两个属性之后,E4X就会将注释和处理指令解析到XML结构中。 由于XML类型可以表示所有节点,因此必须有一种方式来确定节点类型。使用nodeKind()方法可以得到XML 对象表示的类型,该访问可能会返回"text"、"element"、"comment"、"processing- instruction"或"attribute"。以下面的XML对象为例。 var employees = <employees>     <?Dont forget the donuts?>     <employee position="Software Engineer">         <name>Nicholas C. Zakas</name>     </employee>     <!--just added-->     <employee position="Salesperson">         <name>Jim Smith</name>     </employee> </employees> ; 我们可以通过下面的表格来说明nodeKind()返回的节点类型。 语   句 返 回  值 employees.nodeKind() "element" employees.*[0].nodeKind() "processing-instruction" employees.employee[0][email protected]() "attribute" employees.employee[0].nodeKind() "element" employees.*[2].nodeKind() "comment" employees.employee[0].name.*[0].nodeKind() "text" 不能在包含多个XML对象的XMLList上调用nodeKind()方法;否则,会抛出一个错误。 可以只取得特定类型的节点,而这就要用到下列方法。   attributes():返回XML 对象的所有特性。   comments():返回XML 对象的所有子注释节点。   elements(tagName):返回 XML 对象的所有子元素。可以通过提供元素的tagName(标签名)来过滤想要返回的结果。   processingInstructions(name):返回 XML 对象的所有处理指令。可以通过提供处理指令的name(名称)来过滤想要返回的结果。   text():返回XML对象的所有文本子节点。 上述的每一个方法都返回一个包含适当XML对象的XMLList。 使用hasSimpleContent()和hasComplexContent()方法,可以确定XML 对象中是只包含文本,还是包含更复杂的内容。如果XML对象中只包含子文本节点,则前一个方法会返回true;如果XML对象的子节点中有任何非文本节点,则后一个方法返回true。来看下面的例子。 alert(employees.employee[0].hasComplexContent());       alert(employees.employee[0].hasSimpleContent());        alert(employees.employee[0].name.hasComplexContent());      alert(employees.employee[0].name.hasSimpleContent());      //true //false //false //true 利用这些方法,以及前面提到的其他方法,可以极大地方便查找XML结构中的数据。

19.2.3  查询 实际上,E4X提供的查询语法在很多方面都与XPath类似。取得元素或特性值的简单操作是最基本 的查询。在查询之前,不会创建表现XML文档结构中不同部分的XML对象。从底层来看,XML和XMLList的所有属性事实上都是查询的结果。也就是说,引用不表现 XML 结构中某一部分的属性仍然会返回XMLList;只不过这个XMLList 中什么也不会包含。例如,如果基于前面的XML示例执行下列代码,则返回的结果就是空的。 var cats = employees.cat; alert(cats.length());     //0 QueryingExample01.htm 这个查询想要查找<employees/>中的<cat/>元素,但这个元素并不存在。上面的第一行代码会返回一个空的XMLList对象。虽然返回的是空对象,但查询可以照常进行,而不会发生异常。 前面我们看到的大多数例子都使用点语法来访问直接的子节点。而像下面这样使用两个点,则可以进一步扩展查询的深度,查询到所有后代节点。 var allDescendants = employees..*;         //取得<employees/>的所有后代节点 上面的代码会返回<employees/>元素的所有后代节点。结果中将会包含元素、文本、注释和处理指令,最后两种节点的有无取决于在XML构造函数上的设置(前面曾经讨论过);但结果中不会包含特性。要想取得特定标签的元素,需要将星号替换成实际的标签名。 var allNames = employees..name;     //取得作为<employees/>后代的所有<name/>节点 同样的查询可以使用descendants()方法来完成。在不给这个方法传递参数的情况下,它会返回所有后代节点(与使用..*相同),而传递一个名称作为参数则可以限制结果。下面就是这两种情况的例子。 var allDescendants = employees.descendants();     //所有后代节点 var allNames = employees.descendants("name");     //后代中的所有<name/>元素 还可以取得所有后代元素中的所有特性,方法是使用下列任何一行代码。 var allAttributes = employees..@*;     //取得所有后代元素中的所有特性 var allAttributes2 = employees.descendants("@*");     //同上 与限制结果中的后代元素一样,也可以通过用完整的特性名来替换星号达到过滤特性的目的。例如: var allAttributes = employees..@position;         //取得所有position 特性 var allAttributes2 = employees.descendants("@position");     //同上 除了访问后代元素之外,还可以指定查询的条件。例如,要想返回 position 特性值为"Salesperson"的所有<employee/>元素,可以使用下面的查询: var salespeople = employees.employee.(@position == "Salesperson"); 同样的语法也可以用于修改 XML 结构中的某一部分。例如,可以将第一位销售员(salesperson)的position特性修改为"Senior Salesperson",代码如下: employees.employee.(@position == "Salesperson")[0].@position= "Senior Salesperson"; 注意,圆括号中的表达式会返回一个包含结果的XMLList,而方括号返回其中的第一项,然后我们重写了@position 属性的值。 使用parent()方法能够在XML结构中上溯,这个方法会返回一个XML对象,表示当前XML对象的父元素。如果在XMLList 上调用 parent()方法,则会返回列表中所有对象的公共父元素。下面是一个例子。 var employees2 = employees.employee.parent(); 这里,变量employees2中包含着与变量employees相同的值。在处理来源未知的XML对象时,经常会用到parent()方法。


19.2.4  构建和操作XML 将XML数据转换成XML 对象的方式有很多种。前面曾经讨论过,可以将XML字符串传递到XML构造函数中,也可以使用XML字面量。相对而言,XML字面量方式更方便一些,因为可以在字面量中嵌入JavaScript 变量,语法是使用花括号({})。可以将JavaScript变量嵌入到字面量中的任意位置上,如下面的例子所示。 var tagName = "color"; var color = "red"; var xml = <{tagName}>{color}</{tagName}>; alert(xml.toXMLString());     //"<color>red</color> XMLConstructionExample01.htm 在这个例子中,XML字面量的标签名和文本值都是使用花括号语法插入的。有了这个语法,就可以省去在构建XML结构时拼接字符串的麻烦。 E4X 也支持使用标准的 JavaScript语法来构建完整的 XML结构。如前所述,大多数必要的操作都是查询,而且即便元素或特性不存在也不会抛出错误。在此基础上更进一步,如果将一个值指定给一个不存在的元素或特性,E4X就会首先在底层创建相应的结构,然后完成赋值。来看下面的例子。 var employees = <employees/>; employees.employee.name = "Nicholas C. Zakas"; employees.employee.@position = "Software Engineer"; XMLConstructionExample02.htm 这个例子一开始声明了<employees/>元素,然后在这个元素基础上开始构建 XML结构。第二行代码在<employees/>中创建了一个<employee/>元素和一个<name/>元素,并指定了文本值。第三行代码添加了一个position特性并为该特性指定了值。此时构建的XML结构如下所示。 <employees>     <employee position="Software Engineer">         <name>Nicholas C. Zakas</name>     </employee> </employees> 当然,使用加号操作符也可以再添加一个<employee/>元素,如下所示。 employees.employee += <employee position="Salesperson">                           <name>Jim Smith</name>                       </employee>;
最终构建的XML结构如下所示: <employees>     <employee position="Software Engineer">         <name>Nicholas C. Zakas</name>     </employee>     <employee position="Salesperson">         <name>Jim Smith</name>     </employee> </employees> 除了上面介绍的基本的XML构建语法之外,还有一些类似DOM的方法,简介如下。   appendChild(child):将给定的child 作为子节点添加到XMLList的末尾。 
copy():返回XML对象副本。   insertChildAfter(refNode, child):将 child 作为子节点插入到XMLList 中refNode 的后面。   insertChildBefore(refNode, child):将 child 作为子节点插入到XMLList 中refNode 的前面。   prependChild(child):将给定的child 作为子节点添加到XMLList的开始位置。   replace(propertyName,  value):用 value 值替换名为propertyName 的属性,这个属性可能是一个元素,也可能是一个特性。   setChildren(children):用 children 替换当前所有的子元素,children 可以是XML对象,也可是XMLList对象。 这些方法既非常有用,也非常容易使用。下列代码展示了这些方法的用途。
var employees = <employees>     <employee position="Software Engineer">         <name>Nicholas C. Zakas</name>     </employee>     <employee position="Salesperson">         <name>Jim Smith</name>     </employee> </employees>; employees.appendChild(<employee position="Vice President">                       <name>Benjamin Anderson</name>                   </employee>); employees.prependChild(<employee position="User Interface Designer">                        <name>Michael Johnson</name>                    </employee>); employees.insertChildBefore(employees.child(2),                             <employee position="Human Resources Manager">                                <name>Margaret Jones</name>                            </employee>); employees.setChildren(<employee position="President">                            <name>Richard McMichael</name>                       </employee> +                       <employee position="Vice President">                           <name>Rebecca Smith</name>                       </employee>);

以上代码首先在员工列表的底部添加了一个名为Benjamin Anderson的副总统(vice president)。然后,在员工列表顶部又添加了一个名为Michael Johnson的界面设计师。接着,在列表中位置为 2的员 工——此时这个员工是Jim Smith,因为他前面还有Michael Johnson和Nicholas C. Zakas——之前又添加了一个名为Margaret Jones的人力资源部经理。最后,所有这些子元素都被总统Richard McMichael和副总统Rebecca Smith替代。结果XML如下所示。 <employees>     <employee position="President">     <name>Richard McMichael</name>     </employee>     <employee position="Vice President">         <name>Rebecca Smith</name>     </employee> </employees> 熟练运用这些技术和方法,就能够使用E4X执行任何DOM风格的操作。


19.2.5  解析和序列化 E4X将解析和序列化数据的控制放在了XML构造函数的一些设置当中。与XML解析相关的设置有如下三个。   ignoreComments:表示解析器应该忽略标记中的注释。默认设置为true。   ignoreProcessingInstructions:表示解析器应该忽略标记中的处理指令。默认设置为true。   ignoreWhitespace:表示解析器应该忽略元素间的空格,而不是创建表现这些空格的文本节点。默认设置为true。 这三个设置会影响对传入到XML构造函数中的字符串以及XML字面量的解析。 另外,与XML数据序列化相关的设置有如下两个。   prettyIndent:表示在序列化XML时,每次缩进的空格数量。默认值为2。   prettyPrinting:表示应该以方便人类认读的方式输出XML,即每个元素重起一行,而且子元素都要缩进。默认设置为true。 这两个设置将影响到toString()和toXMLString()的输出。 以上五个设置都保存在settings对象中,通过XML构造函数的settings()方法可以取得这个对象,如下所示。 var settings = XML.settings(); alert(settings.ignoreWhitespace);     //true alert(settings.ignoreComments);       //true ParsingAndSerializationExample01.htm 通过向setSettings()方法中传入包含全部 5项设置的对象,可以一次性指定所有设置。在需要临时改变设置的情况下,这种设置方式非常有用,如下所示。 var settings = XML.settings(); XML.prettyIndent = 8; XML.ignoreComments = false;

//执行某些处理 XML.setSettings(settings);     //重置前面的设置 而使用defaultSettings()方法则可以取得一个包含默认设置的对象,因此任何时候都可以使用下面的代码重置设置。 XML.setSettings(XML.defaultSettings());

19.2.6  命名空间 E4X提供了方便使用命名空间的特性。前面曾经讨论过,使用namspace()方法可以取得与特定前缀对应的Namespace 对象。而通过使用setNamespace()并传入Namespace 对象,也可以为给定元素设置命名空间。来看下面的例子。 var messages = <messages>     <message>Hello world!</message> </messages>; messages.setNamespace(new Namespace("wrox", "http://www.wrox.com/")); 调用 setNamespace()方法后,相应的命名空间只会应用到调用这个方法的元素。此时,序列化messages变量会得到如下结果。 <wrox:messages xmlns:wrox="http://www.wrox.com/">     <message>Hello world!</message> </wrox:messages> 可见,由于调用了 setNamespace()方法,<messages/>元素有了 wrox 命名空间前缀,而<message/>元素则没有变化。 如果只想添加一个命名空间声明,而不想改变元素,可以使用 addNamespace()方法并传入Namespace对象,如下面的例子所示。 messages.addNamespace(new Namespace("wrox", "http://www.wrox.com/")); 在将这行代码应用于原先的<messages/>元素时,就会创建如下所示的XML结构。 <messages xmlns:wrox="http://www.wrox.com/">     <message>Hello world!</message> </messages> 调用removeNamespace()方法并传入 Namespace 对象,可以移除表示特定命名空间前缀和URI的命名空间声明;注意,必须传入丝毫不差的表示命名空间的Namespace对象。例如: messages.removeNamespace(new Namespace("wrox", "http://www.wrox.com/")); 这行代码可以移除wrox命名空间。不过,引用前缀的限定名不会受影响。 有两个方法可以返回与节点相关的 Namespace 对象的数组:namespaceDeclarations()和inScopeNamespaces()。前者返回在给定节点上声明的所有命名空间的数组,后者返回位于给定节点作用域中(即包括在节点自身和祖先元素中声明的)所有命名空间的数组。如下面的例子所示: var messages = <messages xmlns:wrox="http://www.wrox.com/">     <message>Hello world!</message> </messages>;

alert(messages.namespaceDeclarations());     //"http://www.wrox.com" alert(messages.inScopeNamespaces());         //",http://www.wrox.com" alert(messages.message.namespaceDeclarations());     //"" alert(messages.message.inScopeNamespaces());         //",http://www.wrox.com" 这里,<messages/>元素在调用namespaceDeclarations()时,会返回包含一个命名空间的数组,而在调用inScopeNamespaces()时,则会返回包含两个命名空间的数组。作用域中的这两个命名空间,分别是默认命名空间(由空字符串表示)和 wrox 命名空间。在<message/>元素上调用这些方法时,namespaceDeclarations(),会返回一个空数组,而 inScopeNamespaces()方法返回的结果与在<messages/>元素上调用时的返回结果相同。 使用双冒号(::)也可以基于Namespace 对象来查询XML结构中具有特定命名空间的元素。例如,要取得包含在wrox命名空间中的所有<message/>元素,可以参考下面的代码。 var messages = <messages xmlns:wrox="http://www.wrox.com/">     <wrox:message>Hello world!</message> </messages>; var wroxNS = new Namespace("wrox", "http://www.wrox.com/"); var wroxMessages = messages.wroxNS::message; 这里的双冒号表示返回的元素应该位于其中的命名空间。注意,这里使用的是JavaScript变量,而不是命名空间前缀。 还可以为某个作用域中的所有XML对象设置默认命名空间。为此,要使用default xml namespace语句,并将一个Namespace对象或一个命名空间URI作为值赋给它。例如: default xml namespace = "http://www.wrox.com/"; function doSomething(){     //只为这个函数设置默认的命名空间     default xml namespace = new Namespace("your", "http://www.yourdomain.com"); }  在 doSomething()函数体内设置默认命名空间并不会改变全局作用域中的默认 XML命名空间。在给定作用域中,当所有XML数据都需要使用特定的命名空间时,就可以使用这个语句,从而避免多次引用命名空间的麻烦。



19.3  其他变化 为了与 ECMAScript 做到无缝集成,E4X 也对语言基础进行了一些修改。其中之一就是引入了for-each-in循环,以便迭代遍历每一个属性并返回属性的值,如下面的例子所示。 var employees = <employees>                     <employee position="Software Engineer">                         <name>Nicholas C. Zakas</name>                     </employee>                     <employee position="Salesperson">                         <name>Jim Smith</name>                     </employee>                 </employees>;

for each (var child in employees){     alert(child.toXMLString()); }  ForEachInExample01.htm 在这个例子的for-each-in循环中,<employees/>的每个子节点会依次被赋值给child变量,其中包括注释、处理指令和/或文本节点。要想返回特性节点,则需要对一个由特性节点组成的XMLList对象进行操作,如下所示。 for each (var attribute in employees.@*){ //遍历特性     alert(attribute); }  虽然for-each-in循环是在E4X中定义的,但这个语句也可以用于常规的数组和对象,例如: var colors = ["red","green","blue"]; for each(var color in colors){     alert(color); }  ForEachInExample01.htm 对于数组,for-each-in 循环会返回数组中的每一项。对于非XML对象,这个循环返回对象每个属性的值。 E4X 还添加了一个全局函数,名叫 isXMLName()。这个函数接受一个字符串,并在这个字符串是元素或特性的有效内部名称的情况下返回true。在使用未知字符串构建XML数据结构时,这个函数可以为开发人员提供方便。来看下面的例子。 alert(isXMLName("color"));           //true alert(isXMLName("hello world"));     //false 如果你不确定某个字符串的来源,而又需要将该字符串用作一个内部名称,那么最好在使用它之前先通过isXMLName()检测一下是否有效,以防发生错误。 E4X对标准ECMAScript的最后一个修改是typeof 操作符。在对XML 对象或XMLList 对象使用这个操作符时,typeof返回字符串"xml"。但在对其他对象使用这个操作符时,返回的都是"object",例如: var xml = new XML(); var list = new XMLList(); var object = {}; alert(typeof xml);   //"xml" alert(typeof list);  //"xml" alert(typeof object); //"object" 多数情况下,都没有必要区分XML和XMLList对象。在E4X中,这两个对象都被看成是基本数据类型,因而也无法通过instanceof操作符来将它们区分开来。

19.4  全面启用E4X 鉴于E4X在很多方面给标准JavaScript带来了不同,因此Firefox在默认情况下只启用E4X中与其 他代码能够相安无事的那些特性。要想完整地启用 E4X,需要将<script>标签的 type 特性设置为"text/javascript;e4x=1",例如: <script type="text/javascript;e4x=1" src="e4x_file.js"></script> 在打开这个“开关”之后,就会全面启用 E4X,从而能够正确地解析嵌入在 E4X字面量中的注释和CData段。在没有完整启用E4X的情况下使用注释和/或CData段会导致语法错误。

19.5  小结 E4X是以ECMA-357标准的形式发布的对ECMAScript的一个扩展。E4X的目的是为操作XML数据提供与标准ECMAScript更相近的语法。E4X具有下列特征。   与DOM不同,E4X只用一个类型来表示XML中的各种节点。   XML 对象中封装了对所有节点都有用的数据和行为。为表现多个节点的集合,这个规范定义了XMLList 类型。   另外两个类型,Namespace和QName,分别表现命名空间和限定名。 为便于查询XML结构,E4X还修改了标准了的ECMAScript语法,修改的地方如下。   使用两个点(..)表示要匹配所有后代元素,使用@字符表示应该返回一或多个特性。   星号字符(*)是一个通配符,可以匹配任意类型的节点。   所有这些查询都可以通过一组执行相同操作的方法来实现。 到2011年底,Firefox还是唯一一个支持E4X的浏览器。尽管没有其他浏览器提供商承诺会实现E4X,但在服务器上,由于BEA Workshop for WebLogic和Yhaoo! YQL的推动,E4X已经取得了不小的成功。






























猜你喜欢

转载自zhyp29.iteye.com/blog/2304608
E4X
e4e