ECMAScript5 严格模式 JSON 还有更多

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]";
}
posted @ 2011-12-20 16:26  MrPrime  阅读(1226)  评论(2编辑  收藏  举报