New in JavaScript 1.7
来源:互联网 发布:淘宝千牛一键发货 编辑:程序博客网 时间:2024/06/05 16:36
这篇文章涵盖了 Firefox 2 中的新特性 JavaScript 1.7 介绍了一系列新的语言特性,特别是生成器、迭代器、数组领悟、 Firefox 2 Beta 1 开始提供对 JavaScript 1.7 的支持,当然也包括所有当前代码库(trunk目录)中的版本。 本文中包含的代码示例可以在 JavaScript shell 中实验。请参照 Introduction to the JavaScript shell 来学习如何安装并使用 shell。 为了使用 JavaScript 1.7 的新特性,你需要指定 JavaScript 版本为 1.7。在HTML或者XUL代码中,这样写: 当使用JavaScript shell时,你需要通过在命令行添加 如果用到了需要使用新关键字 "yield" 和 "let" 的特性,你就必须要指定版本为 1.7,因为已经写好的代码中可能会把那两个关键字当作变量名或者函数名来使用。如果没有涉及使用了新关键字的特性(解构赋值和数组领悟),就可以不指定 JavaScript 版本。 每当开发那些涉及到迭代算法(如对列表的迭代或对同一数据集进行相同的演算)的代码时,通常都需要声明一些变量,并在整个计算过程中都保留它们的值。一般来说,你还需要使用一个回调函数来获得迭代算法的中间值。 下面这个迭代算法计算了斐波那契数列: 这段代码使用了一个回调函数,来机械地执行算法每一步迭代的操作。这里,每一个斐波那契数都被简单地打印在控制台上。 使用生成器和迭代器,可以提供一个更新更好的方法。现在就让我们来看看使用了生成器的斐波那契数列: 包含 你可以重复调用一个生成器-迭代器的 一个生成器在被调用其 你无法强制生成器从某个位置启动,在你 通过调用生成器的 如果在抛出异常的过程中没有遇到 yield,那么该异常将被通过调用 生成器的 这段代码执行了一个每 100 次循环 yield 一次的生成器。 迭代器可以让你迭代数据,是一种特殊的对象。 在常规的用法中,迭代器对象是“不可见”的,你不需要显示地操作它们。取而代之,使用 JavaScript 的 如果你想实现你自己的迭代器对象,或对直接操作迭代器有另外的需求,你就需要了解 你可以通过调用 一旦你拥有了一个迭代器,你就能够轻松地获取对象中的下一项了,调用该迭代器的 这里有一个直接迭代器操作的简单示例: 该程序的输出如下: 你可以有选择性地指定创建迭代器的第二个参数,它是一个指示你调用其 对于每一种情况,返回数据的实际顺序只基于实现,数据的顺序是不能保证的。 迭代器是一种扫描对象中的数据的简便的方式。被扫描的对象的内容中可能包含你没有注意到的数据,所以当你需要保护那些你的应用程序并不期望的数据时,迭代器会显得非常有用。 数组领悟是一种对生成器的运用,展示了一种初始化数组的强大而方便的方式。例如: 这段代码预初始化了一个新数组 ten_squares,包含了范围在 在初始化数字时你可以使用任何限制条件。如果你想要初始化一个包含 0 到 20 之间偶数的数组,你可以使用下面的代码: 在 JavaScript 1.7 之前,这个工作必须要写出类似这样的代码: 一旦你熟悉了数组领悟,你就会发现它不但紧凑,而且易于阅读。 数组领悟被暗含的块包围着,包含了方括号内所有的东西,类似隐含的 更多细节。 有好几种使用 例如: 这段程序的输出是: 此规则对任何 JavaScript 中的代码块适用。使用 用 你可以使用 输出如下: 在这个例子中,对 x 的值 在 表达式 周围创建了一个隐含的块。 使用内部函数时, 上面的例子能像我们所期望的那样工作,因为五个匿名函数的实例引用了变量 用 在程序和类中, 这段代码将输出 "global" 而不是 "42"。 隐含的块指的不是那种被括弧括起的块,它只由 JavaScript 引擎隐式创建。 由 换句话说,当你使用 你可以使用 在这个例子中,表达式1,表达式2,表达式3和语句都被闭合在一个隐含的块中,其中包含了用 在这两个例子中,都存在包含了每个 statement 的隐含的块。上面例子中的第二个循环展示了这里的第一种用法。 解构赋值使用了一种反映数组常量和对象常量结构的语法,使得从数组或对象中抽取数据成为可能。 通过使用对象常量和数组常量表达式,你可以轻松地按需创建成包的数据,然后以任意你想要的方式使用它们。你甚至可以从函数中返回它们。 而你使用解构赋值最重要的一件事就是,你可以从一个语句中读取整个结构。在这一节的余下的部分中会给出很多例子,你将看到你可以使用的很多有趣的功能。 解构赋值这种能力与 Perl,Python 等语言所显示出的特性很类似。 用例子就可以很好地诠释解构赋值,所以这里几乎没有什么需要你通过阅读来学习的内容。 你可以用解构赋值来交换两个变量的值。例如: 这段代码执行之后,b 为 1,a 为 3。不使用解构赋值的话,这个工作就需要一个临时变量。(当然了,在某些低级语言中,可以使用 XOR-交换技巧) 与此类似,解构赋值也可以被用于三个或更多变量之间的轮换: 这段代码执行之后,将显示一个可视化的、彩色的变量轮换过程。 回到我们上文中的菲波那契数生成器的例子,我们可以使用一个成组赋值语句来计算 "i" 和 "j" 的新值,从而消除临时变量 "t"。 有了解构赋值之后,函数就可以返回多个值了。虽然在以前也可以从函数中返回数组,但解构赋值为此增加了极大的便利。 正如你所看到的,用方括号括住所有要返回的值,使它们看上去像是数组,这就是多值返回。在上面的例子中, 命令 你也可以把这些返回值恢复成一个数组: 在这个例子中,a 是一个包含值 1 和 2 的数组。 你也可以使用解构赋值把数据从对象中倾倒出来: 这段代码循环了对象 obj 中的所有键值对,并显示出它们的名字和值。这个例子的输出如下: 在 JavaScript 1.7 中, 你可以循环一个由对象组成的数组,并倾倒出每个对象中你感兴趣的域。 这段代码用 name 域给 n 赋值,用 family.father 域给 f 赋值,然后打印它们。这个过程被作用于 people 数组中的每一个对象。输出如下: 你也可以忽略掉那些你不感兴趣的返回值: 这段代码运行之后,a 为 1,b 为 3,2 这个值被忽略了。你也可以用这种方式忽略多个,甚至是全部返回值。例如: 当正则表达式的 let
表达式,以及解构赋值。它还引入了在JavaScript 1.6中的全部特性。使用 JavaScript 1.7
<script type="application/javascript;version=1.7"/>
-version 170
或者使用 version()
函数,来设置版本: version(170);
生成器和迭代器
生成器
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
语句。throw()
传播,且后续的 next()
调用将导致 StopIteration
异常抛出。关闭生成器
close()
方法能强行关闭生成器自身。关闭生成器的效果如下:finally
语句。finally
语句抛出任何除 StopIteration
之外的异常,该异常被传播给 close()
方法的调用者。生成器示例
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;
}迭代器
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))];
0..9
之间的值的平方数。var evens = [i for each (i in range(0, 21)) if (i % 2 == 0)];
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
5let
声明,可以给代码块建立属于它自己的本地变量。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
5x+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");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) 语句
let expr1
声明的本地变量。上面例子中的第一个循环示范了这个用法。for (let 表达式1 in 表达式2) 语句
for each(let 表达式1 in 表达式2) 语句解构赋值
示例
避免临时变量
var a = 1;
var b = 3;
[a, b] = [b, a];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 />");}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);在对象中循环
let obj = { width: 3, length: 1.5, color: "orange" };
for (let [name, value] in Iterator(obj)) {
document.write ("Name: " + name + ", Value: " + value + "<br>/n");
}Name: width, Value: 3
Name: length, Value: 1.5
Name: color, Value: orangeobj
周围的 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: 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");[,,,] = f();
从正则表达式匹配结果中倾倒值
exec()
方法找到一个匹配时,它会返回一个数组,第一项是目标字符串中被匹配的完整部分,后面是被正则表达式中被括号括起的组所匹配的数段字符串。解构赋值使你能轻松地倾倒出该数组中的各个部分,并忽略掉其中不需要的匹配。// 一个简单的正则表达式,用来匹配 http / https / ftp 型的 URL
var parsedURL = /^(/w+)/:////([^//]+)//(.*)$/.exec(url);
if (!parsedURL)
return null;
var [, protocol, fullhost, fullpath] = parsedURL;
- New in JavaScript 1.7
- New in JavaScript 1.6
- New in JavaScript 1.8
- New in JavaScript 1.8.1
- What's "new" in JavaScript?
- Add new rows to WebCombo in client-side javascript
- Add new function to a prototype in javascript
- JavaScript new Date() Returning NaN in IE or Invalid Date in Safari
- nirvana_Unplugged in New York_lyrics
- select new in hibernate
- New Guy Join in
- New in Axis2
- New Feature in JDK7
- new Date() in IE
- NEW IN C++ 11
- New Start in 大学
- jQuery, new wave JavaScript
- JavaScript 的 new
- New in JavaScript 1.6
- 匿名内部类
- 支持zip的压缩,zip的解压【支持一级目录的,不支持多级】
- .NET开发资源站点和部分优秀.NET开源项目
- 批处理命令调用WINRAR对文件进行压缩
- New in JavaScript 1.7
- ext监听事件
- 简单的留言板,遇到困难了
- 让IDataErrorInfo和ValidationAttribute结合实现基础验证
- New in JavaScript 1.8
- 深入剖析WTL—WTL框架窗口分析
- New in JavaScript 1.8.1
- 随机数
- 批处理命令拷贝文件