ECMAScript5 规范自推出以来, 对javascript的发展产生了蛮大的影响。这篇文章是我翻译自John Resig(jQuery作者)的一篇博文
《ECMAScript 5 Strict Mode, JSON, and More》
之前我分析了ES5的对象和属性系统。这是这门语言的一大亮点。
同样有许多其他新特性和API也需要我们注意。最主要的有严格模式(Strict Mode)和原生JSON支持(native JSON support)。
严格模式(Strict Mode)
严格模式在ES5中是个新特性,它允许你在一个“严格”的环境中执行你的代码。严格的环境会阻止采用一些特定的代码行为,抛出更多的异常(总体上说给用户更多的信息和规范编码)。
自ES5对ES3进行兼容后,所有在ES3中可以正常使用,但在ES5中注为“不推荐”的特性将在严格模式下失效(或者抛出异常)。
严格模式将以如下这些方式推进代码规范:
-
捕获一些常见的代码漏洞,抛出异常
-
当一些相对“不安全”的代码被执行时,进行阻止或抛出异常(例如获取全局对象访问)
-
禁掉那些令人疑惑或者欠考虑的特性
大多数关于严格模式的信息可以在ES5规范[PDF]223页找到。
应该注意的是ES5的严格模式与FireFox提供的严格模式不同(可以通过about:config来启动javascript.options.strict)。ES5关注编码规范上的潜在错误。(而FireFox的严格模式只是强调一些最佳实践)。
如何启动严格模式?
很简单。在你的代码前加上"use strict",为你所有代码启动严格模式:
"use strict";
或者只在一个执行上下文内,如:
function imStrict() { "use strict"; // ... your code... }
看吧,启动严格模式的语法,不能再简单了,就一条语句"use strict"就行了。没有新的语法,不需要什么复杂的配置,并且对老的浏览器也没什么副作用。
ES5并没有对语言语法进行添加或者更改。所以可以在老浏览器中进行优雅降级。
在一个函数中定义严格模式,你可以在其中定义自己的javascript库,而不影响外部的代码。
// Non-strict code... (function() { "use strict"; // Define your library strictly... })(); // Non-strict code...
当前很多库用上面的这种包裹技术(把整个库包在一个自动执行的匿名函数中)。
那么当你把脚本置于严格模式后,会发生什么呢?很多事情
变量和属性
尝试赋值foo = 'bar';如果'foo'没有事先定义,那么将失败。在这之前,foo会被赋值,并且foo会作为全局对象window的属性window.foo,现在这会抛出异常。确保避免一些恼人的bug。
对任何writable属性(attribute)为false的属性(property)进行写操作,configurable属性(attribute)为false的属性(property)进行删除操作,extensible属性(attribute)为false的对象进行添加属性(property)都会导致报错。传统上,当尝试这些行为时错误不会被抛出,将静静地失败。
删除变量,一个函数,一个参数将导致错误。
var foo = "test"; function test() {} delete foo; // Error delete test; // Error function test2(arg) { delete arg; // Error }
在一个字面对象中不止一次定义一个同名属性,将导致异常抛出
// Error { foo: true, foo: false }
eval
几乎任何尝试使用'eval'这个名称的操作都会被禁止.
// All generate errors... obj.eval = ... obj.foo = eval; var eval = ...; for(var eval in ...) {} function eval() {} function test(eval) {} function(eval) {} new Function("eval")
除此之外,通过eval引入的新变量无效
eval("var a = false;"); print(typeof a); // undefined
Functions
试图去覆写arguments对象将导致出错:
arguments = [...]; // now allowed
定义同名的参数会导致错误:function(foo, foo)
对arguments.caller和arguments.callee的访问将抛出异常。因此任何你想引用的匿名函数要给个名称,像这样:
setTimeout(function later() { // do stuff... setTimeout(later, 1000); }, 1000);
其他函数的arguments和caller属性不在存在,并且定义它们的能力被禁止:
function test() { function inner() { // Don't exist, either test.arguments = ...; // Error inner.caller = ...; // Error } }
最后一个长期困扰的(并且很恼人的)bug被解决了:像如下用例中,null或者undefined被强制转换为全局对象。严格模式现在阻止这样的事情发生,并会抛个异常。
(function() { ... }).call(null); // Exception
with
with语句在严格模式中完全被干掉了,事实上它的出现会作为语法错误。
ES5严格模式对代码的改变是多样的。观察开发者如何开始采用这些规范和它如何改变javascript开发,将是个有趣的过程。
JSON
ECMAScript语言第二个主要的特性是加入了对JSON的原生支持。
我已经拥护此项推进很久了,如今我很高兴的看到它被写入了规范。
有2个主要的方法用于处理JSON:JSON.parse(把一个JSON字符串转成一个javascript对象)和JSON.stringify(把一个javascript对象转成一个序列化的字符串)。
JSON.parse(text)
把一个序列化的JSON字符串转成一个javascript对象。
var obj = JSON.parse('{"name": "John"}'); // Prints 'John' print(obj.name);
JSON.parse(text, translate)
使用一个转换函数 转换值或者进行移除
function translate(key, value) { if( key === "name") { return value + " Resig"; } } var obj = JSON.parse('{"name": "John", "last": "Resig"}', translate); // Prints 'John Resig' print( obj.name ); // Undefined print( obj.last );
JSON.stringify(obj)
把一个对象转成一个序列化的JSON字符串
var str = JSON.stringify({name: "John"}); // Prints {"name": "John"} print( str );
JSON.stringify(obj, ["white", "list"])
只序列化白名单指定的属性
var list = ["name"]; var str = JSON.stringify({name: "John", last: "Resig"}, list); // Prints {"name": "John"} print( str );
JSON.stringify(obj, translate)
用转换函数来序列化对象
function translate(key, value) { if(key === "name") { return value + " Resig"; } } var str = JSON.stringify({"name": "John", "last": "Resig"}, translate); // Prints {"name": "John Resig"} print( str );
JSON.stringify(obj, null, 2)
给输出增加指定的空格数
var str = JSON.stringify({ name: "John" }, null, 2); // prints // { // "name": "John" // } print( str );
JSON.stringify(obj, null, "\t")
使用特定的字符来做分隔
var str = JSON.stringify({ name: "John" }, null, "\t"); // Prints: // {\n\t"name": "John"\n} print( str );
另外,一些基础类型添加了新的通用方法,但是,恕我直言,它们并不有趣。来自String,Boolean和Number的结果等同于调用valueOf(), Date的结果等同于调用toISOString()
// Yawn String.prototype.toJSON Boolean.prototype.toJSON Number.prototype.toJSON Date.prototype.toJSON
.bind()
内置bind()方法的引入很受欢迎,它可以使一个函数的执行环境得到改变(几乎等同于prototype framework中的bind实现)
Function.prototype.bind(thisArg, arg1, arg2...)
使得函数的this关键字指向一个特定的对象,并传入任何你要指定的参数。
var obj = { method: function(name) { this.name = name; } }; setTimeout(obj.method.bind(obj, "John"), 100);
Date
首先,如果所给的值是ISO格式,Date构造函数会试图去解析该日期,接着再处理它接受的其他输入。
var date = new Date("2009-05-21T16:06:05.000Z"); // Prints 2009-05-21T16:06:05.000Z print( date.toISOString() );
另外,date对象现在有个新的toISOString()方法,以ISO格式输出日期
.trim()
一个原生,内置的trim()方法被String对象支持,和我们自己实现的trim方法功能一样,但是原生的效率更高。
Array
JavaScript Array扩展方法包括: indexOf, lastIndexOf, every, some, forEach, map, filter, reduce 和 reduceRight
另外一个新方法Array.isArray(译者注:可以理解为Array的static方法)被包含了进来, 提供和下面这段代码同样地功能:
Array.isArray = function( array ) { return Object.prototype.toString.call( array ) === "[object Array]"; }