子集的安全性
为了让JavaScript代码静态的通过安全检查,必须移除一些JavaScript特性:
- eval()和Function()构造函数在任何安全子集里都是禁止使用的,因为它们可以执行任意代码,而且JavaScript无法对这些代码做静态分析。
- 禁止使用this关键字,因为函数(在非严格模式中)可以通过this访问全局对象。而沙箱系统的一个重要目的就是组织对全局对象的访问。
- 禁止使用with语句,因为with语句增加了静态代码检查的难度。
- 禁止使用某些全局变量。
- 禁止使用某些属性和方法,以免在沙箱中的代码拥有过多的权限。这些属性和方法包括arguments对象的两个属性caller和callee(甚至在某些子集中干脆禁止使用arguments对象)、函数的call()和apply()方法,以及constructor和prototype两个属性。非标准的属性也被禁止掉了,比如_proto_。
- 静态分析可以有效的防止带有点(.)运算符的属性存取表达式去读写特殊属性。使用方括号[]来访问属性则与此不同,因为无法对方括号内的字符串表达式做静态分析。
常量和局部变量
在JavaScript1.5及后续版本中可以使用const关键字来定义常量。常量可以看成不可重复的变量(对敞亮重新复制会失败但不报错),对常量的重复生命会报错。
一直以来,JavaScript中的变量缺少块级作用域的支持被普遍认为是JavaScript的短板,JavaScript1.7针对这个缺陷增加了关键字let。关键字let并不是保留字,JavaScript1.7及以后的版本才能识别,需要手动加入版本号才可以。
解构赋值
在解构赋值中,等号右侧是一个数组或对象(一个结构化的值),指定左侧一个或多个变量的语法和右侧的数组和对象直接量的语法保持格式一致。
当发生解构赋值时,右侧的数组和对象中一个或多个的值就会被提取出来(解构),并赋值给左侧相应的变量名。除了用于常规的赋值运算符之外,解构赋值还用于初始化用var和let新声明的变量。
解构赋值右侧的数组所包含的元素不必和左侧的变量一一对应,左侧多余的变量的赋值为undefined,而右侧多余的值会被忽略。左侧的变量列表可以包含连续的逗号用以跳过左侧对应的值。
let[x,y] = [1];//x=1,y=undefined
[x,y] = [1,2,3];//x=1,y=2
[,x,,y] = [1,2,3,4];//x=2,y=4
JavaScript并未提供将右侧的多余的值以数组的形式赋值给左侧变量的语法。
解构赋值同样可以用于数组嵌套的情况,解构赋值的左侧应当也是同样格式的嵌套数组直接量:
let [one, [twoA, twoB]] = [1,[2,2.5],4]; //one=1,twoA=2,twoB=2.5
迭代
for/each
for/each循环并不仅针对数组的元素进行遍历,它也会遍历数组中所有的可枚举属性的值,宝货由数组继承来的可枚举方法。所以,不推荐for/each循环和数组一起使用。
迭代器
迭代器是一个对象,这个对象允许对它的值集合进行便利,并保持任何必要的状态以便跟踪到当前遍历的“位置”。
迭代器必须包含next()方法,每一次调用next()都返回集合中的下一个值。
当迭代器用于有限的集合时,当遍历完所有的值并且没有多余的值可迭代时,再调用next()方法会抛出StopIteration。StopIteration是JavaScript1.7中的全局对象的属性。它的值是一个普通的对象,为了终结迭代的目的而保留的一个对象。
生成器
生成器是JavaScript1.7中的特性,用到了一个关键字yield,使用这个关键字时必须显示指定JavaScript的版本1.7。关键字yield在函数内使用,用法和return类似,返回函数中的一个值,yiedl和return的区别在于,使用yield的函数“产生”一个可保持函数内部状态的值,这个值是可以恢复的。
任何使用关键字yield的函数都成为“生成器函数”。生成器函数通过yield返回值。这些函数中可以使用return来终止函数的执行而不带任何返回值,但不能使用return来返回一个值。除了使用yield,对return的使用限制也使生成器函数更明显的区别于普通函数。然而和普通的函数一样,生成器函数是通过关键字function声明,typeof运算符返回“function”,并且可以从Function.prototype继承属性和方法。但对生成器的函数调用却和普通函数全球不一样,不是执行生成器函数的函数体,而是返回一个生成器队形。
生成器是一个对象,用以表示生成器函数的当前执行状态。它定义了一个next()方法,后者可以恢复生成器函数的执行,直到遇到吓一跳yield语句为止。
数组推导
数组推导是一种利用另外一个数组或可迭代对象来初始化数组元素的技术。数组推导的语法是基于定义元素集合的数学模型,也就是说,表达式和从句的写法和JavaScript程序员的期望的不一致。
let evensquares = [x*x for (x in range(0,10)) if (x%2===0)]
这段代码和下面这5行代码等价:
let evensquares = [];
for(x in range(0,10)){
if(x % 2 ===0)
evensquares.push(x*x);
}
一般来讲,数组推导的语法如下:
[expression for (variable in object) if(condition)]
数组推导包含三个部分:
- 一个没有循环体的for/in或for/each循环。这部分推导包括一个变量(或者通过解构赋值得到的多个变量),它位于关键字in的左侧,in的右侧是一个对象。尽管这个对象后面没有循环体,这段数组推到也能正确执行迭代,并能给制定的变量赋值。
- 在执行遍历的对象之后,是园括号中的关键字if和条件表达式,目前,这个表达式只是用做过过滤迭代的值。每次for循环产生一个值之后会判断条件表达式。如果条件表达式返回false,则跳过这个值,这个值也不会被添加到数组当中。if从句是可选的,如果省略的话,相当于给数组推导补充了一条if(true)从句。
- 在关键字for之前是expression,可以认为这个表达式是循环体。在迭代器返回了一个值并将它赋给一个变量,且这个变量通过了condition测试之后,将计算这个表达式,并将表达式的结算结果插入到要创建的数组中。
生成器表达式
在JavaScript1.8中,将数组推导中的方括号替换成圆括号,它就成了一个生成器表达式。生成器表达式和数组推导非常类似,只是它的返回值是一个生成器对象,而不是一个数组。和数组推导相比,使用生成器表达式的好处是可以惰性求值,只有在需要的时候求值而不是每次都计算求值,这种特性可以用用与潜在的无穷序列。使用生成器表达式而不用数组也有不足之处,生成器只支持对值的顺序存取而不是随机存取。和数组不同,生成器没有索引,为了得到第n个值,必须便利他之前的n-1个值。
函数简写
对于简单的函数,JavaScript1.8引入了一种简写形式:表达式闭包/如果函数只计算一个表达式并返回它的值,关键字return和花括号都可以省略。
let succ=function(x)x+1, yes=funciton()true, no=funciton() false;
多catch从句
在JavaScript1.5中,try/catch语句已经可以使用多catch从句了,在catch从句的参数中加入关键字if以及一个条件判断表达式:
try{
throw l;
}
catch(e if e instanceof ReferenceError){
}
catch(e if e ===”quit”){
}
catch(e if typeof e === “string”){
}
catch(e){
}
finally{
}
当产生一个异常时,程序会尝试一次执行每一条catch从句。catc从句中的命名参数即时这个异常,执行到catch的时候会计算它的条件表达式。如果条件表达式计算结果为true,则判断当前catch从句的逻辑,同时跳过其他的catch从句。如果catch从句中没有条件表达式,则程序就会假设它包含一个if true的条件,如果它之前的catch从句都没有触发,那么这条catch语句一定会执行。如果所有的catc从句都包含条件,但没有一个条件是true,那么程序会向上抛出这个未捕获的异常。