嵌套函数,javascript嵌套函数的效率问题

javascript自诞生以来就是一门受争议的编程语言,很多人也对javascript的语法表示不解,例如javascript嵌套函数。本文来自Nettuts+的一篇教程,详细的介绍了javascript中嵌套函数效率问题,从小处说起,一直说到匿名函数、继承,感觉不错。
嵌套函数效率很多jser喜欢在javascript代码中使用嵌套函数,例如下面的例子就是一个典型的嵌套函数:
function foo(a, b) { function bar() { return a + b; } return bar(); } foo(1, 2);
上面的代码中foo()中嵌入了bar(),当foo()运行的时候,就会调用bar()。javascript引擎不会创建bar()函数,直到外部引用了foo(),随着foo()的运行结束,bar()也会销毁。
当多次运行foo的时候,javascript引擎就要在每次的运行foo时创建bar函数,而每次foo结束就要销毁bar函数,这是一个很费劲的工作。
那么为什么我们不把bar函数拿出来,做为一个独立的函数,它在foo外部只被创建一次,而不是多次,这样就大大的提高了代码效率。例如下面的代码:
function foo(a, b) { return bar(a, b); } function bar(a, b) { return a + b; } foo(1, 2);
当然这样做可能随着程序的复杂性,可能存在命名冲突的危险,所以jser需要在这方面权衡,或者采用命名空间来解决这个方式。下面是在jsperf中做的关于上面两个函数大量运行的速度测试http://jsperf.com/nested-named-functions。不同的浏览器测试的结果不同,但是总体来看,两个独立的函数要比相互嵌套的javascript函数效率提高10%~90%。
匿名函数javascript开发中常用到匿名函数,例如事件处理函数、callback函数等,例如下面的事件处理函数:
document.addEventListener("click", function(evt) { alert("You clicked the page."); });
这里给document创建了一个事件监听,当每次页面点击之后会alert出来一条消息。跟嵌套函数一样,每次点击需要创建一次匿名函数,处理事件完成之后再销毁。
jQuery中的each方法也是一个匿名函数,例如下面的代码,选择出来所有的a元素,并且添加each方法来处理a元素:
$("a").each(function(index) { this.style.color = "red"; });
如果写成jQuery插件,可以下面的代码:
$.fn.myPlugin = function(options) { return this.each(function() { var $this = $(this); function changeColor() { $this.css({color : options.color}); } changeColor(); }); };
javascript代码定义了一个名字为myPlugin的jQuery插件,插件中有一个嵌套函数changeColor,根据上面说的,上面的代码效率不如独立出来changeColor高,所以我们可以把changeColor拿到外部来,即下面的代码:
function changeColor($obj, color) { $obj.css({color : color}); } $.fn.myPlugin = function(options) { return this.each(function() { var $this = $(this); changeColor($this, options.color); }); };
经过修改过的jQuery插件在效率上提高了15%左右,大家可以通过jsperf来测试两个jQuery插件的效率。所以说嵌套的函数越多,调用的次数越多,则可以优化的地方也越多。
javascript嵌套函数和函数构造我们在javascript类中长写下面的代码:
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.getFullName = function() { return this.firstName + " " + this.lastName; }; } var jeremy = new Person("Jeremy", "McPeak"), jeffrey = new Person("Jeffrey", "Way");
这段代码定义了一个Person的类,其中包括了getFullName的方法,将firstName和lastName返回。getFullName的方法在每次创建不同的Person对象时会不同,所以jeremy.getFullName === jeffrey.getFullName返回的结果是false(http://jsfiddle.net/k9uRN/).
具体分析见下面图,jeremy和jeffrey是不同的两个对象,他们的getFullName也是不同的。

嵌套函数,javascript嵌套函数的效率问题
jeremy和jeffrey
使用prototype关键字在javascript中有prototype这个关键字,prototype的属性是实例化后的对象所共有的属性,所以上面的代码可以通过prototype改写成下面的方式:
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } Person.prototype.getFullName = function() { return this.firstName + " " + this.lastName; }; var jeremy = new Person("Jeremy", "McPeak"), jeffrey = new Person("Jeffrey", "Way");
这样getFullName的方法是定义在Person.prototype中的,为所有实例化的对象共有方法,所以jeremy和jeffrey的getFullName是相等的(http://jsfiddle.net/Pfkua/)。他们之间的关系可以通过下面的图片来解释:

嵌套函数,javascript嵌套函数的效率问题
jeremy和jeffrey共有getFullName
通过jsPerf的测试,我们可以看出来,第二种方法要比第一种方法在效率上面快了18%~96%。
变量的私有化在函数内部的变量是私有的,外面是不可以访问到函数内部的变量的,但是函数内部可以访问到外部的变量。看下面的代码:
function Foo(paramOne) { var thisIsPrivate = paramOne; this.bar = function() { return thisIsPrivate; }; } var foo = new Foo("Hello, Privacy!"); alert(foo.bar()); // alerts "Hello, Privacy!"
代码中创建了一个构造函数Foo();,并且私有了一个变量thisIsPrivate,当运行bar()时,私有的thisIsPrivate会被返回。这样thisIsPrivate受到了保护,在Foo()之外是访问不到的.
这种方法也是很多javascript工程师所推荐的写法,但是跟上面的代码一样,每次实例化Foo();之时,会创建一个bar方法,这样看上去又是对资源的浪费,而且会影响效率。所以我们可以通过使用prototype的方法来实现:
function Foo(paramOne) { this._thisIsPrivate = paramOne; } Foo.prototype.bar = function() { return this._thisIsPrivate; }; var foo = new Foo("Hello, Convention to Denote Privacy!"); alert(foo.bar()); // alerts "Hello, Convention to Denote Privacy!"
这样的代码有不可以保证变量的私有化,只是我们在变量之前添加下划线_(很多公司内部规定,或者已经成为了很多程序员的编程习惯,_开头的变量是私有的),这样每次实例化Foo();会只建立一个通用的bar方法。
总结本文也不是说不要大家在javascript中写嵌套函数,只是要适当,要注意这个知识点,在频繁调用的函数内部是不推荐写javascript嵌套函数的。开发者写代码给用户用,为的就是高效代码提高用户体验。
Tags:  if函数嵌套使用 函数的嵌套调用 函数的嵌套 if函数嵌套 嵌套函数

延伸阅读

最新评论

发表评论