闵超的主页

JS的子集和扩展

2016-04-06
闵超

JS的子集和扩展

参照ECMAScript3和ECMAScript 5中的标准规范完整地讨论了JavaScript这门官方语言。本章将讨论JavaScript的子集和超集。其中子集的定义大部分都是出于安全考虑,只有使用这门语言的一个安全的子集编写脚本,才能让代码执行得更安全、更稳定,比如如何更安全地执行一段由不可信第三方提供的广告代码。

JS的子集

大多数语言都会定义它们的子集,用以更安全地执行不可信的第三方代码。这里有一个很有趣的子集,定义这个子集的原因有些特殊。我们首先来看这个有趣的子集,然后再讨论安全的语言子集。

精华

多数编程语言都有精华部分和鸡肋部分,如果只使用精华部分而避免使用鸡肋部分,可以让我们成为更好的程序员。 提倡函数定义表达式而不是函数定义语句来定义函数。该子集要求:循环体和条件分支都使用花括号括起来,它不允许在循环体和条件分支中只包含一条语句时省略花括号,任何语句只要不是以花括号结束都应当使用分号做结尾。

《JavaScript:The Good Parts》中为我们提炼出的子集部分对var语句做了限制,var语句只能出现在函数体的顶部,并要求程序员将函数内所有的变量声明写在一条单独的var语句中,作为函数体的第一条语句。在子集中禁止使用所有全局变量,但这个限制只是编程约定,并不是真正语言上的限制。

在线代码质量检测工具JSLint,可以通过http://jslint.com访问这个工具。这个工具提供了很多选项来增强代码的一致性检查。除了确保代码使用了子集推荐的特性之外,JSLint工具还对编码风格做了一些强制约定,比如合理的缩进等。

子集的安全性

利用”精华部分”的一个语言子集可以设计出更具美感的程序并提升程序员的开发效率。

每一个子集都带有一个静态的检查器,可以对代码进行解析检查以确保代码是符合子集规范的。

为了让JS代码静态地通过安全检查,必须移除一些js特性

  • eval()和Function()构造函数在任何安全子集里都是禁止使用的,因为他们可以执行任意代码,而且JS无法对这些代码做静态分析
  • 禁止使用this关键字,因为函数(非严格模式中)可以通过this访问全局对象。而沙箱系统的一个重要目的就是阻止对全局对象的访问.
  • 禁止使用with语句,因为with语句增加了静态代码检查的难度
  • 禁止使用某些全局变量

常量和局部变量

在js1.5以及以后的版本中使用const关键字来定义常量。常量可以看成不可重复赋值的变量,对常量的重复声明会报错。 因为JavaScript中没有块级作用域,因此常量会被提前至函数定义的顶部。

JavaScript中的变量缺少块级作用域的支持被普遍认为是JavaScript的短板,JavaScript1.7中针对这个缺陷增加了关键字let,关键字const一直都是JavaScript的保留字(没有使用)。

关键字let有4种使用方式:

  • 可以作为变量声明,和var一样;
  • 在for或for/in循环中,作为var的替代方案
  • 在语句块中定义一个新变量并显示指向它的作用域;
  • 定义一个在表达式内部作用域中的变量,这个变量只在表达式内可用。

解构赋值

可能在Python或Ruby中接触过这个概念,在解构赋值中,等号右侧是一个数组或对象(一个结构化的值),指定左侧一个或朵儿变量的语法和右侧的数组和对象直接量的语法保持一致。

let [x,y] = [1.2]; //等价于 let x=1,y =2
[x,y] = [x+1,y+1]; //等价于x = x +1,y = y+1
[x,y] = [y,x];  	//交换两个变量的值
console.log([x,y]);  //输出[3,2]

迭代

Mozilla的JS扩展引入了一些新的迭代机制,包括for/each循环和Python风格迭代器和生成器。

for/each

for/each循环是由E4X规范定义的一种新的循环语句。E4X是语言的扩展,它允许JS程序中直接出现XML标签,并添加了操作XML数据的语法和API。for/each和for/in循环非常相似。但for/each并不是遍历对象的属性,而是遍历对象的属性的值:

let o ={one:1,two :2,three:3}
for(let p in o)console.log(p);//for/in: 输出’one’,’two’,’three’
for each (let v in o )console.log(v);//for/each:输出1~3

当使用数组时for/each循环遍历循环的元素(而不是索引)。它通常按竖直顺序枚举他们,但实际上这并不是标准化或必需的:

a=[‘one’,’two’,’three’];
for(let p in o)
	console.log(p);//prints array indexes 0,1,2

for each (let v in o )
	console.log(v);//prints array elts ‘one’,’two’,’three’

注意:for/each循环并不仅仅针对数字本身的元素进行遍历,也会遍历数组中所有可枚举属性的值,包括由数组继承来的可枚举方法。

迭代器:

迭代器是一个对象,这个对象允许对它的值集合进行遍历,并保持任何必要的状态以便能够跟踪到当前遍历的“位置”。

迭代器必须包含next()方法,这迭代器每次调用next()都返回集合中的下一个值。比如下面counter()函数返回一个迭代器,这个地带器每次调用next()都会返回连续递增的整数。

当迭代器用于有限的集合时,当遍历完所有并且没有多余的值可迭代时,再调用next()方法会抛出StopIteration。它的值是一个普通的对象(它自身没有属性),只是为了终结迭代的目的而保留的一个对象。 注意,这里的循环使用一个迭代器对象,并且显示处理Stopiteration方法,这种方式非常糟糕,因此我们不经常直接使用迭代器对象,而是使用可迭代的对象。可迭代对象表示一组可迭代处理的值,可迭代对象必须定义一个名叫_iterator_()的方法,用以返回这个集合的迭代器对象。

生成器

生成器是JS1.7的特性,这里用到了一个新的关键字yield,使用这个关键字时代码必须显示指定JS1.7,关键字yield在函数内使用,用法和return类似,返回函数中的一个值。

数组推导

JS中 的数组推导,也是从Python中借用过来的一个概念。它是一种利用另外一种数组或可迭代对象来初始化数组元素的技术。数组推导的语法是基于定义元素集合的数字模型的,也就是说,表达式从句的写法和js程序员期望的不一致,但不必担心,因为花不了太多时间就可以掌握这种新式写法,一旦掌握,威力无穷。

生成器的表达式

在js中,将数组推导中的方括号替换成圆括号,它将成立一个生成器表达式。生成器表达式和数组推导非常类似(两者在圆括号内的语法几乎完全一样),只是它的返回值是一个生成器对象,而不是一个数组。

函数简写

对于函数简写,js引入了一种简写像是:表达式闭包。如果函数只计算一个表达式并返回它的值,关键字 return和花括号可以省略,并将待计算的表达式紧接着放在参数列表之后,这里有一例子:

let succ = function(x)x+1,yes=function() true,no=function() false; 这只是一种简写的快捷写法,用这种形式定义的函数,其实和带花括号和关键字return的函数完全一样。

多catch从句

try/catch语句已经可以使用多catch从句了,在catch从句的参数中加入关键字if以及一个条件判断表达式: try{ //这里可能会抛出多种类型的异常 Throw 1; } catch(e if e instanceof ReferenceError){ //这里处理引用错误 }…

E4X:ECMAScript for XML

E4X将XML文档(或者XML文档的元素或属性)视为一个XML对象,将XML片段(在常见的弗雷对象中包含多个XML元素),视为一个紧密相关的XML列表对象。


相似文章

Content

如果喜欢,打个赏,加个好友吧

扫描二维码打赏

扫描二维码打赏