记解答QQ群里的一个JavaScript问题

本文摘自 勾三股四 更早时期的 不老歌 博客。

问题是这样的:

window.onload = function () {
    var jsonStr = '{"name": "w", "sex": "male"}';

    //method 1
    var json = (new Function("return " + jsonStr))();

    //method 2
    function strToJson(str) {
        return str;
    };
    var json = new strToJson(jsonStr);
}
问 method 2 为什么不能达到 method 1 的效果?

大家觉得对于这样的问题该如何解答呢?

我是这么分析的。

new Function('...')

首先我觉得这位朋友对 new Function('...') 的意思没有完全理解正确。这句话是用来定义一个函数的,换句话说:
var funcName = new Function('return 1;');
相当于:
function funcName() {return 1;}
如果这两句 js 等价可以理解的话,那么 method 1 就等价于:
var json = (function () {return {"name": "w", "sex": "male"}})()

(function () {...})()

如果还看得不明白,那么这位朋友对匿名函数的定义和调用可能也不熟悉。比如:
function funcName() {return 1;}
var a = funcName();
相当于:
var a = (function funcName() {return 1;})();
进而:
var a = (function () {return 1;})();
所以 method 1 又可以进一步等价于
function funcName() {return {"name": "w", "sex": "male"}}
var json = funcName();

(function () {...})()写法的好处在哪里?

如果大家接触的js略微深入一些,会发现如今的js代码里已经大量存在(function () {...})()这样的用法了——包括一些比较复杂的函数。那么大家为什么都这么用呢?好处在哪里呢?
这种写法相比较先定义一个有名字的函数,再执行这个函数的写法相比,有一个比较明显的区别,就是你不需要为执行的函数起名字。这样的好处是这段函数的定义不会污染命名空间。
那你可能还有另外一个疑问,我不写成函数,直接写函数里的代码让它执行不就行了吗?如果要回答这个问题,我们需要引入更复杂的函数,比如:
(function () {var a = ...; ...})()
如果把函数里的代码直接写在外面:
var a = ...;
...
这样的话,命名空间会被函数定义中的局部变量所污染。而上一种写法可以使你尽情的使用局部变量而不会有命名空间被污染的后顾之忧。
所以综合两方面的优势,(function () {...})()算是一个两全其美的写法,它也因此被广泛运用了起来。

如果对 method 1 有足够深入和准确的认识,那么 method 2 显然是做不到 method 1 相同的结果的。而且 method 2 中的 strToJson 是一个普通的执行函数而不是构造函数,在这里用 new strToJson() 当构造函数创建对象就更杯具了,离 method 1 的思路也越来越远了。

这里想提醒大家的是,(new Function(...))() 的代码执行方式和 eval(...) 有异曲同工之妙,都是将字符串当做脚本来执行。我们平时对于 eval 的使用时很谨慎的,而 new Function 则见得非常少,也几乎不会主动去使用,其实在实际运用的时候需要和 eval 同等对待但它和(function () {...})()的道理相同,会比eval更好的解决命名空间被污染的问题(感谢egg的补充)。

向本文提出修改或勘误建议