JavaScript 的子集
大多数语言都会定义子集以便安全地执行第三方代码。
精华
Douglas Crockford 曾写过一本书《JavaScript: The Good Parts》(中文译作《JavaScript 语言精粹》),专门介绍了 JavaScript 中值得发扬光大的精华部分,这个语言子集的目标是简化这门语言,规避掉语言中的怪癖、缺陷部分,最终使编程更轻松、程序更健壮。书中提炼出的子集主要涵盖以下部分:
- 不包含
with
和continue
语句以及eval()
函数 - 使用函数表达式而不是函数定义语句来定义函数
- 循环体和条件分支都要使用花括号括起来,即使只包含一条语句也不要省略
- 任何语句只要不是以花括号结束都要使用分号结尾
- 不包含逗号运算符、位运算符以及
++
和--
- 不包含
==
和!=
,更推荐使用===
和!==
。 var
语句只能出现在函数体的顶部,并且函数内所有变量声明要写在一条单独的var
语句中,并作为函数第一条语句- 禁止使用全局变量
利用「精华部分」的 JavaScript 语言子集可以设计出可读性更好的程序并提升开发效率。
Crockford 还写过一个在线代码质量检测工具 JSLint,这个工具提供了很多选择用来增强代码的一致性检查,除了确保代码使用了子集推荐的特性之外,还对编码风格做了强制约定,比如合理的缩进等。
《JavaScript 语言精粹》这本书出版时,ECMAScript 5 的严格模式还没有出来,不过 Crockford 所提取的 JavaScript「鸡肋」部分很多在严格模式中同样做出了限制。
子集的安全性
这里要讨论的是一个更大的子集,这个子集的设计目的是能在一个容器或「沙箱」中更安全地运行不可信的第三方 JavaScript 代码。因此,所有能破坏这个沙箱并影响全局执行环境的语言特性和 API 在这个安全子集中都是禁止的。每个子集都带有一个静态的检查器,可以对代码进行解析检查以确保代码是符合子集规范的。为了让 JavaScript 静态通过安全检查,必须移除一些 JavaScript 特性:
eval()
和Function()
构造函数在任何安全子集都是禁止使用的,因为 JavaScript 无法对它们执行的代码进行静态分析- 禁止使用
this
关键字,因为函数可以通过this
关键字访问全局对象,二者在沙箱中是不允许的 - 禁止使用
with
语句,因为这增加了静态代码检查的难度 - 禁止使用某些全局变量,比如
window
对象、document
对象,因为这会带来很多安全隐患 - 禁止使用某些属性和方法,以免在沙箱中的代码拥有过多权限,包括
arguments
对象的两个属性caller
和callee
、函数的call()
和apply()
方法,以及constructor
和prototype
属性 - 静态分析可以有效阻止带有
.
运算符的属性存取表达式去读写特殊属性,但使用[]
访问属性则不然,因为我们无法对方括号内的字符串表达式做静态分析,因此,安全子集通常会禁用方括号,除非方括号内是数字或字符串直接量
有一些安全子集已经实现了,这里我们只是简要介绍下一些比较主要的实现:
- ADsafe :第一个正式提出的安全子集,它的提出者是我们上面提到过的 Douglas Crockford,这个安全子集只包含静态检查,使用 JSLint 作为检查器。
- dojox.secure:由 Dojo 工具包发布,也是基于静态检查,但允许使用标准 DOM API,同时包含一个用 JavaScript 实现的检查器,因而可以用它对第三方代码进行动态检查
- Caja:Google 发布的开源安全子集,Caja 定义了两个语言子集:Cajita(小沙盒)是一个和 ADsafe 和 dojox.secure 类似的严格子集;Valija(行李箱)则是一个范围更广的语言子集,更接近于 ECMAScript 5 的严格模式。Caja 本身也是一个编译器的名字,可以将一段网页内容转换为安全的模块。
- FBJS:这是 JavaScript 语言的变种,被 Facebook 采用,用以在用户资料页嵌入不可信的第三方代码。
- Microsoft Web Sandbox:定义了 JavaScript 的一个更宽泛的子集,包含 HTML 和 CSS,代码重写规则非常激进,有效的重新实现了一个 JavaScript 虚拟机,针对不安全的 JavaScript 顶层代码进行处理。
No Comments