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

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

return x;

}
alert(f(a,a=a*a)); //2
alert(f(a)); //4
在上面的示例中,两次调用同一个函数并传递同一个变量,所返回的值却不一样。在第一次调用函数时,向其传递了两个参数,第二个参数是一个表达式,该表达式对变量a进行重新计算和赋值。也就是说,当调用函数时,第二个参数虽然不使用,但是也被计算了。这就是JavaScript的非惰性求值特性,也就是说,不管表达式是否被利用,只要在执行代码行中都会被计算。
如果在一个函数参数中无意添加了几个表达式,虽然这样不会对函数的运算结果产生影响,但是由于表达式被执行,就会对整个程序产生潜在的负面影响。
在惰性求值语言中,如果参数不被调用,那么无论参数是直接量还是某个表达式,都不会占用系统资源。但是,由于JavaScript支持非惰性求值,问题就变得很特殊了。
function f(){}
f( function(){while(true);}())
在上面的示例中,虽然函数f没有参数,但是在调用时将会执行传递给它的参数表达式,该表达式是一个死循环结构的函数值,最终将导致系统崩溃。
惰性函数模式是一种将对函数或请求的处理延迟到真正需要结果时进行的通用概念,很多应用程序都采用了这种概念。从惰性编程的角度来思考问题,可以帮助消除代码中不必要的计算。例如,在Scheme语言中,delay特殊表单接收一个代码块,它不会立即执行这个代码块,而是将代码和参数作为一个promise存储起来。如果需要promise产生一个值,就会运行这段代码。promise 随后会保存结果,这样将来再请求这个值时,该值就可以立即返回,而不用再次执行代码。这种设计模式在JavaScript中大有用处,尤其是在编写跨浏览器的、高效运行的库时非常有用。例如,下面是一个时间对象实例化的函数。
var t;
function f(){

t = t ? t : new Date();
return t;

}
f(); // 调用函数
上面的示例使用全局变量t来存储时间对象,这样在每次调用函数时都必须进行重新求值,代码的效率没有得到优化,同时全局变量t很容易被所有代码访问和操作,存在安全隐患。当然,可以使用闭包隐藏全局变量t,只允许在函数f内访问。
var f =(function(){

var t;
return function(){
    t = t ? t : new Date();
    return t;
}

})();
f();
这仍然没有提高调用时的效率,因为每次调用f依然需要求值:
var f = function() {

var t = new Date();
f = function() {
    return t;
}
return f();

};
f();
在上面的示例中,函数f的首次调用将实例化一个新的Date对象并重置f到一个新的函数上,f在其闭包内包含Date对象。在首次调用结束之前,f的新函数值也已被调用并提供返回值。
函数f的调用都只会简单地返回t保留在其闭包内的值,这样执行起来非常高效。弄清这种模式的另一种途径是,外部函数f的首次调用是一个保证(promise),它保证了首次调用会重定义f为一个非常有用的函数,保证来自于Scheme的惰性求值机制。

时间: 2024-07-30 02:27:08

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

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编程建议——6:正确处理JavaScript特殊值(2)

建议6:正确处理JavaScript特殊值(2)与null不同,undefined不是JavaScript的保留字,在ECMAScript v3标准中才定义undefined为全局变量,初始值为undefined.因此,在使用undefined值时就存在一个兼容问题(早期浏览器可能不支持undefined).除了直接赋值和使用typeof运算符外,其他任何运算符对undefined的操作都会引发异常.不过,可以声明undefined变量,然后查看它的值,如果它的值为undefined,则说明浏览

JS编程建议——9:不要信任hasOwnProperty

建议9:不要信任hasOwnPropertyhasOwnProperty方法常被用做一个过滤器,用来消除for in语句在枚举对象属性时的弊端.考虑到hasOwnProperty是一个方法,而不是一个运算符,因此,在任何对象中,它可能会被一个不同的函数甚至一个非函数的值所替换.例如,在下面代码中, obj对象的hasOwnProperty成员被清空了,此时如果再利用这个方法来过滤出obj对象的本地属性就会失败. var obj={}, name; obj.hasOwnProperty = nul

JS编程建议——16:防止switch贯穿

建议16:防止switch贯穿JavaScript语言中那些显而易见的危险或无用的特性不是最糟糕的,这些特性很容易被避免.最糟糕的特性像"带刺的玫瑰",它们是有用的,但也是危险的.switch语句的由来可以追溯到FORTRAN IV的go to语句.除非明确地中断流程,否则每次条件判断后都贯穿到下一个case条件.switch语句的基本语法格式如下: switch (expression ){ statements } 完全扩展后的switch结构如下: switch ( expres

JS编程建议——27:小心if隐藏的Bug

建议27:小心if隐藏的Bug很多程序员都犯过这样低级的错误: if(a = 1){ alert(a); } 把比较运算符(==)错写为赋值运算符(=).这样的Bug一般很难发现,由于它是一个合法的表达式,不会导致编译错误.由于此表达的返回值为非0数值, JavaScript会自动把它转换为true,因此这样的分支结构的条件永远成立. 为了防止出现这样低级而又令人讨厌的错误,建议在条件表达式的比较运算中,把常量写在左侧,把变量写在右侧,这样即使把比较运算符(==)错写为赋值运算符(=),也会导致

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

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

JS编程建议——76:要重视函数节流

建议76:要重视函数节流比起非DOM交互,DOM操作需要更多内存和CPU时间.连续尝试进行过多的DOM相关操作可能会导致浏览器变慢甚至崩溃.函数节流的设计思想就是让某些代码可以在间断情况下连续重复执行,实现的方法是使用定时器对函数进行节流.例如,在第一次调用函数时,创建一个定时器,在指定的时间间隔后执行代码.当第二次调用时,清除前一次的定时器并设置另一个,实际上就是前一个定时器演示执行,将其替换成一个新的定时器.var processor = { timeoutId : null, //实际进行

JS编程建议——72:惰性载入函数

建议72:惰性载入函数惰性载入函数主要解决的问题也是兼容性,原理跟分支函数类似,下面是简单的示例.var addEvent = function(el, type, handle) { addEvent = el.addEventListener ? function(el, type, handle) { el.addEventListener(type, handle, false); } : function(el, type, handle) { el.attachEvent("on&q

JS编程建议——70:惰性实例化

建议70:惰性实例化惰性实例化要解决的问题是:避免了在页面中JavaScript初始化执行的时候就实例化类,如果在页面中没有使用这个实例化的对象,就会造成一定的内存浪费和性能消耗.如果将一些类的实例化推迟到需要使用它的时候才去做,就可以避免资源过早损耗,做到"按需供应".var myNamespace = function() { var Configure = function() { var privateName = "someone's name"; var