JavaScript 的子集


大多数语言都会定义子集以便安全地执行第三方代码。

精华

Douglas Crockford 曾写过一本书《JavaScript: The Good Parts》(中文译作《JavaScript 语言精粹》),专门介绍了 JavaScript 中值得发扬光大的精华部分,这个语言子集的目标是简化这门语言,规避掉语言中的怪癖、缺陷部分,最终使编程更轻松、程序更健壮。书中提炼出的子集主要涵盖以下部分:

  • 不包含 withcontinue 语句以及 eval() 函数
  • 使用函数表达式而不是函数定义语句来定义函数
  • 循环体和条件分支都要使用花括号括起来,即使只包含一条语句也不要省略
  • 任何语句只要不是以花括号结束都要使用分号结尾
  • 不包含逗号运算符、位运算符以及 ++--
  • 不包含 ==!=,更推荐使用 ===!==
  • var 语句只能出现在函数体的顶部,并且函数内所有变量声明要写在一条单独的var语句中,并作为函数第一条语句
  • 禁止使用全局变量

利用「精华部分」的 JavaScript 语言子集可以设计出可读性更好的程序并提升开发效率。

Crockford 还写过一个在线代码质量检测工具 JSLint,这个工具提供了很多选择用来增强代码的一致性检查,除了确保代码使用了子集推荐的特性之外,还对编码风格做了强制约定,比如合理的缩进等。

《JavaScript 语言精粹》这本书出版时,ECMAScript 5 的严格模式还没有出来,不过 Crockford 所提取的 JavaScript「鸡肋」部分很多在严格模式中同样做出了限制。

子集的安全性

这里要讨论的是一个更大的子集,这个子集的设计目的是能在一个容器或「沙箱」中更安全地运行不可信的第三方 JavaScript 代码。因此,所有能破坏这个沙箱并影响全局执行环境的语言特性和 API 在这个安全子集中都是禁止的。每个子集都带有一个静态的检查器,可以对代码进行解析检查以确保代码是符合子集规范的。为了让 JavaScript 静态通过安全检查,必须移除一些 JavaScript 特性:

  • eval()Function() 构造函数在任何安全子集都是禁止使用的,因为 JavaScript 无法对它们执行的代码进行静态分析
  • 禁止使用 this 关键字,因为函数可以通过 this 关键字访问全局对象,二者在沙箱中是不允许的
  • 禁止使用 with 语句,因为这增加了静态代码检查的难度
  • 禁止使用某些全局变量,比如 window 对象、document 对象,因为这会带来很多安全隐患
  • 禁止使用某些属性和方法,以免在沙箱中的代码拥有过多权限,包括 arguments 对象的两个属性 callercallee、函数的 call()apply() 方法,以及 constructorprototype 属性
  • 静态分析可以有效阻止带有.运算符的属性存取表达式去读写特殊属性,但使用[]访问属性则不然,因为我们无法对方括号内的字符串表达式做静态分析,因此,安全子集通常会禁用方括号,除非方括号内是数字或字符串直接量

有一些安全子集已经实现了,这里我们只是简要介绍下一些比较主要的实现:

  • 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 顶层代码进行处理。

点赞 取消点赞 收藏 取消收藏

<< 上一篇: RegExp 对象

>> 下一篇: 常量和局部变量