您的位置:首页 > Web前端 > JavaScript

W3C Group的JavaScript1.7新特性介绍

2009-05-08 09:00 567 查看
在介绍完JavaScript1.6的新特性之后,W3C Group继续为大家介绍JavaScript1.7,JavaScript1.7带来了一系列的新特性,除了向下兼容javascript1.6之外,还包括生成器迭代器数组领悟let 表达式,以及解构赋值等重要特性!

浏览器支持:

Firefox 2 Beta 1或以上版本

如何使用JavaScript1.7

如果你想使用Javascript1.7的新特性,你只需要在写Script标签的时候引入javascript1.7的版本号即可,如:

<script type="application/javascript;version=1.7"/>

注意:"yield" 和 "let"是javascript1.7中的两个新特性,请不要当作变量名或函数名使用!

下面我们介绍一下javascript1.7给我们带来的这些新特性!

生成器和迭代器

每当开发那些涉及到迭代算法(如对列表的迭代或对同一数据集进行相同的演算)的代码时,通常都需要声明一些变量,并在整个计算过程中都保留它们的值。一般来说,你还需要使用一个回调函数来获得迭代算法的中间值。

生成器

下面这个迭代算法计算了斐波那契数列

function do_callback(num) {

document.write(num + "<br>/n");

}

function fib() {

var i = 0, j = 1, n = 0;

while (n < 10) {

do_callback(i);

var t = i;

i = j;

j += t;

n++;

}

}

fib();

这段代码使用了一个回调函数,来机械地执行算法每一步迭代的操作。这里,每一个斐波那契数都被简单地打印在控制台上。

使用生成器和迭代器,可以提供一个更新更好的方法。现在就让我们来看看使用了生成器的斐波那契数列

function fib() {

var i = 0, j = 1;

while (true) {

yield i;

var t = i;

i = j;

j += t;

}

}

var g = fib();

for (var i = 0; i < 10; i++) {

document.write(g.next() + "<br>/n");

}

包含 yield 关键字的函数,就是一个生成器。当你调用它时,它的真正参数就会绑定到实际的变量上,但它自身其实并不做实际的计算,而是返回一个生成器-迭代器(generator-iterator)
每调用一次该生成器-迭代器的 next() 方法就再执行一次迭代算法。yield 关键字指定了每一步的值,就像生成器-迭代器版的 return
一样,指出了算法的每次迭代之间的边界。你每调用一次 next(),生成器代码就从 yield 以下的语句处恢复。

你可以重复调用一个生成器-迭代器的 next() 方法来使它转动,直到到达你期望的结果条件。在这个例子中,我们只需不停地调用 g.next(),就可以获得任意多的斐波那契数,直到满足我们所需要的结果个数。

从指定位置恢复生成器-迭代器

一个生成器在被调用其 next() 方法启动之后,你可以使用 send() 方法传递一个指定的的值,作为上一个 yield 的结果。这个生成器将返回后续 yield 的操作数。

你无法强制生成器从某个位置启动,在你 send() 一个指定的值给生成器之前,你必须先以 next() 启动它。

注意: 有意思的是,调用 send(undefined) 与调用 next() 是等价的。不过,用任何除 undefined 之外的值调用 send() 启动一个刚刚创建的生成器将引发 TypeError 异常。

生成器的异常

通过调用生成器的 throw() 方法并传递异常值,你可以强制生成器抛出异常。此异常将从该生成器当前挂起的上下文中被抛出,就好像在当前被挂起的 yield 中插入了一个 throw value 语句。

如果在抛出异常的过程中没有遇到 yield,那么该异常将被通过调用 throw() 传播,且后续的 next() 调用将导致 StopIteration 异常抛出。

关闭生成器

生成器的 close() 方法能强行关闭生成器自身。关闭生成器的效果如下:

1. 运行这个生成器函数中活动的 finally 语句。

2. 一旦 finally 语句抛出任何除 StopIteration 之外的异常,该异常被传播给 close() 方法的调用者。

3. 该生成器终结。

生成器示例

这段代码执行了一个每 100 次循环 yield 一次的生成器。

var gen = generator();

function driveGenerator() {

if (gen.next()) {

window.setTimeout(driveGenerator, 0);

} else {

gen.close();

}

}

function generator() {

while (i < something) {

/** 工作 **/

++i;

/** 100 循环 yield 一次 **/

if ((i % 100) == 0) {

yield true;

}

}

yield false;

}

迭代器

迭代器可以让你迭代数据,是一种特殊的对象。

在常规的用法中,迭代器对象是“不可见”的,你不需要显示地操作它们。取而代之,使用 JavaScript 的 for...in 和 for each...in 语句 自然地在对象的键、值之上循环。

var objectWithIterator = getObjectSomehow();

for (var i in objectWithIterator)

{

document.write(objectWithIterator[i] + "<br>/n");

}

如果你想实现你自己的迭代器对象,或对直接操作迭代器有另外的需求,你就需要了解 next 方法、StopIteration 异常和 __iterator__ 方法了。

你可以通过调用 Iterator(对象名) 来为一个对象创建迭代器。调用一个对象的 __iterator__
方法时,需要查找它的迭代器;当这个方法不存在时,创建一个默认的迭代器。默认迭代器会根据传统的 for...in 和 for each...in
模型产出该对象的属性。也可以提供一个自定义的迭代器,只需重载 __iterator__
方法,使它返回你自定义的迭代器的一个实例即可。你可以使用 Iterator(对象) 从脚本中获取一个对象的迭代器,以避免直接访问
__iterator__ 的属性,因为只有前者才对数组起作用。

一旦你拥有了一个迭代器,你就能够轻松地获取对象中的下一项了,调用该迭代器的 next() 方法即可。对象中没有余下的数据时,抛出 StopIteration 异常。

这里有一个直接迭代器操作的简单示例

var obj = {name:"Jack Bauer", username:"JackB", id:12345, agency:"CTU", region:"Los Angeles"};

var it = Iterator(obj);

try {

while (true) {

print(it.next() + "/n");

}

} catch (err if err instanceof StopIteration) {

print("End of record./n");

} catch (err) {

print("Unknown error: " + err.description + "/n");

}

该程序的输出如下:

name,Jack Bauer

username,JackB

id,12345

agency,CTU

region,Los Angeles

End of record.

你可以有选择性地指定创建迭代器的第二个参数,它是一个指示你调用其 next() 方法时是否需要返回 keys
的布尔值。这个参数被作为第一个参数传入给用户定义的 __iterator__ 函数。在上面的例子中,把 var it =
Iterator(obj); 改为 var it = Iterator(obj, true); 后的输出如下:

name

username

id

agency

region

End of record.

对于每一种情况,返回数据的实际顺序只基于实现,数据的顺序是不能保证的。

迭代器是一种扫描对象中的数据的简便的方式。被扫描的对象的内容中可能包含你没有注意到的数据,所以当你需要保护那些你的应用程序并不期望的数据时,迭代器会显得非常有用。

数组领悟

数组领悟是一种对生成器的运用,展示了一种初始化数组的强大而方便的方式。例如:

function range(begin, end) {

for (let i = begin; i < end; ++i) {

yield i;

}

}

range() 是一个返回从 begin 到 end 之间值的生成器。知道了这些,我们就可以这样使用它:

var ten_squares = [i * i for each (i in range(0, 10))];

这段代码预初始化了一个新数组 ten_squares,包含了范围在 0..9 之间的值的平方数。

在初始化数字时你可以使用任何限制条件。如果你想要初始化一个包含 0 到 20 之间偶数的数组,你可以使用下面的代码:

var evens = [i for each (i in range(0, 21)) if (i % 2 == 0)];

在 JavaScript 1.7 之前,这个工作必须要写出类似这样的代码:

var evens = [];

for (var i=0; i <= 20; i++) {

if (i % 2 == 0)

evens.push(i);

}

一旦你熟悉了数组领悟,你就会发现它不但紧凑,而且易于阅读。

作用域规则

数组领悟被暗含的块包围着,包含了方括号内所有的东西,类似隐含的 let 声明。

更多细节。

let 的块作用域

有好几种使用 let 来管理数据和函数的块作用域的方式:

* let 语句 提供了一种在块作用域中联系值与变量、常量、函数的途径,且不会影响到块之外的同名变量。

* let 表达式 使你能在仅有一个表达式所限定的作用域内建立变量。

* let 定义 在一个块中,定义变量、常量和函数,并把它们的作用域限制在块内。从语法上看,let 定义 很类似 var。

* 你也可以使用 let 建立只存在于 for 循环上下文中的变量。

let 语句

let
语句为变量、常量和函数提供了本地作用域,其工作是把零个或多个变量绑定到单个代码块的词法作用域上,这个过程与块语句完全相同。特别要注意的是,在
let 语句内部使用 var 声明的变量,其作用域和它在 let 语句外被声明时相同,这样的变量仍然具有函数作用域。

例如:

var x = 5;

var y = 0;

let (x = x+10, y = 12) {

print(x+y + "/n");

}

print((x + y) + "/n");

这段程序的输出是:

27

5

此规则对任何 JavaScript 中的代码块适用。

使用 let 声明,可以给代码块建立属于它自己的本地变量。

注意: 使用 let 语句这个语法时,跟在 let 后面的括号是必需的,丢掉它们将导致一个语法错误。



作用域规则


用 let 定义的变量的作用域是 let 块本身,覆盖了任何 let 块内部的块,除非这些内部块定义了同名的变量。

let 表达式

你可以使用 let 建立作用域仅在一个表达式中的变量。

var x = 5;

var y = 0;

document.write( let(x = x + 10, y = 12) x+y + "<br>/n");

document.write(x+y + "<br>/n");

输出如下:

27

5

在这个例子中,对 x 的值 x+10 的绑定和对 y 的值 12 的绑定的作用域被限制于表达式 x+y。

作用域规则

let 表达式

let (声明) 表达式

在 表达式 周围创建了一个隐含的块。

let 定义

let 关键字也可被用于在块中定义变量、常量和函数。

if (x > y)

{

let gamma = 12.7 + y;

i = gamma * x;

}

使用内部函数时,let 语句,let 表达式和 let 定义常常能使代码更加清晰。

var list = document.getElementById("list");

for (var i = 1; i <= 5; i++)

{

var item = document.createElement("LI");

item.appendChild(document.createTextNode("Item " + i));

let j = i;

item.onclick = function (ev) {

alert("Item " + j + " is clicked.");

};

list.appendChild(item);

}

上面的例子能像我们所期望的那样工作,因为五个匿名函数的实例引用了变量 j 的不同实例。请注意,如果你把 let 换成 var,或者移除变量 j 并仅在内部函数中使用变量 i,这段代码就不能正常工作了。

作用域规则

用 let 定义的变量的作用域覆盖了它们被定义的块,已经任何没有重定义它们的子块。从这一点来看,let 的效果非常类似 var。主要的区别在于用 var 定义的变量的作用域是整个闭合函数:

function varTest() {

var x = 31;

if (true) {

var x = 71; // 同一个变量!

alert(x); // 71

}

alert(x); // 71

}

function letTest() {

let x = 31;

if (true) {

let x = 71; // 不是同一个变量

alert(x); // 71

}

alert(x); // 31

}

= 右侧的表达式处于块的内部。这与 let-表达式和 let-语句的作用域不同:

function letTests() {

let x = 10;

// let-语句

let (x = x + 20) {

alert(x); // 30

}

// let-表达式

alert(let (x = x + 20) x); // 30

// let-定义

{

let x = x + 20; // 这里的 x 的求值结果为 undefined

alert(x); // undefined + 20 ==> NaN

}

}

在程序和类中,let 不创建 global 对象上的属性,这与 var 的做法不同。但 let 会在一个隐含的块中创建属性,使其它上下文中的语句可以被求值。这就意味着,let 不会覆写前面用 var 定义的变量。例如:

var x = 'global';

let x = 42;

document.write(this.x + "<br>/n");

这段代码将输出 "global" 而不是 "42"。

隐含的块指的不是那种被括弧括起的块,它只由 JavaScript 引擎隐式创建。

由 eval() 执行的 let 与 var 不同,它不会在变量对象(活动对象或在最内层绑定的组成部分)上创建属性。为使其它上下文中的语句可以被求值,它会在一个隐含的块中创建属性,这是 eval() 对于程序的操作与前述规则共同作用的结果。

换句话说,当你使用 eval() 执行代码时,这段代码就被当作一段独立程序处理,拥有自己的包围其代码的隐含的块。

for 循环中的 let-界定变量

你可以使用 let 关键字在 for 循环的作用域内绑定本地变量,和使用 var 类似。

** 添加对象 **

var i=0;

for ( let i=i ; i < 10 ; i++ )

document.write(i + "<br>/n");

for ( let [name,value] in obj )

document.write("Name: " + name + ", Value: " + value + "<br>/n");

作用域规则

for (let 表达式1; 表达式2; 表达式3) 语句

在这个例子中,表达式1,表达式2,表达式3和语句都被闭合在一个隐含的块中,其中包含了用 let expr1 声明的本地变量。上面例子中的第一个循环示范了这个用法。

for (let 表达式1 in 表达式2) 语句

for each(let 表达式1 in 表达式2) 语句

在这两个例子中,都存在包含了每个 statement 的隐含的块。上面例子中的第二个循环展示了这里的第一种用法。

解构赋值

解构赋值使用了一种反映数组常量和对象常量结构的语法,使得从数组或对象中抽取数据成为可能。

通过使用对象常量和数组常量表达式,你可以轻松地按需创建成包的数据,然后以任意你想要的方式使用它们。你甚至可以从函数中返回它们。

而你使用解构赋值最重要的一件事就是,你可以从一个语句中读取整个结构。在这一节的余下的部分中会给出很多例子,你将看到你可以使用的很多有趣的功能。

解构赋值这种能力与 Perl,Python 等语言所显示出的特性很类似。

示例

用例子就可以很好地诠释解构赋值,所以这里几乎没有什么需要你通过阅读来学习的内容。

避免临时变量

可以用解构赋值来交换两个变量的值。例如:

var a = 1;

var b = 3;

[a, b] = [b, a];

这段代码执行之后,b 为 1,a 为 3。不使用解构赋值的话,这个工作就需要一个临时变量。(当然了,在某些低级语言中,可以使用 XOR-交换技巧)

与此类似,解构赋值也可以被用于三个或更多变量之间的轮换:

var a = 'o';

var b = "<span style='color:green;'>o</span>";

var c = 'o';

var d = 'o';

var e = 'o';

var f = "<span style='color:blue;'>o</span>";

var g = 'o';

var h = 'o';

for (lp=0;lp<40;lp++)

{[a, b, c, d, e, f, g, h] = [b, c, d, e, f, g, h, a];

document.write(a+''+b+''+c+''+d+''+e+''+f+''+g+''+h+''+"<br />");}

这段代码执行之后,将显示一个可视化的、彩色的变量轮换过程。

回到我们上文中的菲波那契数生成器的例子,我们可以使用一个成组赋值语句来计算 "i" 和 "j" 的新值,从而消除临时变量 "t"。

function fib() {

var i = 0, j = 1;

while (true) {

yield i;

[i, j] = [j, i + j];

}

}

var g = fib();

for (let i = 0; i < 10; i++)

print(g.next());



多值返回


有了解构赋值之后,函数就可以返回多个值了。虽然在以前也可以从函数中返回数组,但解构赋值为此增加了极大的便利。

function f() {

return [1, 2];

}

正如你所看到的,用方括号括住所有要返回的值,使它们看上去像是数组,这就是多值返回。在上面的例子中,f() 返回 [1, 2] 作为其输出。

var a, b;

[a, b] = f();

document.write ("A is " + a + " B is " + b + "<br>/n");

命令 [a, b] = f() 使用函数结果给方括号中的变量依次赋值。a 被设为 1,b 被设为 2。

你也可以把这些返回值恢复成一个数组:

var a = f();

document.write ("A is " + a);

在这个例子中,a 是一个包含值 1 和 2 的数组。

在对象中循环

你也可以使用解构赋值把数据从对象中倾倒出来:

let obj = { width: 3, length: 1.5, color: "orange" };

for (let [name, value] in Iterator(obj)) {

document.write ("Name: " + name + ", Value: " + value + "<br>/n");

}

这段代码循环了对象 obj 中的所有键值对,并显示出它们的名字和值。这个例子的输出如下:

Name: width, Value: 3

Name: length, Value: 1.5

Name: color, Value: orange

在 JavaScript 1.7 中,obj 周围的 Iterator() 不是必须的,不过 JavaScript 1.8 就需要了,这样可以允许对数组的解构赋值(见 bug 366941)。

在对象组成的数组中循环

你可以循环一个由对象组成的数组,并倾倒出每个对象中你感兴趣的域。

var people = [

{

name: "Mike Smith",

family: {

mother: "Jane Smith",

father: "Harry Smith",

sister: "Samantha Smith"

},

age: 35

},

{

name: "Tom Jones",

family: {

mother: "Norah Jones",

father: "Richard Jones",

brother: "Howard Jones"

},

age: 25

}

];

for each (let {name: n, family: { father: f } } in people) {

document.write ("Name: " + n + ", Father: " + f + "<br>/n");

}

这段代码用 name 域给 n 赋值,用 family.father 域给 f 赋值,然后打印它们。这个过程被作用于 people 数组中的每一个对象。输出如下:

Name: Mike Smith, Father: Harry Smith

Name: Tom Jones, Father: Richard Jones

忽略某些返回值

你也可以忽略掉那些你不感兴趣的返回值:

function f() {

return [1, 2, 3];

}

var [a, , b] = f();

document.write ("A is " + a + " B is " + b + "<br>/n");

这段代码运行之后,a 为 1,b 为 3,2 这个值被忽略了。你也可以用这种方式忽略多个,甚至是全部返回值。例如:

[,,,] = f();

从正则表达式匹配结果中倾倒值

当正则表达式的 exec() 方法找到一个匹配时,它会返回一个数组,第一项是目标字符串中被匹配的完整部分,后面是被正则表达式中被括号括起的组所匹配的数段字符串。解构赋值使你能轻松地倾倒出该数组中的各个部分,并忽略掉其中不需要的匹配。

// 一个简单的正则表达式,用来匹配 http / https / ftp 型的 URL

var parsedURL = /^(/w+)/:////([^//]+)//(.*)$/.exec(url);

if (!parsedURL)

return null;

var [, protocol, fullhost, fullpath] = parsedURL;

貌似JavaScript1.7的新特性来的更多更猛,接下来,w3c group将为大家介绍javascript1.8的一些新特性!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: