JS编程建议——57:禁用Function构造函数

建议57:禁用Function构造函数
定义函数的方法包括3种:function语句、Function构造函数和函数直接量。不管用哪种方法定义函数,它们都是Function对象的实例,并将继承Function对象所有默认或自定义的方法和属性。
// 使用function语句编写函数
function f(x){

return x;

}
// 使用Function()构造函数克隆函数
var f = new Function("x", "return x;");
// 使用函数直接量直接生成函数
var f = function(x){

return x;

}
虽然这些方法定义函数的结构体相同,函数的效果相近,但是也存在很多差异,详细比较见表3.1。
表3.1 函数定义方法比较
使用function语句 使用Function构造函数 使用函数直接量
兼容 完全 JavaScript 1.1及以上 JavaScript 1.2及以上
形式 句子 表达式 表达式
名称 有名 匿名 匿名
主体 标准语法 字符串 标准语法
性质 静态 动态 静态
解析 以命令的形式构造一个函数对象 解析函数体,能够动态创建一个新的函数对象 以表达式的形式构造一个函数对象
(1)作用域比较
使用Function构造函数创建的函数具有顶级作用域,JavaScript解释器也总是把它作为顶级函数来编译,而function语句和函数直接量定义的函数都有自己的作用域(即局部作用域,或称为函数作用域)。例如:
var n = 1;
function f(){

var n = 2;
function e(){
    return n;
}
return e;

}
alert(f()()); //2
在上面示例中,分别在函数体外和函数体内声明并初始化变量n,然后在函数体内使用function语句定义一个函数e,定义该函数返回变量n的值。最后在函数体外调用函数的返回函数。通过结果可以发现,返回值为局部变量n的值(即为2),也就是说,function语句定义的函数拥有自己的作用域。同理,如果使用函数直接量定义函数e,当调用该返回函数时,返回值是2,而不是1,那么也说明函数直接量定义的函数拥有自己的作用域。
但是,如果使用Function构造函数定义函数e,则调用该返回函数时,返回的值是1,而不再是2了,看来Function构造函数定义的函数作用域需要动态确定,而不是在定义函数时确定的,代码如下:
var n = 1;
function f(){

var n = 2;
var e = new Function("return n;");
return e;

}
alert(f()()); //1
(2)解析效率比较
JavaScript解释器在解析代码时,并非一行行地分析和执行程序,而是一段段地分析执行。在同一段代码中,使用function语句和函数直接量定义的函数结构总会被提取出来优先执行。只有在函数被解析和执行完毕之后,才会按顺序执行其他代码行。但是使用Function构造函数定义的函数并非提前运行,而是在运行时动态地被执行,这也是Function构造函数定义的函数具有顶级作用域的根本原因。
从时间角度审视,function语句和函数直接量定义的函数具有静态的特性,而Function构造函数定义的函数具有动态的特性。这种解析机制的不同,必然带来不同的执行效率,这种差异性可以通过将一个循环结构放大来比较得出。
在下面这个示例中,分别把function语句定义的空函数和Function构造函数定义的空函数放在一个循环体内,让它们空转十万次,这样就会明显感到使用function语句定义的空函数运行效率更高。
// 测试function语句定义的空函数执行效率
var a = new Date();
var x = a.getTime();
for(var i=0;i<100000;i++){

function(){   //使用function语句定义的空函数
    ;
}

}
var b = new Date();
var y = b.getTime();
alert(y-x); //62,不同环境和浏览器会存在差异
// 测试Function构造函数定义的空函数执行效率
var a = new Date();
var x = a.getTime();
for(var i=0;i<100000;i++){

new Function();  //使用Function构造函数定义的空函数

}
var b = new Date();
var y = b.getTime();
alert(y-x); //2390
JavaScript解释器首先把function语句定义的函数提取出来进行编译,这样每次循环执行该函数时,就不再从头开始重新编译该函数对象了,而Function构造函数定义的函数每次循环时都需要动态编译一次,这样效率就非常低了。
(3)兼容性比较
从兼容角度考虑,使用function语句定义函数不用考虑JavaScript版本问题,所有版本都支持这种方法。而Function构造函数只能在JavaScript 1.1及其以上版本中使用,函数直接量仅在JavaScript 1.2及其以上版本中有效。当然,版本问题现在已经不是问题了。
Function构造函数和函数直接量定义函数方法有点相似,它们都是使用表达式来创建的,而不是通过语句创建的,这样带来了很大的灵活性。对于仅使用一次的函数,非常适合使用表达式的方法来创建。
由于Function构造函数和函数直接量定义函数不需要额外的变量,它们直接在表达式中参与运算,所以节省了资源,克服了使用function语句定义函数占用内存的弊端,也就是说,这些函数运行完毕即被释放而不再占用内存空间。
对于Function构造函数来说,由于定义函数的主体必须以字符串的形式来表示,使用这种方法定义复杂的函数就显得有点笨拙,很容易出现语法错误。但函数直接量的主体使用标准的JavaScript语法,这样看来使用函数直接量是一种比较快捷的方法。
通过function语句定义的函数称为命名式函数、声明式函数或函数常量,而通过匿名方式定义的函数称为引用式函数或函数表达式,而把赋值给变量的匿名函数称为函数对象,把该变量称为函数引用。

时间: 2024-10-23 05:01:51

JS编程建议——57:禁用Function构造函数的相关文章

JS编程建议——77:推荐作用域安全的构造函数

建议77:推荐作用域安全的构造函数构造函数其实是一个使用new运算符的函数.当使用new调用时,构造函数的内部用到的this对象会指向新创建的实例.function Person(name, age, job) { this.name = name; this.age = age; this.job = job; }var person = new Person("Nicholas", 34, 'software Engineer');在没有使用new运算符来调用构造函数的情况下,由于

JS编程建议——18:比较function语句和function表达式

建议18:比较function语句和function表达式在JavaScript语言中,既有function语句,也有函数表达式,这是令人困惑的,因为它们看起来是相同的.一个function语句就是值为一个函数的var语句的简写形式.下面的语句: function f() {} 相当于: var f=function() {} 这里建议使用第二种形式,因为它能明确表示f是一个包含一个函数值的变量.要用好JavaScript这门语言,理解函数就是数值是很重要的. function语句在解析时会被提

JS编程建议——64:建议通过Function扩展类型

建议64:建议通过Function扩展类型JavaScript允许为语言的基本数据类型定义方法.通过为Object.prototype添加原型方法,该方法可被所有的对象使用.这样的方式对函数.数组.字符串.数字.正则表达式和布尔值都适用.例如,通过给Function. prototype增加方法,使该方法对所有函数可用.Function.prototype.method = function(name, func) { this.prototype[name] = func; return th

JS编程建议——3:减少全局变量污染

建议3:减少全局变量污染定义全局变量有3种方式:在任何函数外面直接执行var语句. var f = 'value'; 直接添加一个属性到全局对象上.全局对象是所有全局变量的容器.在Web浏览器中,全局对象名为window. window.f = 'value'; 直接使用未经声明的变量,以这种方式定义的全局变量被称为隐式的全局变量. f = 'value'; 为方便初学者在使用前无须声明变量而有意设计了隐式的全局变量,然而不幸的是忘记声明变量成了一个非常普遍的现象.JavaScript的策略是让

JS编程建议——4:注意JavaScript数据类型的特殊性(3)

建议4:注意JavaScript数据类型的特殊性(3)其中,object表示对象的通用类型,class表示对象的内部类型,内部类型的名称与该对象的构造函数名对应.例如,Array对象的class为"Array",Function对象的class为"Function",Date对象的class为"Date",内部Math对象的class为"Math",所有Error对象(包括各种Error子类的实例)的class为"E

JS编程建议——19:不要使用类型构造器

建议19:不要使用类型构造器在默认状态下,JavaScript预定义了很多构造函数,如Function().Array().Date().string()等,如果去掉小括号,它们就是JavaScript内置对象.在JavaScript中,构造函数实际上就是类的一种抽象结构.利用new运算符调用构造函数,可以快速生成很多实例对象.例如: var f = new Function(p1, p2, ..., pn, body); 其中构造函数Function()的参数类型都是字符串,p1-pn表示所创

JS编程建议——50:正确检测数组类型

建议50:正确检测数组类型由于数组和对象的数据同源性,导致在JavaScript编程中经常会出现:在必须使用数组时使用了对象,或者在必须使用对象时使用了数组.选用数组或对象的规则很简单:当属性名是小而连续的整数时,应该使用数组,或者当对属性的位置和排列顺序有要求时,应该使用数组.否则,使用对象.JavaScript语言对数组和对象的区别是混乱的.typeof运算符检测数组的类型是"object",这没有什么意义,因此在正确检测数组和对象方面JavaScript没有提供很多的机制.这时可

JS编程建议——59:推荐动态调用函数

建议59:推荐动态调用函数调用函数更便捷的方式是使用Function对象的call和apply方法.apply与call方法在本质上没有太大区别,只不过它们传递给函数的参数方式不同, apply是以数组形式进行参数传递,而call方法可以同时传递多个值.如果某个函数仅能够接收多个参数列表,而现在希望把一个数组的所有元素作为参数进行传递,那么使用apply方法就显得非常便利.function max(){ var m = Number.NEGATIVE_INFINITY; // 声明一个负无穷大的

JS编程建议——65:比较函数的惰性求值与非惰性求值

建议65:比较函数的惰性求值与非惰性求值在JavaScript中,使用函数式风格编程时,应该对于表达式有着深刻的理解,并能够主动使用表达式的连续运算来组织代码.1)在运算元中,除了JavaScript默认的数据类型外,函数也作为一个重要的运算元参与运算.2)在运算符中,除了JavaScript的大量预定义运算符外,函数还作为一个重要的运算符进行计算和组织代码.函数作为运算符参与运算,具有非惰性求值特性.非惰性求值行为自然会对整个程序产生一定的负面影响.先看下面这个示例:var a = 2;fun